PCI: Multiple Vendor and Device IDs for one device index?
Posted: Sat Feb 20, 2021 9:49 am
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):
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:
And here is how I read the configuration space of a single b/d/f:
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 |
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);
}
}
}
}
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;
}