2. PCIe 配置空间:配置空间结构、Capability 链表、BAR 空间、MSI/MSI-X Capability
PCIe 配置空间是主机软件发现、枚举和配置设备的核心数据结构。与传统的 PCI 不同,PCIe 将配置空间扩展为 4 KB,并引入了 Capability 链表机制来管理各种可选功能。本章将深入剖析配置空间的布局、BAR 地址映射机制以及中断 Capability 的结构,为后续的 DMA 与中断协同设计打下坚实基础。
2.1 配置空间结构概述
每个 PCIe 设备(包括 RC、Switch、Endpoint)在逻辑上拥有独立的 4 KB 配置空间。前 256 字节(Offset 0x00 - 0xFF)与 PCI 兼容,称为 PCI 兼容配置空间;扩展部分(Offset 0x100 - 0xFFF)用于 PCIe 特有的 Capability 和 Extended Capability。
| 偏移范围 | 大小 | 内容 | 说明 |
|---|---|---|---|
| 0x00 - 0x3F | 64 B | Header Type 0 / 1 | 设备标识、命令、状态、Revision ID、Class Code 等 |
| 0x40 - 0xFF | 192 B | Capability 链表 + 基础 Capability | Power Management、MSI、MSI-X、PCI Express Capability 等 |
| 0x100 - 0xFFF | 3.75 KB | PCIe Extended Capability | ARI、SR-IOV、DPA、LTR 等高级功能 |
pci_read_config_*() / pci_write_config_*() 系列函数访问配置空间。对于扩展 Capability,使用 pci_find_ext_capability() 进行遍历。
2.2 Capability 链表机制
PCIe 使用 Capability 链表来组织各种可选功能。每个 Capability 结构以一个 8 位的 Capability ID 开始,紧接着是一个 8 位的 Next Capability Pointer,指向下一个 Capability 的偏移地址。链表以 Next Pointer 为 0x00 结束。
2.3 BAR 空间详解
Base Address Register(BAR)是设备向系统声明其所需 MMIO 或 I/O 地址空间的机制。每个 BAR 占用配置空间中的 4 字节或 8 字节(64 位地址)。Endpoint 设备最多支持 6 个 BAR(Type 0 Header),而 Switch 的 Upstream/Downstream Port 则使用 Type 1 Header,包含 2 个 BAR。
2.3.1 BAR 寄存器格式
| 位域 | 宽度 | 描述 |
|---|---|---|
| Bit 0 | 1 | 空间类型:0 = Memory Space,1 = I/O Space |
| Bit 1-2 | 2 | Memory 类型:00 = 32-bit,10 = 64-bit |
| Bit 3 | 1 | Prefetchable 标志(仅 Memory 有效) |
| Bit 4-31 | 28 | 基地址(低 4 位固定为 0,表示对齐要求) |
2.3.2 BAR 大小探测算法
- 保存原始值: 读取 BAR 寄存器,保存当前值。
- 写入全 1: 向 BAR 写入 0xFFFFFFFF。
- 回读并解析: 读取 BAR 值,清除低 4 位(Memory)或低 2 位(I/O),取反后加 1 即得到 BAR 大小。
- 恢复原始值: 将第一步保存的值写回 BAR。
pci_read_bases() 函数实现了上述算法。驱动开发者通常不需要手动探测 BAR 大小,而是使用 pci_resource_start()、pci_resource_len() 等 API 获取已分配的资源信息。
2.4 MSI/MSI-X Capability
Message Signaled Interrupt(MSI)和其增强版 MSI-X 是 PCIe 设备向 CPU 发送中断的首选机制。它们通过向特定地址写入特定数据来触发中断,避免了传统 INTx 中断线的共享和边沿/电平问题。
2.4.1 MSI Capability 结构
| 偏移 | 寄存器 | 描述 |
|---|---|---|
| 0x00 | Capability ID + Next Pointer | Cap ID = 0x05 |
| 0x02 | Message Control | 使能位、Multiple Message Enable、64-bit 地址支持 |
| 0x04 | Message Address (低 32 位) | 中断写入的目标地址 |
| 0x08 | Message Address (高 32 位,可选) | 64 位地址的高 32 位 |
| 0x0C | Message Data | 写入的数据,用于区分不同中断向量 |
2.4.2 MSI-X Capability 结构
MSI-X 相比 MSI 提供了更大的灵活性:支持最多 2048 个中断向量,且每个向量可以独立配置地址和数据。MSI-X 使用两个独立的表:Message Table 和 Pending Bit Array (PBA),它们通过 BAR 空间映射到主机内存。
2.5 驱动开发中的配置空间操作
在 Linux 内核驱动中,配置空间的读写通常通过以下步骤完成:
- 设备枚举: 内核 PCI 子系统在启动时扫描总线,为每个设备分配
struct pci_dev结构体。 - 资源分配: 系统固件(BIOS/UEFI)或内核为每个 BAR 分配 MMIO/I/O 地址范围。
- 驱动绑定: 驱动在
probe()函数中调用pci_enable_device()使能设备。 - 中断配置: 调用
pci_alloc_irq_vectors()分配 MSI/MSI-X 中断向量。 - BAR 映射: 使用
pci_iomap()将 BAR 映射到内核虚拟地址空间。
// 典型驱动 probe 函数中的配置空间操作示例
static int my_pcie_driver_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret;
int irq_vec;
void __iomem *bar0_base;
// 1. 使能 PCI 设备
ret = pci_enable_device(pdev);
if (ret)
return ret;
// 2. 设置 DMA 掩码(64 位地址)
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
goto err_disable;
}
// 3. 分配 MSI-X 中断向量(请求 4 个向量)
irq_vec = pci_alloc_irq_vectors(pdev, 1, 4, PCI_IRQ_MSIX);
if (irq_vec < 0) {
dev_err(&pdev->dev, "Failed to allocate MSI-X vectors\n");
ret = irq_vec;
goto err_disable;
}
// 4. 映射 BAR0(假设 BAR0 为 4KB MMIO 空间)
bar0_base = pci_iomap(pdev, 0, 0);
if (!bar0_base) {
dev_err(&pdev->dev, "Failed to iomap BAR0\n");
ret = -ENOMEM;
goto err_free_irq;
}
// 5. 注册中断处理函数(每个向量独立注册)
for (int i = 0; i < irq_vec; i++) {
ret = request_irq(pci_irq_vector(pdev, i),
my_interrupt_handler,
0, "my_driver", &pdev->dev);
if (ret)
goto err_free_irqs;
}
pci_set_drvdata(pdev, bar0_base);
return 0;
err_free_irqs:
while (--i >= 0)
free_irq(pci_irq_vector(pdev, i), &pdev->dev);
pci_iounmap(pdev, bar0_base);
err_free_irq:
pci_free_irq_vectors(pdev);
err_disable:
pci_disable_device(pdev);
return ret;
}
- 在调用
pci_enable_device()之前,不要访问 BAR 空间或分配中断。 - MSI-X 的 Message Table 和 PBA 可能位于不同的 BAR 中,驱动必须正确解析 Capability 中的 Table Offset 和 PBA Offset。
- 对于 64 位 BAR,必须连续使用两个 BAR 寄存器(例如 BAR0 和 BAR1),驱动应检查 BAR 的 Memory Type 位。