如何知道或设置一个PCI/PCIe设备地址映射?

How to know or set a PCI/PCIe device address map?

我正在尝试了解如何指示 CPU 指示 PCI 设备将其内存映射到 CPU 的物理地址。

我读过 https://wiki.osdev.org/PCI#Memory_Mapped_PCI_Configuration_Space_Access 并且我了解如何通过 CPU:

的 IO 端口配置 PCI 设备

Two 32-bit I/O locations are used, the first location (0xCF8) is named CONFIG_ADDRESS, and the second (0xCFC) is called CONFIG_DATA. CONFIG_ADDRESS specifies the configuration address that is required to be accesses, while accesses to CONFIG_DATA will actually generate the configuration access and will transfer the data to or from the CONFIG_DATA register.

因此,为了配置 PCI 设备,我只需要输入我想要配置的配置地址,CONFIG_DATA 是我放置数据的地方。这两个都是 I/O 位置,所以我在 CPU.

中使用 I/O 指令

这两个 I/O 位置让我可以访问 256 字节 "big register",我可以用它来配置 PCI 设备。这个大寄存器的所有字段都在 OSDEV 页面中进行了描述。感兴趣的是这 2 位:

Memory Space - If set to 1 the device can respond to Memory Space accesses; otherwise, the device's response is disabled.

I/O Space - If set to 1 the device can respond to I/O Space accesses; otherwise, the device's response is disabled.

然而除此之外,还不清楚如何获取或设置每个 PCI 设备将响应的物理地址 space

These two I/O locations give me access to a 256byte "big register" that I can use to configure the PCI device.

那个"big register"是一个有很多字段的结构体。其中一个字段是 "Command Register",它包含全局 enable/disable 标志,可用于禁用设备向总线发送内容(IRQ、从设备写入内存等)和响应来自总线的东西(从 CPU 写入设备等)。

其他字段包括 "BARs"(基地址寄存器,从设备配置中的偏移量 0x10 开始 space),它告诉设备哪个地址范围(在 IO 端口 space 或物理地址 space) 设备应该接受。对于这些,设备将被硬连线以使用 IO 端口或物理地址(并且无法更改)并且区域的大小也将被硬连线(并且也无法更改)。因为这些东西是hardwired的,你可以把0写到一个BAR上,找出什么是hardwired的(确定区域的大小,以及设备是否需要IO端口space或物理地址space)。

幸运的是固件负责配置 BAR,所以(不包括热插拔设备和 "unusual circumstances")OS 不需要自己设置 BAR,只需读取 value/s 从 BAR(由固件设置)确定设备已配置为使用的地址(在 IO 端口 space 或物理地址 space 中)。遗憾的是,如果 OS 需要确定区域的大小(例如,不能依赖设备驱动程序已经知道其设备的大小),OS 可能需要读取固件的值,然后执行 "write zeros to find out the size of the area",然后恢复固件的值。

当然 PCI 配置 space 不会说明 BAR 的用途 - 由设备驱动程序了解每个区域(由每个 BAR 描述)的用途。

如果 OS 出于任何原因想要(重新)配置 BARs 本身;那么它就复杂得多,因为您还必须确保任何网桥正确转发对设备的访问(如果网桥后面有多个设备,那么这些设备必须使用网桥将转发的单个更大范围内的子范围);并且您必须确保 CPU 的缓存不会破坏所有内容(这主要意味着在所有 CPU 上同时重新配置 MTRRs/Memory 类型范围寄存器同步);并且你必须确保你没有踩到任何东西(例如固件、RAM、CPU 等已经使用的范围);并且必须确保所有内容都在 CPU 可以访问的物理地址 space 的一部分(即使 CPU 有勘误并报告错误的 "number of physical address bits" 通过。 CPUID).