Quickly scanning PCI config space
- NickJohnson
- Member
- Posts: 1249
- Joined: Tue Mar 24, 2009 8:11 pm
- Location: Sunnyvale, California
Quickly scanning PCI config space
I'm trying to write a (originally thought to be) simple function that will run through all of the functions of all of the slots of all of the buses in the PCI configuration space and locate all instances of a class and optionally a subclass, obviously for device detection purposes. Originally, I wrote it to scan through all three dimensions assuming they were contiguous, i.e. if bus 1 does not exist, bus 2 does not exist; same for slots and functions. Of course, I was completely wrong, which I realized after inspecting lspci on my laptop. I know that if function 0 does not exist for a slot, no other functions do, but are there any more rules like that that will save me from looping though (and doing at least two I/O cycles for) all 65536 possible combinations of bus, slot, and function fields? Is there any way to quickly figure out the topology of the buses or slots without actually checking each one?
Thanks!
Thanks!
Re: Quickly scanning PCI config space
Hi,
For PCI to PCI bridges (anything with 1 in the "header type"), the format of PCI configuration space is different, and there's a "primary bus number" field (which you already knew to access the PCI to PCI bridge), a "secondary bus number" field and a "suboordinate bus number" field. The "secondary bus number" is the number of the bus that is directly behind the bridge. The "suboordinate bus number" is number of the highest bus that is (directly or indirectly) behind the bridge.
You can use this information to avoid checking a lot of things that aren't present anyway (e.g. all "not present" buses and most "not present" functions). For example:
Cheers,
Brendan
If bit 7 in the "header type" field (for function 0) is clear then the device is a single-function device (and you don't need to care about functions 1 to 7 as they don't exist). If it's a multi-function device then the function numbers should be sequential (when you've found the first invalid function you can assume that higher number functions aren't present).NickJohnson wrote:I'm trying to write a (originally thought to be) simple function that will run through all of the functions of all of the slots of all of the buses in the PCI configuration space and locate all instances of a class and optionally a subclass, obviously for device detection purposes. Originally, I wrote it to scan through all three dimensions assuming they were contiguous, i.e. if bus 1 does not exist, bus 2 does not exist; same for slots and functions. Of course, I was completely wrong, which I realized after inspecting lspci on my laptop. I know that if function 0 does not exist for a slot, no other functions do, but are there any more rules like that that will save me from looping though (and doing at least two I/O cycles for) all 65536 possible combinations of bus, slot, and function fields? Is there any way to quickly figure out the topology of the buses or slots without actually checking each one?
For PCI to PCI bridges (anything with 1 in the "header type"), the format of PCI configuration space is different, and there's a "primary bus number" field (which you already knew to access the PCI to PCI bridge), a "secondary bus number" field and a "suboordinate bus number" field. The "secondary bus number" is the number of the bus that is directly behind the bridge. The "suboordinate bus number" is number of the highest bus that is (directly or indirectly) behind the bridge.
You can use this information to avoid checking a lot of things that aren't present anyway (e.g. all "not present" buses and most "not present" functions). For example:
Code: Select all
void main(void) {
scanDevices(0);
}
scanDevices(int bus) {
for(device = 0; device < 32; device++) {
if(vendorID(bus, device, 0) is valid) {
checkDevice(bus, device, 0);
if(headerType(bus, 0, 0) has_bit7_set) {
for(function = 1; function < 8; function++) {
if(vendorID(bus, device, function) is valid) checkDevice(bus, device, function);
else break;
}
}
}
}
}
checkDevice(int bus, int device, int function) {
if(headerType(bus, device, function) says_device_is_PCI_to_PCI_bridge) {
printf("Found bridge: <details>\n");
secondaryBus = get_secondary_bus((bus, device, function);
scanDevices(secondaryBus);
} else {
printf("Found device: <details>\n");
}
}
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Quickly scanning PCI config space
As I understand it, all motherboards that support PCI are supposed to support BIOS function INT1A AX=B101 -- IIRC CL is the maximum PCI bus number configured on the machine, so you can discount all of them above that.
Note however that bochs & qemu do not support this BIOS function. At least the last time I tested it.
Note however that bochs & qemu do not support this BIOS function. At least the last time I tested it.
Re: Quickly scanning PCI config space
Hi,
Also note that for hot-plug PCI you might (eventually) need code to scan PCI buses after boot (when the PC BIOS may be unusable); and for embedded systems you may need to configure PCI yourself (rather than just assuming that the firmware pre-configured everything). In both of these cases your code would need to be able to (for e.g.) set the "primary bus", "secondary bus" and "subordinate bus" numbers in bridges (rather than reading them), and do some other things (like setting I/O ranges and memory ranges in bridges that the bridge forwards to the secondary bus, configuring BARs in devices, etc).
Cheers,
Brendan
It's probably good to do it yourself (e.g. using I/O ports directly), so that it's easier for your OS to support other types of firmware (e.g. EFI/UEFI, coreboot, OpenFirmware) later on; and so that your OS is immune to BIOS/firmware bugs.bewing wrote:As I understand it, all motherboards that support PCI are supposed to support BIOS function INT1A AX=B101 -- IIRC CL is the maximum PCI bus number configured on the machine, so you can discount all of them above that.
Also note that for hot-plug PCI you might (eventually) need code to scan PCI buses after boot (when the PC BIOS may be unusable); and for embedded systems you may need to configure PCI yourself (rather than just assuming that the firmware pre-configured everything). In both of these cases your code would need to be able to (for e.g.) set the "primary bus", "secondary bus" and "subordinate bus" numbers in bridges (rather than reading them), and do some other things (like setting I/O ranges and memory ranges in bridges that the bridge forwards to the secondary bus, configuring BARs in devices, etc).
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Quickly scanning PCI config space
At least qemu does. My entry for the fourth 512 byte contest relies on exactly that (see http://forum.osdev.org/viewtopic.php?f=2&t=21042).bewing wrote:Note however that bochs & qemu do not support this BIOS function. At least the last time I tested it.
Re: Quickly scanning PCI config space
qemu was using bochsbios until (including) 0.11. It switched to SeaBIOS though for 0.12 (released in December), so that's probably what makes the difference.
Re: Quickly scanning PCI config space
As for me, I use 0.11.0.
Re: Quickly scanning PCI config space
Hm... But it was a nice theory at least.
- NickJohnson
- Member
- Posts: 1249
- Joined: Tue Mar 24, 2009 8:11 pm
- Location: Sunnyvale, California
Re: Quickly scanning PCI config space
Thanks for the suggestions.
Using the BIOS function is out of the question for me anyway, because this scanning is being done from a userspace driver.
@Brendan: so, you're saying I should recursively run through each bus by following the PCI-PCI bridges? That seems like a good idea, provided I interpreted it right of course.
Using the BIOS function is out of the question for me anyway, because this scanning is being done from a userspace driver.
@Brendan: so, you're saying I should recursively run through each bus by following the PCI-PCI bridges? That seems like a good idea, provided I interpreted it right of course.
Re: Quickly scanning PCI config space
Hi,
You should be able to find & download a document called "PCI-to-PCI Bridge Architecture Specification Revision 1.1" (a PDF file), which includes a description of the format used for the PCI configuration space header used by PCI to PCI bridges. There's also some other interesting stuff in this document..
Cheers,
Brendan
Yes.NickJohnson wrote:@Brendan: so, you're saying I should recursively run through each bus by following the PCI-PCI bridges? That seems like a good idea, provided I interpreted it right of course.
You should be able to find & download a document called "PCI-to-PCI Bridge Architecture Specification Revision 1.1" (a PDF file), which includes a description of the format used for the PCI configuration space header used by PCI to PCI bridges. There's also some other interesting stuff in this document..
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.