Quickly scanning PCI config space

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Quickly scanning PCI config space

Post by NickJohnson »

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!
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Quickly scanning PCI config space

Post by Brendan »

Hi,

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?
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).

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.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Quickly scanning PCI config space

Post by bewing »

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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Quickly scanning PCI config space

Post by Brendan »

Hi,
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.
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.

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.
User avatar
XanClic
Member
Member
Posts: 138
Joined: Wed Feb 13, 2008 9:38 am

Re: Quickly scanning PCI config space

Post by XanClic »

bewing wrote:Note however that bochs & qemu do not support this BIOS function. At least the last time I tested it.
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).
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: Quickly scanning PCI config space

Post by Kevin »

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.
Developer of tyndur - community OS of Lowlevel (German)
User avatar
XanClic
Member
Member
Posts: 138
Joined: Wed Feb 13, 2008 9:38 am

Re: Quickly scanning PCI config space

Post by XanClic »

As for me, I use 0.11.0. :wink:
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: Quickly scanning PCI config space

Post by Kevin »

Hm... But it was a nice theory at least. ;)
Developer of tyndur - community OS of Lowlevel (German)
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: Quickly scanning PCI config space

Post by NickJohnson »

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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Quickly scanning PCI config space

Post by Brendan »

Hi,
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.
Yes.

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.
Post Reply