Read PCI config address for PCI device iteration

185 views Asked by At

I want to find all PCI devices with as little Linux use as possible because of exercise.

I find in scattered docs to read address 0xCF8 or maybe there is some protocol where I write to that address in RAM but I haven't been able to hack it.

Any attempts to read that address or write some config address to write to it segfault in simple C/C++ programs. I thought I might need ring 0 so I created a Linux kernel module but the program got killed just trying to read that 0xCF8 address as a MULL pointer dereference?

I am aware of lspci, /sys/bus, etc... This is a little more advanced please.

1

There are 1 answers

3
Ian Abbott On

You can probably do everything you want in userspace using pcilib, which is used by lspci, etc., but isn't well documented. If you must use kernel code, carry on reading…

Linux kernel code can use the for_each_pci_dev() macro and pointers to struct pci_dev (both defined by #include <linux/pci.h>) to iterate over all PCI device like this:

struct pci_dev *pdev;

pdev = NULL; /* IMPORTANT: Start with NULL unless continuing a previous loop. */
for_each_pci_dev(pdev) {
    /* Examine pdev-> members here. */
}

Each iteration of the loop will decrement ("put") the reference count of the current struct pci_dev pointed to by pdev (if it is not NULL) and will increment ("get") the reference count of the next struct pci_dev pointed to by pdev. When the loop terminates naturally after the last PCI device has been seen, pdev will be NULL. If the code breaks out of the loop early so that pdev is still valid, it may need to decrement ("put") the reference with pci_dev_put(pdev); when it has finished with it.

for_each_pci_dev(pdev) uses pci_get_device() internally, so you could replace for_each_pci_dev(pdev) with the following while loop:

struct pci_dev *pdev;

pdev = NULL; /* IMPORTANT: Start with NULL unless continuing a previous loop. */
while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev) != NULL) {
    /* Examine pdev-> members here. */
}

The PCI_ANY_ID value matches any PCI vendor ID or PCI device ID, so you can change the first two arguments of pci_get_device() to only iterate over PCI devices with a specific vendor and/or device ID.

Using your own while (or for) loop also allows you to change the pci_get_device() calls to similar functions. For example pci_get_subsys() can match devices with specific PCI vendor, device, subsystem vendor, and/or subsystem device IDs, and pci_get_class() can match devices with specific PCI class codes.