2. PCIe配置空间:配置空间结构(Type0/Type1 Header)、Capability结构、BAR空间解析
PCIe配置空间是软件与硬件交互的核心接口,驱动加载、设备枚举、资源分配均依赖对配置空间的正确解析。本章深入剖析Type0/Type1 Header布局、Capability链表机制以及BAR空间的编址与解码逻辑。
核心要点:配置空间是PCIe设备的“身份证”与“资源清单”,驱动通过读写配置空间完成设备发现、中断绑定、DMA地址映射等关键操作。
2.1 配置空间整体结构
每个PCIe Function独立拥有4KB配置空间,前256字节(DWORD 0~63)兼容传统PCI规范,后256字节(DWORD 64~255)为PCIe扩展配置空间。前64字节为预定义的Header区,根据设备类型分为Type0(Endpoint)和Type1(Bridge/Root Port)。
2.2 Type0 Header与Type1 Header对比
Type0 Header用于Endpoint设备,Type1 Header用于Bridge设备(包括Root Port、Switch Port)。两者前16字节完全相同,差异主要体现在BAR数量、Subordinate Bus Number字段以及扩展ROM地址。
| DWORD偏移 | 字节偏移 | Type0 Header字段 | Type1 Header字段 |
|---|---|---|---|
| 0 | 0x00~0x03 | Vendor ID (16b) + Device ID (16b) | |
| 1 | 0x04~0x07 | Command (16b) + Status (16b) | |
| 2 | 0x08~0x0B | Revision ID (8b) + Class Code (24b) | |
| 3 | 0x0C~0x0F | Cache Line Size (8b) + Latency Timer (8b) + Header Type (8b) + BIST (8b) | |
| 4~9 | 0x10~0x27 | 6个BAR(每个32位或成对64位) | 2个BAR(BAR0/BAR1) |
| 10~11 | 0x28~0x2F | CardBus CIS Pointer / Reserved | Secondary Latency Timer + Subordinate Bus Number + Secondary Bus Number + Primary Bus Number |
| 12 | 0x30~0x33 | Expansion ROM Base Address | |
| 13 | 0x34~0x37 | Capabilities Pointer (8b) + Reserved | |
| 14 | 0x38~0x3B | Reserved | Bridge Control (16b) + Interrupt Pin (8b) + Interrupt Line (8b) |
| 15 | 0x3C~0x3F | Interrupt Line (8b) + Interrupt Pin (8b) + Min Gnt (8b) + Max Lat (8b) | |
识别技巧:读取Offset 0x0E的Header Type寄存器,bit[7]=0表示Type0,bit[7]=1表示Type1。bit[6:0]为0表示单Function,为1表示Multi-Function设备。
2.3 Capability结构链表
Capability结构是PCI/PCIe设备扩展功能的统一描述机制。每个Capability结构以1字节Capability ID开头,紧接着1字节Next Capability Pointer,形成单向链表。链表头位于Header的Offset 0x34(Capabilities Pointer)。
2.4 BAR空间解析
BAR(Base Address Register)是设备向系统声明所需地址空间的“资源请求单”。每个BAR寄存器为32位宽,支持Memory空间或I/O空间映射。64位地址需要占用连续两个BAR寄存器。
2.4.1 BAR寄存器格式
| Bit位 | Memory BAR | I/O BAR |
|---|---|---|
| bit 0 | 0(Memory空间指示) | 1(I/O空间指示) |
| bit 2:1 | 00=32位地址空间 10=64位地址空间 |
保留 |
| bit 3 | 0=可预取(Prefetchable) 1=不可预取 |
保留 |
| bit 31:4 | 基地址(硬件可编程) | 基地址(硬件可编程) |
2.4.2 BAR空间大小探测方法
驱动通过“写全1再读回”的方式探测BAR空间大小:
- 保存原始值:读取BAR寄存器,保存当前值。
- 写入全1:向BAR写入0xFFFFFFFF。
- 读回掩码:读取BAR值,低有效位为0的位数表示地址空间大小。
- 恢复原始值:写回保存的原始BAR值。
示例:读回值为0xFFFF0000,则低16位为0,表示地址空间大小为2^16 = 64KB。注意bit 0~3为属性位,需先屏蔽。
2.4.3 64位BAR处理流程
当Memory BAR的bit[2:1]=10时,表示该BAR为64位地址,需连续占用两个BAR寄存器(如BAR0+BAR1)。探测时需同时处理高32位:
// 伪代码:64位BAR探测
uint32_t bar_low_orig = pci_read_config_dword(dev, offset);
uint32_t bar_high_orig = pci_read_config_dword(dev, offset + 4);
// 检查是否为64位BAR
if ((bar_low_orig & 0x6) == 0x4) {
// 64位BAR
pci_write_config_dword(dev, offset, 0xFFFFFFFF);
pci_write_config_dword(dev, offset + 4, 0xFFFFFFFF);
uint32_t bar_low_mask = pci_read_config_dword(dev, offset);
uint32_t bar_high_mask = pci_read_config_dword(dev, offset + 4);
// 计算大小:低32位掩码 + 高32位掩码组合
uint64_t size = ~(((uint64_t)bar_high_mask << 32) | (bar_low_mask & 0xFFFFFFF0)) + 1;
// 恢复原始值
pci_write_config_dword(dev, offset, bar_low_orig);
pci_write_config_dword(dev, offset + 4, bar_high_orig);
}
2.5 配置空间访问实战要点
调试陷阱:配置空间访问必须遵循DWORD对齐规则,跨DWORD读取可能导致硬件异常。使用lspci -xxx -s <BDF>可查看完整配置空间原始数据。
- BDF寻址:Bus:Device.Function,每个PCIe域最多256条Bus,每条Bus最多32个Device,每个Device最多8个Function。
- ECAM机制:通过MMIO方式访问扩展配置空间,基地址由MCFG ACPI表提供。
- Capability遍历:从Offset 0x34读取Capabilities Pointer,逐节点遍历直到Next Pointer为0。
- BAR冲突:多个BAR地址范围不可重叠,系统固件或OS负责分配不冲突的基地址。
2.6 常见Capability ID速查
| Capability ID | 名称 | 说明 |
|---|---|---|
| 0x01 | Power Management | 电源状态管理(D0/D1/D2/D3hot/D3cold) |
| 0x05 | MSI | 传统消息信号中断 |
| 0x10 | PCI Express | PCIe设备核心能力(链路状态、设备能力等) |
| 0x11 | MSI-X | 扩展消息信号中断(支持更多向量) |
| 0x13 | ACS | 访问控制服务(用于IO虚拟化隔离) |
| 0x15 | ARI | 替代路由ID(扩展Function号至256) |
调试建议:使用lspci -vvv可查看设备所有Capability的详细内容。重点关注PCIe Capability中的Link Capabilities和Device Capabilities寄存器,它们直接决定链路速率和负载能力。