Page 1 of 1

pci device detection

Posted: Mon Feb 12, 2007 2:27 am
by os64dev
Currently i am trying to enumerate all the pci devices with the following code. It is kind a raw i guess because it scans all the posibble combinations. pci::read_pci returns a pointer to the configuration header of a device or null if there is no device at that location.

Code: Select all

for(int bus = 0; bus <= 0xFF; bus++) {
        for(int dev = 0; dev <= 0x1F; dev++) {
            for(int fnc = 0; fnc <= 0x07; fnc++) {
                pci_cfg = pci::read_pci(bus, dev, fnc);
            }
        }
    }
now my question is because i find a lot of pci bridges how do i detect de devices behind the bridges, do i have to program the bridge device? or does the above code find all the device.

regards

Re: pci device detection

Posted: Mon Feb 12, 2007 4:27 am
by Brendan
Hi,
os64dev wrote:Currently i am trying to enumerate all the pci devices with the following code. It is kind a raw i guess because it scans all the posibble combinations. pci::read_pci returns a pointer to the configuration header of a device or null if there is no device at that location.
For each PCI bridge there's a "primary bus" and a "secondary bus" where the secondary bus is the bus on the other side of the bridge. Because PCI is a hierarchy of buses, you can start at the top (which is always "bus 0") and find all other buses without checking every bus number.

There's also a flag somewhere in a device's PCI configuration space (e.g. bus X, device Y, function 0) that determines if it is a multi-function device or not. If it's not a multi-function device you don't need to check the other functions (e.g. bus X, device Y, function 1 to bus X, device Y, function 7 can be skipped).

PCI bus 0, device 0, function 0 is always the first PCI host controller. If the system has multiple PCI host controllers then PCI bus 0, device 0 will be a multi-function device (with one function per host controller). This means that PCI bus 0, device 0, function 0 should represent a host controller with a secondary bus = 0, and PCI bus 0, device 0, function 1 should be another PCI host controller with a secondary bus = 1. AFAIK the secondary bus number returned in PCI host controllers is hard-wired (regardless of how many PCI host controllers there are).

This is all suited to recursive functions, for either detecting settings (like most OSs) or configuring the PCI bus numbers and other things (like the BIOS does).
os64dev wrote:now my question is because i find a lot of pci bridges how do i detect de devices behind the bridges, do i have to program the bridge device? or does the above code find all the device.
Very roughly, it'd go something like:

Code: Select all

void main(void) {
    scanPCIbus(0);
}


void scanPCIbus(int bus) {
    int device;
    int function;

    for(device = 0; device < 32; device ++) {
        if( checkIfMultifunction(bus, device) == MULTI_FUNCTION) {
            for(function = 0; function < 7; function++) {
                scanPCIfunction(bus, device, function);
            }
        } else {
            scanPCIfunction(bus, device, 0);
        }
    }
}


void scanPCIfunction(int bus, int device, int function) {
    int type;

    if( (bus == 0) && (device == 0) ) {
        handlePCIhostController(bus, device, function);
    } else {
        type = getPCIdeviceType();
        switch(type) {
        case PCI_TO_PCI_BRIDGE:
            handlePCItoPCIbridge(bus, device, function);
            break;
       /* More device types here */ 
       }
    }
}

void handlePCIhostController(int bus, int device, int function) {
    scanPCIbus( getSecondaryBus(bus, device, function) );
}

void handlePCItoPCIbridge(int bus, int device, int function); {
    scanPCIbus( getSecondaryBus(bus, device, function) );
}
Note that if you're completely reconfiguring the PCI buses you'd keep track of used bus numbers and set the bus number in the "secondary bus number" field, rather than using a "getSecondaryBus()" function.

You shouldn't have to program the bridge device, as the BIOS should do this correctly. Here I use the word "should" like a lawyer (i.e. "should" isn't necessarily the same as "will"), and there's no guarantee that the BIOS will acheive the best configuration. It's also technically possible (but very unlikely) that someone will plug in a new PCI bridge while the OS is runniing (if the computer supports hot-plug PCI)

BTW the code you had will find everything on the PCI bus, it's just that it's less efficient and harder to get right if you're configuring bridges (including setting which address range/s are forwarded by the bridge from the primary bus to the secondary bus).


Cheers,

Brendan

Posted: Mon Feb 12, 2007 4:30 am
by Combuster
If your system has been properly configured by the BIOS, all the devices behind the PCI bridges should be visible. (Like, an AGP video card is generally visible from boot even though its behind a PCI bridge)

One thing you should be aware of is that devices with only one function usually dont decode the function part of the address, i.e. you get 8x the same device if it doesnt have multiple functions. (which might also explain the amount of bridges you get)

[edit]I see brendan beat me to the point[/edit]

Posted: Mon Feb 12, 2007 5:01 am
by os64dev
@brendan
thanks for the explanation, i see that i get all the devices though in a very inefficient way. Your code example explains a lot and shows a lot of similarities with a new version i've implemented but i seem to get stuck at the function getSecondaryBus how is this implemented and where can i obtain more information about it and please another answer then pcisig :wink:

regards

Posted: Mon Feb 12, 2007 5:17 pm
by Brendan
Hi,
os64dev wrote:@brendanthanks for the explanation, i see that i get all the devices though in a very inefficient way. Your code example explains a lot and shows a lot of similarities with a new version i've implemented but i seem to get stuck at the function getSecondaryBus how is this implemented and where can i obtain more information about it and please another answer then pcisig :wink:
To determine if the device is a multi-function device, you need to check bit 7 of the "header type" field in the devices PCI configuration space (set = multifunction, clear = single function).


Cheers,

Brendan

getting a PCI bus count

Posted: Mon Feb 12, 2007 9:48 pm
by bewing
If you are not using GRUB, then while you are still in real mode, you can do an INT call to get the total number of PCI busses, and shorten your loop to only what is used. Busses are always supposed to be sequential -- no skips.
(This is one reason why I don't use GRUB, and wrote my own bootloader -- GRUB doesn't pass me enough info from real mode.)

Code: Select all

	mov ax, 0xb101
	int 0x1a			; get PCI bus info
	jc short nopci

; check signature bytes
	cmp dx, 0x4350
	jne short nopci

; check for error code, AH=00 is good
	test ah, ah
	jne short nopci

; info is here, then. CL is the PCI bus count -1
; BH is the PCI major version number of the chipset
; BL is the PCI minor version number
; AL bit 0 means "use configuration method 1" -- should always be set
; bit 1 means "use config method 2" -- should never be set
; bit 4 means use special cycle method 1
; bit 5 means use special cycle method 2
When you do your check, if function 0 does not exist for a device, then the device does not exist at all, and you can skip the other 7 functions.
os64dev wrote:how do i detect the devices behind the bridges, do i have to program the bridge device?
As said above, your code finds all the devices, because the BIOS programmed and initialized all the PCI devices (including bridges) during bootup.

Re: getting a PCI bus count

Posted: Tue Feb 13, 2007 2:53 am
by os64dev
bewing wrote:If you are not using GRUB, then while you are still in real mode, you can do an INT call to get the total number of PCI busses, and shorten your loop to only what is used. Busses are always supposed to be sequential -- no skips.
(This is one reason why I don't use GRUB, and wrote my own bootloader -- GRUB doesn't pass me enough info from real mode.)
for the sequential busses thingy i'll have to check that because i think it skipped a bus on my amd64 x2 4400+. i also don't use grub fro the same reasons.

regards