http://www.waste.org/~winkles/hardware/pci.htm
Detecting the PCI bus
you can detect the PCI
bus a couple different ways. the easist one is to call the
PCI BIOS interface directly and do an installation check.
note that you can also do a non-80386+ check by issuing this call and
only check DX for
"CP" (4350h), but when have you ever seen a 286 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 read to 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 that was completed.
This is by no means a foolproof method, but can do the job for
quick-n-dirty type
applications.
finding a device on
the PCI bus:
there are 2 ways of
finding a PCI device on the bus. You can either use 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 are trying
to locate the 2nd or
more instance of the same PCI device.
(ie, you had 3 PCI intel etherexpress cards installed)
an extremely handy
utility for snooping through the PCI bus on your system is a DOS
program called PCIVIEW.EXE. i was able to locate this utility on the
net by simply
searching for pciview.exe
--------------------------------------------------------------------------------------
the non-BIOS way:
locating a specific device on the PCI bus requires you to understand
how the PCI
configuration cycle is broken down.
it's a 32bit value that looks like this:
bit 31=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 sitting at
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 from PCI_DATA reads
a 32bit value, or 4 PCI registers. In the above example, after the
read, EAX =
device ID, AX = vendor ID.
Per the PCI specification, the vendor ID is always registers 0 and 1,
and the device ID is
registers 2 and 3.
Thus, AL=register 0, AH=register 1, EAL=register 2, EAH=register 3.
PCIe update:
PCI express did some
minor tweaks to the interface. Specifically, they changed the number of
config registers from 255 to 4096. The 1st 255 registers still look and
feel just like
regular PCI registers-in fact, they are also readable and writable
using the same methods
described above.
The additional registers however, are only available through a memory
mapped subsystem, and
it's a total pain in the cheeks to get to them.
For PCIe, it turns out that the *ENTIRE* PCI subsystem has been mapped
into a 256MB chunk of system memory. On the 2 machines that I had a
chance to play with, this memory map started
at phsyical address 0xe0000000. That's way up near the top of 4Gig of
memory. I suggest
you grab a memory browser/editor that allows you to poke around in
memory out there and
explore the area for yourself.
At 0xe0000000, you'll see a copy of every PCI(e) register from Bus 0,
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 is guess. I'm
not sure if each function is packed into
memory as tightly as every 0x100 bytes-my machines didn't have
multi-function PCI devices, but
you can clearly see that all PCI registers are there 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 is there to respond to
the read. To me, it seems like a HUGE
waste of memory space, considering that there is already a perfectly
good mechanism for accessing PCI, and all they'd need to do is use a
couple of the reserved bits in the 32bit
index port to allow access to all the extended PCIe registers, but I
digress...
Changing any register value here in memory is just like changing them
via I/O ports the old way; the
memory map is just a mirror of whatever you see through the index and
data ports and vice versa.
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 the cheeks
starts, with ACPI.
Tucked away in the ACPI Root System Description Table (RSDT) is an
entry for the
PCI Express memory mapped configuration space base address Description
Table, or MCFG for short.
In order to get there, you have to parse the ACPI tables. Here's how to
do it:
1) search in memory (real mode) in the BIOS segments 0xE000 or 0xF000
for the byte sequence:
"RSD PTR "
2) add 0x10 to the address that "RSD PTR " is found, to get a 32bit
pointer to where the main ACPI RSDT tables are located.
3) You will need to be in protected mode, or unreal mode to access
memory to do this next part.
Starting at the 32bit address of the RSDT tables (should be in high
memory, my NVidia board put them at 0x7FEF3040) search for the keyword
"MCFG" or parse through all the tables
until you get to one labeled "MCFG". At offset 0x2c from "MCFG" will be
another 32bit pointer 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/acpidump
although it is out of date and won't dump out the MCFG table, it'll at
least point you
in the right direction of where to look.
It appeared to me that very few PCIe devices have any additional
registers above the 255
that are available through the index and data ports, so getting to them
this way appears
to a bit of moot point, but I'm sure eventually something will come up,
so now you know.
feel free to contact me with questions or comments.
-jeff!

|