2. PCIe配置空间:配置空间结构(Type0/Type1)、Capability链表、扩展配置空间
PCIe配置空间是软件与硬件交互的核心接口,驱动通过它发现设备功能、分配资源、配置中断与DMA引擎。本章深入解析Type0/Type1配置空间布局、Capability链表遍历方法以及扩展配置空间(Extended Configuration Space)的关键结构。
2.1 配置空间概述
PCIe继承了PCI的配置空间机制,并将其从256字节扩展至4KB。前256字节(Offset 0x00–0xFF)称为PCI兼容配置空间,与PCI标准完全兼容;Offset 0x100–0xFFF称为扩展配置空间,用于存放PCIe特有的Capability结构(如AER、VC、ACS等)。
配置空间的访问方式分为两种:IO映射方式(通过CF8/CFC端口访问前256字节)和内存映射方式(MMCONFIG,通过内存地址直接访问整个4KB空间)。现代系统普遍使用MMCONFIG方式。
2.2 Type0配置空间(Endpoint)
Type0配置空间用于Endpoint(端点设备),如网卡、NVMe SSD、GPU等。其前64字节为Header区,布局如下:
| Offset | 寄存器名称 | 宽度 | 说明 |
|---|---|---|---|
| 0x00 | Vendor ID | 16-bit | 厂商ID(如0x8086为Intel) |
| 0x02 | Device ID | 16-bit | 设备ID |
| 0x04 | Command | 16-bit | 控制I/O、内存、总线主控等使能 |
| 0x06 | Status | 16-bit | 状态位(中断状态、Capability链表存在等) |
| 0x08 | Revision ID | 8-bit | 修订版本 |
| 0x09 | Class Code | 24-bit | 设备分类(Base/Sub/Prog IF) |
| 0x0C | Cache Line Size | 8-bit | 缓存行大小(单位DWORD) |
| 0x0D | Latency Timer | 8-bit | PCI兼容延迟定时器 |
| 0x0E | Header Type | 8-bit | 0x00为Type0,0x01为Type1 |
| 0x0F | BIST | 8-bit | 内建自测试 |
| 0x10–0x24 | BAR0–BAR5 | 32-bit each | 6个Base Address Register,用于内存/I/O空间映射 |
| 0x28 | CardBus CIS Pointer | 32-bit | CardBus信息结构指针 |
| 0x2C | Subsystem Vendor ID | 16-bit | 子系统厂商ID |
| 0x2E | Subsystem ID | 16-bit | 子系统设备ID |
| 0x30 | Expansion ROM Base Address | 32-bit | 扩展ROM基地址 |
| 0x34 | Capabilities Pointer | 8-bit | 指向第一个Capability结构的偏移(Status寄存器的Capabilities List位必须为1) |
| 0x3C | Interrupt Line | 8-bit | 中断线号(由固件分配) |
| 0x3D | Interrupt Pin | 8-bit | 中断引脚(INTA#/INTB#/INTC#/INTD#) |
| 0x3E | Min_Gnt | 8-bit | PCI兼容:最小Grant时间 |
| 0x3F | Max_Lat | 8-bit | PCI兼容:最大延迟 |
2.3 Type1配置空间(Bridge)
Type1配置空间用于PCIe Bridge(包括Root Port、Switch Upstream/Downstream Port)。其Header布局与Type0不同,主要面向总线管理:
| Offset | 寄存器名称 | 宽度 | 说明 |
|---|---|---|---|
| 0x00–0x0F | Vendor ID / Device ID / Command / Status / Revision / Class Code / Header Type / BIST | 同Type0 | 前16字节与Type0相同 |
| 0x10–0x14 | BAR0–BAR1 | 32-bit each | Bridge只有2个BAR |
| 0x18 | Primary Bus Number | 8-bit | 本Bridge上游总线号 |
| 0x19 | Secondary Bus Number | 8-bit | 本Bridge下游总线号 |
| 0x1A | Subordinate Bus Number | 8-bit | 下游子树最大总线号 |
| 0x1B | Secondary Latency Timer | 8-bit | 下游总线延迟定时器 |
| 0x1C | I/O Base | 8-bit | 下游I/O空间基地址(高4位) |
| 0x1D | I/O Limit | 8-bit | 下游I/O空间上限(高4位) |
| 0x1E | Secondary Status | 16-bit | 下游总线状态 |
| 0x20 | Memory Base | 16-bit | 下游Memory空间基地址(高16位) |
| 0x22 | Memory Limit | 16-bit | 下游Memory空间上限(高16位) |
| 0x24 | Prefetchable Memory Base | 16-bit | 下游Prefetchable Memory基地址 |
| 0x26 | Prefetchable Memory Limit | 16-bit | 下游Prefetchable Memory上限 |
| 0x28 | Prefetchable Base Upper 32 Bits | 32-bit | 64位Prefetchable基地址高32位 |
| 0x2C | Prefetchable Limit Upper 32 Bits | 32-bit | 64位Prefetchable上限高32位 |
| 0x30 | I/O Base Upper 16 Bits | 16-bit | I/O基地址高16位(用于32位I/O空间) |
| 0x32 | I/O Limit Upper 16 Bits | 16-bit | I/O上限高16位 |
| 0x34 | Capabilities Pointer | 8-bit | 同Type0 |
| 0x38 | Expansion ROM Base Address | 32-bit | 扩展ROM基地址 |
| 0x3C | Interrupt Line / Interrupt Pin / Bridge Control | 8/8/16-bit | 中断与桥控制 |
2.4 Capability链表
PCIe通过Capability链表实现功能扩展。每个Capability结构以Capability ID(1字节)和Next Capability Pointer(1字节)开头,形成单向链表。链表起始于配置空间Offset 0x34处的Capabilities Pointer。
常见Capability ID包括:
- 0x01 – PM (Power Management)
- 0x05 – MSI (Message Signaled Interrupts)
- 0x10 – PCIe Capability (PCI Express Capability Structure)
- 0x11 – MSI-X
- 0x13 – AER (Advanced Error Reporting)
- 0x15 – VC (Virtual Channel)
- 0x16 – RCLD (Root Complex Link Declaration)
Capability链表遍历流程:
- 读取Offset 0x34,获取第一个Capability的偏移地址(若Status寄存器的Capabilities List位为0,则链表为空)。
- 从该偏移读取Capability ID(1字节)和Next Pointer(1字节)。
- 根据Capability ID解析对应结构体。
- 若Next Pointer不为0,则跳转到该偏移继续遍历;否则结束。
下面是一个Capability链表遍历的C代码示例:
// 读取配置空间1字节
uint8_t pci_config_read8(uint8_t bus, uint8_t dev, uint8_t func, uint16_t offset);
// 遍历Capability链表
void pcie_enum_capabilities(uint8_t bus, uint8_t dev, uint8_t func) {
uint8_t cap_ptr;
uint8_t cap_id;
uint8_t next_ptr;
int max_caps = 20;
// 1. 读取Capabilities Pointer (Offset 0x34)
cap_ptr = pci_config_read8(bus, dev, func, 0x34);
if (cap_ptr == 0) {
printf("No Capability List.\n");
return;
}
// 2. 遍历链表
while (cap_ptr != 0 && max_caps-- > 0) {
cap_id = pci_config_read8(bus, dev, func, cap_ptr);
next_ptr = pci_config_read8(bus, dev, func, cap_ptr + 1);
printf("Capability: ID=0x%02X, Offset=0x%02X\n", cap_id, cap_ptr);
// 根据cap_id解析具体结构
switch (cap_id) {
case 0x10: // PCIe Capability
// 解析PCIe Capability结构
break;
case 0x05: // MSI
// 解析MSI Capability
break;
case 0x11: // MSI-X
// 解析MSI-X Capability
break;
default:
break;
}
cap_ptr = next_ptr;
}
}
2.5 扩展配置空间(Extended Configuration Space)
PCIe扩展配置空间位于Offset 0x100–0xFFF,共3.75KB。它采用Extended Capability链表机制,每个扩展Capability以Extended Capability ID(16位)和Capability Version(4位)以及Next Capability Offset(12位)开头。
扩展Capability链表起始于Offset 0x100,遍历方式与PCI Capability链表类似,但Next Offset以4字节对齐为单位。
| Extended Capability ID | 名称 | 说明 |
|---|---|---|
| 0x0001 | Advanced Error Reporting (AER) | 高级错误报告,包含Uncorrectable/Correctable Error Status、Mask、Severity等 |
| 0x0002 | Virtual Channel (VC) | 虚拟通道配置,支持TC/VC映射 |
| 0x0003 | Device Serial Number | 64位设备序列号 |
| 0x0004 | Power Budgeting | 电源预算管理 |
| 0x0005 | Root Complex Link Declaration | Root Complex链路声明 |
| 0x0006 | Root Complex Internal Link Control | Root Complex内部链路控制 |
| 0x0007 | Root Complex Event Collector | Root Complex事件收集器 |
| 0x0008 | Multi-Function Virtual Channel | 多功能虚拟通道 |
| 0x0009 | Virtual Channel 2 | 虚拟通道扩展2 |
| 0x000A | RCRB (Root Complex Register Block) | Root Complex寄存器块 |
| 0x000B | Vendor-Specific Extended Capability | 厂商自定义扩展Capability |
| 0x000C | ACS (Access Control Services) | 访问控制服务,用于IOMMU/SR-IOV隔离 |
| 0x000D | Alternate Routing ID Interpretation (ARI) | 替代路由ID解释,支持Function超过8个 |
| 0x0010 | SR-IOV (Single Root I/O Virtualization) | 单根I/O虚拟化,支持VF(Virtual Function) |
| 0x001B | DPA (Dynamic Power Allocation) | 动态电源分配 |
| 0x0026 | L1 PM Substates | L1电源管理子状态 |
| 0x0029 | Physical Layer 16.0 GT/s | PCIe 4.0物理层扩展 |
2.6 配置空间结构总览(SVG)
下图展示了PCIe配置空间的整体布局,包括PCI兼容区、Capability链表、扩展Capability链表以及Type0/Type1 Header的差异:
2.7 配置空间访问与驱动开发要点
在Linux内核中,配置空间访问通过pci_read_config_* / pci_write_config_*系列函数完成。驱动开发时需注意以下要点:
- BAR资源分配: 系统固件(BIOS/UEFI)在启动时枚举总线并分配BAR地址。驱动应使用pci_resource_start()/pci_resource_len()获取已分配的BAR基地址和长度,避免手动操作配置空间。
- Capability解析: 使用pci_find_capability()查找PCI Capability,使用pci_find_ext_capability()查找扩展Capability。内核已封装链表遍历逻辑。
- MMCONFIG访问: 在x86平台,MMCONFIG基地址存储在MCFG ACPI表中。驱动可通过ioremap()映射配置空间,实现直接内存访问。
- 中断配置: MSI/MSI-X Capability是配置中断的首选方式。驱动应优先尝试MSI-X,其次MSI,最后回退到INTx。
- 错误处理: AER Capability提供详细的错误状态寄存器。驱动应注册AER回调(aer_recover_queue)或轮询AER状态,以处理DMA传输中的硬件错误。
2.8 本章小结
PCIe配置空间是驱动与硬件通信的基石。Type0/Type1 Header定义了设备与桥的基本属性;Capability链表提供了标准化的功能扩展机制;扩展配置空间则承载了PCIe特有的高级特性。掌握配置空间的布局与遍历方法,是后续实现BAR映射、DMA传输、中断处理等高级功能的前提。