Accessing network hardware The Network Interface Controllers are part of a larger scheme used in modern PCs for device control Memory-mapped I/O • Non-Intel processor architectures typically will dedicate a set of memory-addresses to performing communication with hardware devices which are installed in the system • Such architectures allow software to read device-status or write device-commands using ordinary CPU instruction-opcodes, such as ‘mov’, ‘test’, ‘add’, ‘xor’, et cetra I/O ports • But Intel’s original x86 architecture used a different approach in which a separate set of addresses and ‘special’ instructions are employed for accessing system devices • The device addresses are called I/O ports, and the opcodes are named ‘in’ and ‘out’: – The ‘in’ instruction reads a device’s ‘status’ – The ‘out’ instruction writes device ‘command’ Range of I/O ports • For devices integrated into a PC system’s motherboard, the port-numbers used were 8-bit values and could be specified within an I/O instruction, as ‘immediate’ data • Example: in $0x64, %al # read port 0x64 • For optional devices plugged into ‘slots’ on the motherboard, a port-number could be a 16-bit value, located in the DX register • Example: in %dx, %al # indirect address Two separate address-spaces accessible by using a wide variety of general-purpose arithmetical and logical instructions memory address-space (4GB) accessed only by using the special ‘in’ and ‘out’ instructions I/O address-space (64KB) Early PCs • Peripheral devices in the early PCs used fixed i/o-ports and fixed memory-addresses, e.g.: – – – – – – – – Video memory address-range: 0xA0000-0xBFFFF Programmable timer i/o-ports: 0x40-0x43 Keyboard and mouse i/o-ports: 0x60-0x64 Real-Time Clock’s i/o-ports: 0x70-0x71 Hard Disk controller’s i/o-ports: 0x01F0-01F7 Graphics controller’s i/o-ports: 0x03C0-0x3CF Serial-port controller’s i/o-ports: 0x03F8-0x03FF Parallel-port controller’s i/o-ports: 0x0378-0x037A The PC’s evolution • It became clear in the 1990s that there would be contention among equipment vendors for ‘fixed’ resource-addresses, which of course were in limited supply • Among the goals that motivated the PCI Specification was the creation of a more flexible scheme for allocating addresses that future peripheral devices could use PCI Configuration Space A non-volatile parameter-storage area for each PCI device-function PCI Configuration Space Header (16 doublewords – fixed format) 64 doublewords PCI Configuration Space Body (48 doublewords – variable format) PCI Configuration Header 16 doublewords 31 0 Status Register BIST Header Type Command Register Latency Timer Cache Line Size 31 0 Device ID Vendor ID Class Code Class/SubClass/ProgIF Revision ID Dwords 1- 0 3- 2 Base Address 1 Base Address 0 5- 4 Base Address 3 Base Address 2 7- 6 Base Address 5 Base Address 4 9- 8 CardBus CIS Pointer 11 - 10 Subsystem Device ID Subsystem Vendor ID reserved capabilities pointer Expansion ROM Base Address 13 - 12 Maximum Minimum Interrupt Latency Grant Pin Interrupt Line reserved 15 - 14 Three x86 address-spaces accessed using a large variety of processor instructions (mov, add, or, shr, push, etc.) and virtual-to-physical address-translation memory space (4GB) accessed only by using the processor’s special ‘in’ and ‘out’ instructions (without any translation of port-addresses) i/o space (64KB) PCI configuration space (16MB) i/o-ports 0x0CF8-0x0CFF dedicated to accessing PCI Configuration Space Interface to PCI Configuration Space PCI Configuration Space Address Port (32-bits) 31 CONFADD ( 0x0CF8) E N 23 reserved 16 15 bus (8-bits) 11 10 device (5-bits) 8 7 function (3-bits) 2 doubleword (6-bits) 0 00 Enable Configuration Space Mapping (1=yes, 0=no) PCI Configuration Space Data Port (32-bits) 31 CONFDAT ( 0x0CFC) 0 Reading PCI Configuration Data • Step one: Output the desired longword’s address (bus, device, function, and dword) with bit 31 set to 1 (to enable access) to the Configuration-Space Address-Port • Step two: Read the designated data from the Configuration-Space Data-Port: # read the PCI Header-Type field (byte 2 of dword 3) for bus=0, device=0, function=0 movl $0x8000000C, %eax # setup address in EAX movw $0x0CF8, %dx # setup port-number in DX outl %eax, %dx # output address to port mov inl shr movb $0x0CFC, %dx %dx, %eax $16, %eax %al, header_type # setup port-number in DX # input configuration longword # shift word 2 into AL register # store Header Type in variable Demo Program • We created a short Linux utility that searches for and reports all of your system’s PCI devices • It’s named “pciprobe.cpp” on our class website • It uses some C++ macros that expand to Intel input/output instructions -- which normally are ‘privileged’ instructions that a Linux applicationprogram is not allowed to execute (segfault!) • But using ‘sudo’ you can escalate the privilegelevel at which this utility-program will be run: $ sudo ./pciprobe Example: network interface • We can identify the network interface controllers in our classroom’s PC’s by class-code 0x02 • Then the subclass-code 0x00 is for ‘Ethernet’ • We can identify the NIC from its VENDOR and DEVICE identification-numbers: • VENDOR_ID = 0x8086 • DEVICE_ID = 0x109A // for Intel Corporation // for 82573L controller • Our second demo-program (i.e, ‘82573pci.cpp’) shows this nic’s full PCI Configuration Space Typical NIC packet main memory TX FIFO buffer B U S CPU nic RX FIFO transceiver LAN cable Intel’s Pro1000 nics • These network controllers implement a large number of 32-bit device registers • Two ways are implemented for accessing these numerous registers: – Using a small range of I/O port-addresses – Using a large range of ‘memory-mapped’ register-addresses PCI Base-Address registers • The key to accessing our NIC’s various registers is provided by values we find in certain PCI Configuration Space locations 32-bit Base-Address Register layout for I/O space unused R S V port-address 1 32-bit Base-Address Register layout for Memory space memory-address 00=32-bit decode, 01=reserved, 10=64-bit decode, 11=reserved R S V P R E F 0 Output from ‘83573pci.cpp’ executed on ‘anchor01’ BAR0 BAR1 BAR2 BAR3 BAR4 BAR5 (at (at (at (at (at (at 0x10): 0x14): 0x18): 0x1C): 0x20): 0x24): 0xE8200000 (Physical memory-address for NIC’s mapped register-bank) 0x00000000 0x00005001 (I/O port-address for NIC’s ‘multiplexing’ register-bank) 0x00000000 0x00000000 0x00000000 ‘mapped’ advantage • For high-performance (and ‘thread-safe’) access to the NIC’s bank of registers, the memory-mapped option offers very clear advantages over multiplexed I/O access: – Access is faster to memory than to I/O ports – Access to memory is ‘atomic’, whereas the multiplexed access via I/O ports is a 2-step operation, giving rise to ‘race conditions’ I/O advantage • But during the system’s ‘startup’ process (before the Operating System is loaded), there’s no alternative but to use the I/O access-method, because the memorymapped registers are at high addresses which the processor cannot yet reach • For example, in cases where an OS needs to be loaded via a BOOTP network-server Clarification • Don’t confuse PCI Configuration Space registers with our NIC’s device registers! System’s PCI Configuration Space CPU’s I/O-port address-space BAR2 Network Interface Controller NIC device-registers BAR0 NIC’s PCI Configuration Space registers window CPU’s memory address-space In-class exercise • Copy our ‘82573pci.cpp’ source-module to your own directory, then rename it as ‘audio.cpp’ • Your assignment is to modify it so that it will display the PCI Configuration Space registers in our classroom workstation’s sound-card (a ‘multimedia’ class device)