从 MSI Capability Structure 读取地址

Read address from MSI Capability Structure

是否可以找到与特定中断关联的 MSI 能力结构的位置?具体来说,我需要知道写入时触发该中断的 PCI 地址。

MSI 中断可以很容易地用 pci_alloc_irq_vectors(9) 函数初始化,但这只是提供了 irq 号,没有引用能力结构。

作为参考,此文档中描述了功能结构:https://pcisig.com/sites/default/files/specification_documents/msi-x_ecn.pdf

听起来您希望能够自己写入该值并生成中断。这并不是 MSI 的实际工作方式(尽管它可能仍然是可能的)。使用 MSI(或 MSI-X),您基本上是使用一个地址对 PCI 设备进行编程,它应该向该地址生成数据写入,并在要生成中断时向该地址写入一个数据值。

AFAIK 不能保证您可以通过写入地址自行触发相同的中断。尽管如此,通常 MSI 中给出的地址是由中断控制器(通常作为 PCI 设备本身实现)控制的地址 space 内的一个位置,数据值告诉中断控制器触发哪个中断。所以很有可能,您可以将相同的值写入相同的物理内存地址,从而生成相同的中断。

无论如何,假设您知道哪个 PCI 设备正在生成中断,您就可以找到 MSI 功能结构,因此您可以读回它的编程内容。这很简单。

PCI 功能(因设备而异)被组织到设备配置中的链接列表中 space。列表的开头始终由设备配置中偏移量 0x34 处的字节给出 space。该字节值为您提供了第一个功能数据结构的 space 内的偏移量。

每个功能都包含一个单字节的功能类型 ID,后跟一个单字节的 "next capability" 指针,然后是特定于该功能的变长数据。因此,从偏移量 0x34 开始,您可以跳过这些功能。

要在任何 linux 机器上查看此操作,您可以 运行 lspci。给它 -v 标志(可以重复以获得越来越多的细节)给你一个配置 space 的注释视图。您还可以添加 -xxxx 以获得配置 space 的完整十六进制转储,因此您自己可以按照功能链进行操作。 (顺便说一句,您需要 运行 它与 sudo 以获得所有功能的详细信息。)

内核中有一些接口可以为您完成此操作:您可以使用 pci_find_capability 来查找所需功能的偏移量。当然,您也可以通过从偏移量为 0x34 的 pci_read_config_byte 开始,然后按照列表查找自己的功能。

找到 MSI 功能后,您便可以根据上面引用的文档解释其内容。您将使用 pci_read_config_byte (/word/dword) 来访问能力数据结构的各个部分。