每一个 Capability 结构都有唯一的ID 号,每一个Capability 寄存器都有一个指针,这个指针指向下一个Capability 结构,从而组成一个单向链表结构,这个链表的最后一个Capability 结构的指针为0。链表开始的指针地址为0x34处的1byte数值,寻址过程如下。
pci_find_capability() 断定一个设备是否支持给定PCI权能,返回在设备PCI配置空间内所请求权能结构的地址,如果设备不支持这种权能,则返回0;
PCIE的MSI-X相关信息存在两个地方,一个是PCIE Capability中,存放MSI-X基本信息,主要包含MSI-X Table所在BAR地址相关信息(访问的MSI-X Table关键),另外一个是MSI-X Table,存放在bar空间中,标识中断的msg addr及对应的msg data(即中断vector)。
MSI-X Capability结构,此结构在PCIe设备配置空间偏移0xB0的位置处,MSI-X中断表不在Capability中,存在bar中,通过table offset和table bir定位。
/* MSI-X registers */
#define PCI_MSIX_FLAGS 2
#define PCI_MSIX_FLAGS_QSIZE 0x7FF
#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
#define PCI_MSIX_FLAGS_MASKALL (1 << 14)
#define PCI_MSIX_TABLE 4
#define PCI_MSIX_PBA 8
#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) // LOW 3 bitsvfio_early_setup_msix {pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); if (!pos) { return 0; } table = pci_get_long(vdev->pdev.config + pos + PCI_MSIX_TABLE); // 获取 MSI-X table(偏移 4 字节) pba = pci_get_long(vdev->pdev.config + pos + PCI_MSIX_PBA); ctrl = pci_get_word(vdev->pdev.config + pos + PCI_CAP_FLAGS); vdev->msix = g_malloc0(sizeof(*(vdev->msix))); vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK; // 获取 msi-x table 所在的bar vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
}
lspci查看当前pcie的msi-x capability
参考
PCIe MSI-X 中断编程 - 知乎