Bus Number
Device Number
Function Number
Register Number
There are up to 256 available Buses on a PCI system, most commonlyall the cards and chips will be located on Bus 0 and Bus 1.
When scanning for hardware, it's a good idea to scan all 256 busesas it won't take that much additional time.
A Device is a physical thing on the PCI bus. It could be a videocard, an ethernet card, a Northbridge, anything. There is asoftware maximum of 32 devices that can exist on each bus. Thephysical hardware limit is much lower than this due to electricalloading issues, but we won't get into that.
Devices on a motherboard are typically scattered throughout the 32devices, holes can and will exist. Your software must scan all 32devices on each bus.
All devices have at least 1 function. Multi-function devices, suchas a combination modem/soundcard will usually have 2 devices,typically numbered 0 and 1. There are 8 possible functions perdevice, numbered 0-7. Any device that has more than 1 function is(hey clever!) called a multi-function device. Functions startnumbering at 0 and work up to7.
Every function of a device has 256 eight-bit registers. Registers0-3F are defined by the PCI specification and provide a wealth ofinformation about the particular function. Registers 40-FF arevendor defined and control the properties of the function itself.Without vendor specific documentation, these registers shouldprobably be left untouched.
A typical PCI function looks like this:
x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
00000000: B7 10 55 90 17 01 10 02 30 00 00 02 08 50 00 00
00000010: 81 10 00 00 00 00 00 0C 00 00 00 00 00 00 00 00
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 B7 10 55 90
00000030: 00 00 00 00 DC 00 00 00 00 00 00 00 0B 01 0A 0A
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000050: 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000D0: 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 F6
000000E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Registers 0 and 1 are defined by the PCI spec as being the vendorID. The vendor ID is a 16bit value, in this case10B7h
Register 2 and 3 are the device ID, 9055h in this example.
By looking up the vendor ID number 10b7 in thePCIvendor databaseyou can see this is a 3COMcard. The 9055h is a 3COM generated number which describes whatmodel card this is.
Other registers can tell us it's an Ethernet controller, it's usingIRQ 11, uses a base I/O address of 1080h and more. Refer to a pcispecification to get all the details, as those are outside thescope of this document.
So, how do you get to all these registers in PCI space?
Accessing these registers is sorta like accessing CMOS memory inthat you first sendanindexvalue (apointer to the register you want to access) to the index port, thenyou read or writethedatato/from thedata port. The index is going to be a combination of all 4 abovementioned components, (bus, device, function,register)
In the x86 world,theindexport alwayslocated at 0CF8h andthedataport is alwayslocated at 0CFCh in I/O space. Most system BIOSes have softwareinterfaces to abstract the index/data port access from you. Idescribe both methods below. Refer to Ralph Brown's interrupt listfor details on the BIOS method of reading/writing PCI
Before you can get to any PCI function's registers, you shouldprobably make sure the PCI interface exists first!
Detecting the PCIbus
you can detect the PCI bus a couple different ways. the easist oneis to call the PCI BIOS interface directly and do an installationcheck.
note that you can also do a non-80386+ check by issuing this calland only check DX for "CP" (4350h), but when have you ever seen a286 with a PCI bus?
.386
mov ax, 0b101h ; interrupt 1a function b101
int 1ah ; will tell us if there is a PCI
cmp edx," ICP" ; bus on the board.
jz yup ; EDX=20494350h
; nope
yup:
------------------------------------
You can also find the PCI bus using I/O by just doing a quick readto port CFCh.
mov dx, 0cfch ; config data port
in eax, dx
cmp eax, -1 ; FFFFFFFF?
jz nope ; something must exist in PCI land
; yup
nope:
you will simply receive the data from the last PCI operation thatwas completed. This is by no means a foolproof method, but can dothe job for quick-n-dirty type applications.
finding a device on thePCI bus:
there are 2 ways of finding a PCI device on the bus. You can eitheruse the PCI BIOS interface call, or direct hardware I/O.
Here's the BIOS way:
INTEL_VENDOR_ID EQU 8086h ; intel's unique sig #
INTEL_EXP_NIC EQU 1227h ; sample PCI device etherexpress 10/100 NIC
.386
mov ax, 0b102h ; interrupt 1a function b102
mov dx, INTEL_VENDOR_ID
mov cx, INTEL_EXP_NIC
xor si, si ; 0=1st device, 1=2nd etc.
int 1ah
jc nope
; once returned from this call, BH=bus number, BL=device/function #
nope:
----------------------------------------------------------------
in the above example, SI will normally be zero, unless you aretrying to locate the 2nd or more instance of the same PCIdevice.
(ie, you had 3 PCI intel etherexpress cards installed)
an extremely handy utility for snooping through the PCI bus on yoursystem is a DOS program called PCIVIEW.EXE. i was able to locatethis utility on the net by simply searchingforpciview.exe
--------------------------------------------------------------------------------------
the non-BIOS way:
locating a specific device on the PCI bus requires you tounderstand how the PCI configuration cycle is broken down.
it's a 32bit value that looks like this:
bit31=1(bit is always set for a PCI access)
bits30:24=0 (reserved)
bit 23:16=bus number (0-255)
bits15:11=device # (0-31)
bits10:8=function # (0-7)
bits7:0=register number (0-255)
you send the above bit-encoded value out to the index port (cf8h)and then do a 32bit read from the data port (cfch)
here's how to read the vendor and device ID from a device sittingat bus 0, device 7, function 3.
BUS EQU 0
DEV EQU 7
FN EQU 3
VEN_ID EQU 0 ; vendor ID=PCI regs 0,1
PCI_INDEX EQU 0CF8h
PCI_DATA EQU 0CFCh
.386
mov ax, 8000h ; set bit 31 (after shift)
or al, BUS ; add in bus number
shl eax, 16
mov ax, DEV
shl ax, 11 ; slide device # up to bits 15:11
mov al, FN
or ah, al ; add function into bits 10:8
mov al, VEN_ID
cli
mov dx, PCI_INDEX
out dx, eax ; send our request out
mov dx, PCI_DATA
in eax, dx ; read back 32bit value.
sti
Remember that PCI registers are 8 bit values. The above read fromPCI_DATA reads a 32bit value, or 4 PCI registers. In the aboveexample, after the read, EAX = deviceID,AX=vendor ID.
Per the PCI specification, the vendor ID is always registers 0 and1, and the device ID is registers 2 and 3.
Thus, AL=register 0, AH=register 1, EAL=register 2, EAH=register3.
PCI express did some minor tweaks to the interface. Specifically,they changed the number of config registers from 255 to 4096. The1st 255 registers still look and feel just like regular PCIregisters-in fact, they are also readable and writable using thesame methods described above.
The additional registers however, are only available through amemory mapped subsystem, and it's a total pain in the cheeks to getto them.
For PCIe, it turns out that the *ENTIRE* PCI subsystem has beenmapped into a 256MB chunk of system memory. On the 2 machines thatI had a chance to play with, this memory map started at phsyicaladdress 0xe0000000. That's way up near the top of 4Gig of memory. Isuggest you grab a memory browser/editor that allows you to pokearound in memory out there and explore the area for yourself.
At 0xe0000000, you'll see a copy of every PCI(e) register from Bus0, Device 0, Function 0.
Function 1 follows at 0xe0000100, Function 2 at 0xe0000200, etc,all the way up to Bus 255, Device 31, Function 7. (ok, this isguess. I'm not sure if each function is packed into memory astightly as every 0x100 bytes-my machines didn't have multi-functionPCI devices, but you can clearly see that all PCI registers arethere in memory.)
Any time there was no PCI device, such as BUS 89, Device 9,Function 3, the memory just returns all FF's because nothing isthere to respond to the read. To me, it seems like a HUGE waste ofmemory space, considering that there is already a perfectly goodmechanism for accessing PCI, and all they'd need to do is use acouple of the reserved bits in the 32bit index port to allow accessto all the extended PCIe registers, but I digress...
Changing any register value here in memory is just like changingthem via I/O ports the old way; the memory map is just a mirror ofwhatever you see through the index and data ports and viceversa.
On your machine, your PCIe memory map might not be at 0xe0000000.How did I find this magic value? This is where the pain in thecheeks starts, with ACPI.
Tucked away in the ACPI Root System Description Table (RSDT) is anentry for the PCI Express memory mapped configuration space baseaddress Description Table, or MCFG for short.
In order to get there, you have to parse the ACPI tables. Here'show to do it:
1) search in memory (real mode) in the BIOS segments 0xE000 or0xF000 for the byte sequence:
"RSD PTR "
2) add 0x10 to the address that "RSD PTR " is found, to get a 32bitpointer to where the main ACPI RSDT tables are located.
3) You will need to be in protected mode, or unreal mode to accessmemory to do this next part.
Starting at the 32bit address of the RSDT tables (should be in highmemory, my NVidia board put them at 0x7FEF3040) search for thekeyword "MCFG" or parse through all the tables until you get to onelabeled "MCFG". At offset 0x2c from "MCFG" will be another 32bitpointer to the PCIe memory map.
My Nvidia board's "MCFG" table looks like this:
4D434647 3C000000 012C4E76 69646961 MCFG<....,Nvidia
4E564441 41435049 312E3042 4E564441 NVDAACPI1.0BNVDA
00000000 00000000 00000000 000000E0 ...............á
00000000 000000FF 00000000 00000000 .......ÿ........
I've got an ACPI dump program (for DOS)here:http://www.waste.org/~winkles/acpidumpalthoughit is out of date and won't dump out the MCFG table, it'll at leastpoint you in the right direction of where to look.
It appeared to me that very few PCIe devices have any additionalregisters above the 255 that are available through the index anddata ports, so getting to them this way appears to a bit of mootpoint, but I'm sure eventually something will come up, so now youknow.
feel free to contact me with questions or comments.