driverspci

Read address from MSI Capability Structure


Is it possible to find the location of the MSI Capability Structure associated with a particular interrupt? Specifically, I need to know the PCI address that when written to, triggers that interrupt.

MSI interrupts can easily be initialize with the pci_alloc_irq_vectors(9) function, but this simply provides the irq number and no reference to the capability structure.

For reference, the capability structure is described in this document: https://pcisig.com/sites/default/files/specification_documents/msi-x_ecn.pdf


Solution

  • It sounds like you want to be able to write that value yourself and generate an interrupt. That's not really how MSI works (though it might still be possible). With MSI (or MSI-X), you're basically programming the PCI device with an address to which it should generate a data write and a data value to write there when an interrupt is to be generated.

    It's not guaranteed AFAIK that you can trigger the same interrupt yourself by writing to an address. Still, typically the address given in the MSI is a location within an address space controlled by the interrupt controller (which is usually implemented as a PCI device itself), and the data value tells the interrupt controller which interrupt to trigger. So in all likelihood, you could write the same value to the same physical memory address and so generate the same interrupt.

    In any case, assuming you know which PCI device is generating the interrupt, you can go find the MSI capability structure and hence you could read back what it was programmed with. That's straightforward to do.

    The PCI capabilities (which vary by device) are organized into a linked list within the device's configuration space. The start of the list is always given by the byte at offset 0x34 within the device configuration space. That byte value gives you the offset within the space of the first capability data structure.

    Each capability consists of a one byte capability type ID, followed by a one byte "next capability" pointer, followed by varying-length data specific to the capability. So, starting from offset 0x34, you can hop through the capabilities.

    To see this in action on any linux machine, you can run lspci. Giving it the -v flag (which can be repeated to get more and more detail) gives you an annotated view of the config space. You can also add -xxxx to get a complete hex dump of the config space, and so follow along the capability chain yourself. (BTW, you need to run it with sudo to get all the capability details.)

    There are interfaces within the kernel that will do this for you: you can use pci_find_capability to find the offset of the capability you want. You can of course also find the capability yourself by starting with pci_read_config_byte with the offset 0x34, and following the list.

    Once you've found the MSI capability, you then interpret its contents according to the document you cited above. You would use pci_read_config_byte (/word/dword) to access the parts of the capability data structure.