Page 1 of 1

PCI: Multiple Vendor and Device IDs for one device index?

Posted: Sat Feb 20, 2021 9:49 am
by peachsmith
This is a bit of a sanity check for my PCI enumeration code.

Given a bus, device, and function index of b/d/f, is it allowed to have multiple vendor IDs and device IDs for a single device index d?
Could a single physical device's configuration space start at (b/d/n), where n is anywhere between 0 and 7?

This feels like something that might have been stated prominently in the wiki, but I've been bouncing back and forth between the PCI and PCIe pages since the header structures are on one page and the #Enhanced Configuration Mechanismâ„¢ is on the other.

My code currently gives the following output (all numbers are in hex):

Code: Select all

+------------------------------------------------+
| (b/d/f)    VID   DID   Class  Subclass  Header |
+------------------------------------------------+
| ( 0/ 0/ 0) 8086  7190  6      0         0      |
|------------------------------------------------|
| ( 0/ 1/ 0) 8086  7191  6      4         1      |
|------------------------------------------------|
| ( 0/ 7/ 0) 8086  7110  6      1         0      |
| ( 0/ 7/ 1) 8086  7111  1      1         0      |
| ( 0/ 7/ 3) 8086  7113  6      80        0      |
| ( 0/ 7/ 7) 15AD  740   8      80        0      |
|------------------------------------------------|
| ( 0/ F/ 0) 15AD  405   3      0         0      |
|------------------------------------------------|
| ( 0/11/ 0) 15AD  790   6      4         1      |
|------------------------------------------------|
| ( 0/15/ 0) 15AD  7A0   6      4         1      |
| ...etc                                         |
Basically, I'm doing the brute force method wherein I check the header at each function 0, (b/d/0), and if bit 7 of the header is set, I then check the rest of the functions like so:

Code: Select all

  // Iterate over all busses in the configuration space.
  for (uint8_t b = pci_base.bus_start; b < pci_base.bus_end; b++)
  {
    // There are 32 devices per bus.
    for (uint8_t d = 0; d < 32; d++)
    {
      // All PCI devices are required to implement function 0.
      int res = print_bdf(b, d, 0);
      if (res == 1)
      {
        // There are 8 functions for each device.
        for (uint8_t f = 1; f < 8; f++)
        {
          print_bdf(b, d, f);
        }
      }
    }
  }
And here is how I read the configuration space of a single b/d/f:

Code: Select all

static int print_bdf(uint8_t b, uint8_t d, uint8_t f)
{
  uint64_t p = pci_base.phys_base;

  p += (
    ((uint64_t)b << 20) | ((uint64_t)d << 15) | ((uint64_t)f << 12)
  );

  // Map the 4KiB configuration space into virtual memory.
  k_regn v = k_paging_map_range(p, p + 0x1000);
  if (v == 0)
  {
    fprintf(stddbg, "[ERROR] failed to map PCI b/d/f into virtual memory\n");
    return 0;
  }

  uint8_t* cfg = (uint8_t*)v;

  uint16_t ven = *(uint16_t*)cfg;       // vendor ID
  uint16_t dev = *(uint16_t*)(cfg + 2); // device ID
  uint8_t sub = *(uint8_t*)(cfg + 10);  // subclass
  uint8_t cls = *(uint8_t*)(cfg + 11);  // class
  uint8_t hea = *(uint8_t*)(cfg + 14);  // header type

  // Unmap the configuration space, since we're not using it yet.
  k_paging_unmap_range(v);

  // Check that vendor ID and device ID are not all ones
  if (ven != 0xFFFF && dev != 0xFFFF)
  {
    fprintf(stddbg,
      "| (%2X/%2X/%2X) %-4X  %-4X  %-5X  %-8X  %-6X |\n",
      b, d, f, ven, dev, cls, sub, hea & 0x7F
    );
  }
  else
  {
    return 0;
  }

  // return 1 to indicate the device has multiple functions,
  // or 2 if it is a single-function device.
  return (hea & 0x80) ? 1 : 2;
}

Re: PCI: Multiple Vendor and Device IDs for one device index

Posted: Sat Feb 20, 2021 10:06 am
by iansjack
I see what you mean about the 0/7/* devices. The odd one out is a VMWare Virtual Machine Communication Interface, which might explain the apparent inconsistency.

Re: PCI: Multiple Vendor and Device IDs for one device index

Posted: Sat Feb 20, 2021 12:20 pm
by peachsmith
Ok, so I need to reevaluate my understanding of the terminology.

Here are my three assumptions:
    1. The word "device" when used in the context of a PCIe endpoint (bus/device/function) does not mean "actual, individual physical object" any more than "function" means "code that executes like a C function or assembly procedure"
    • When I check the header type at endpoint (b/d/0) and see that bit 7 is set, indicating multiple functions, each function at (b/d/f) represents an individual device that may or may not care about the existence of other devices.
    • If I check the header at endpoint (b/d/0) and see that bit 7 is not set, then I can assume that there are no more PCI "functions" at that PCI "device".
How correct are these statements?

Re: PCI: Multiple Vendor and Device IDs for one device index

Posted: Sat Feb 20, 2021 2:16 pm
by xeyes
My observation:

It is usually a single device in the typical sense of the word.

The function number can be "abused" to contain a different device ID just because function has its own header and thus those fields. They are still more like functions of the same device IMHO.

For example a modern video card can also be an audio controller (HDMI, DP) and a USB controller (USB-C). If you scan a real computer or lspci -n on one, you'd see them each has its own device id and not sharing with the video card part.

The one you listed, multiple vendors making the same device, must be quite rare though :shock:

Re: PCI: Multiple Vendor and Device IDs for one device index

Posted: Sat Feb 20, 2021 4:04 pm
by peachsmith
Ah, so (b/d/0) could be a single physical card that has one thing on it that does one thing, or it could be a single physical card with multiple things on it that do different things.

That sounded silly as I read it back.
Basically, you could buy a single PCIe card and plug it into one slot on the motherboard, and it may have multiple BDF endpoints that each represent a logically or physically separate thing that performs some action.

So whether or not they're connected to the same physical object, two different endpoints (b/d/0) and (b/d/1) should logically be considered two different devices.

Re: PCI: Multiple Vendor and Device IDs for one device index

Posted: Sat Feb 20, 2021 4:17 pm
by xeyes
peachsmith wrote:Ah, so (b/d/0) could be a single physical card that has one thing on it that does one thing, or it could be a single physical card with multiple things on it that do different things.

That sounded silly as I read it back.
Basically, you could buy a single PCIe card and plug it into one slot on the motherboard, and it may have multiple BDF endpoints that each represent a logically or physically separate thing that performs some action.

So whether or not they're connected to the same physical object, two different endpoints (b/d/0) and (b/d/1) should logically be considered two different devices.
Yes it is not clear cut in either way so probably best to go by the spec of each device rather than generalize too much.

It can be argued "a single PCIe card" is also multiple physical devices, as the audio controller driving audio out of HDMI really isn't the same piece/part of silicon as the video controller.

But the functions aren't fully "logically separated" either, for example some cards may allow FLR, while some other cards have to be reset as a whole (so all the functions would be reset).