2. 配置空间基础:PCIe配置空间结构(Type0/Type1)、Capability链表机制、配置空间访问方法(I/O与MMCFG)
2.1 PCIe配置空间概述
PCIe配置空间是设备与系统软件(如BIOS、操作系统内核、驱动)进行交互的核心接口。每个PCIe功能(Function)都拥有独立的配置空间,大小为4KB(0x000 – 0xFFF)。系统通过配置空间完成设备发现、资源分配、中断配置、错误报告等关键操作。
配置空间的前256字节(0x00 – 0xFF)与PCI规范完全兼容,称为PCI兼容配置空间;扩展部分(0x100 – 0xFFF)是PCIe新增的PCIe扩展配置空间,用于存放PCIe能力结构(PCIe Capability Structure)和高级功能。
2.2 配置空间头部:Type0与Type1
配置空间头部(Configuration Space Header)位于偏移0x00 – 0x3F,共64字节。根据设备类型,头部分为两种:
- Type0 Header:用于非桥设备(Endpoint、Legacy Endpoint、PCIe到PCI/PCI-X桥等)。
- Type1 Header:用于PCIe桥设备(Root Port、Switch Upstream/Downstream Port、PCIe到PCI桥等)。
两种头部的区别在于基地址寄存器(BAR)的数量和用途,以及是否包含总线号寄存器。
| 偏移 | 寄存器名称 | Type0 | Type1 |
|---|---|---|---|
| 0x00 | Vendor ID / Device ID | 有 | 有 |
| 0x04 | Command / Status | 有 | 有 |
| 0x08 | Revision ID / Class Code | 有 | 有 |
| 0x0C | Cache Line Size / Latency Timer / Header Type / BIST | 有 | 有 |
| 0x10 – 0x24 | Base Address Registers (BAR0 – BAR5) | 6个BAR | 2个BAR |
| 0x28 | CardBus CIS Pointer | 有 | 无 |
| 0x2C | Subsystem Vendor ID / Subsystem ID | 有 | 有 |
| 0x30 | Expansion ROM Base Address | 有 | 有 |
| 0x34 | Capabilities Pointer | 有 | 有 |
| 0x38 | Reserved | – | – |
| 0x3C | Interrupt Line / Interrupt Pin / Min Gnt / Max Lat | 有 | 有 |
| 0x18 – 0x1C | Primary Bus Number / Secondary Bus Number / Subordinate Bus Number | 无 | 有 |
| 0x20 | Memory Base / Memory Limit | 无 | 有 |
| 0x24 | Prefetchable Memory Base / Prefetchable Memory Limit | 无 | 有 |
Header Type 寄存器(偏移0x0E)的bit 7,可以判断设备是Type0(bit7=0)还是Type1(bit7=1)。Type1设备是桥设备,驱动需要处理总线号分配和子设备枚举。
2.3 Capability链表机制
PCI/PCIe引入了Capability链表(Capability List)机制,用于扩展配置空间的功能。每个Capability结构包含一个Capability ID(标识功能类型)和一个Next Capability Pointer(指向下一个Capability结构的偏移)。链表从配置空间偏移0x34处的Capabilities Pointer开始。
常见的Capability ID包括:
- 0x01:Power Management Capability
- 0x05:MSI Capability(Message Signaled Interrupts)
- 0x10:PCI Express Capability(PCIe核心能力)
- 0x11:MSI-X Capability
- 0x13:Advanced Error Reporting Capability
- 0x15:Virtual Channel Capability
- 0x16:Device Serial Number Capability
Next Pointer 是否为0x00(链表结束),并防止出现死循环(如Next Pointer指向自身或形成环)。建议设置最大遍历深度(如20次)。
2.4 配置空间访问方法
系统软件访问PCIe配置空间有两种标准方法:I/O访问(Legacy PCI)和内存映射访问(MMCFG)。现代系统优先使用MMCFG,但驱动开发中仍需了解I/O访问以兼容旧平台。
2.4.1 I/O访问(Legacy PCI Configuration Mechanism)
通过两个I/O端口寄存器实现:
- CONFIG_ADDRESS (0xCF8):32位寄存器,用于指定目标设备的Bus/Device/Function/Register。
- CONFIG_DATA (0xCFC):32位寄存器,用于读写配置数据。
访问流程:
- 构造地址值:
0x80000000 | (bus << 16) | (device << 11) | (function << 8) | register - 将地址值写入
0xCF8。 - 从
0xCFC读取或写入数据。
/* I/O访问配置空间示例(x86) */
static inline uint32_t pci_config_read_long(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) {
uint32_t address = 0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xFC);
outl(address, 0xCF8);
return inl(0xCFC);
}
static inline void pci_config_write_long(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint32_t value) {
uint32_t address = 0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xFC);
outl(address, 0xCF8);
outl(value, 0xCFC);
}
2.4.2 内存映射访问(MMCFG – Memory Mapped Configuration)
MMCFG将PCIe配置空间映射到系统物理内存地址空间,每个功能占用4KB,每个总线占用256个功能 × 4KB = 1MB。系统通过ACPI的MCFG表获取MMCFG基地址。
访问公式:
address = mmcfg_base + (bus << 20) | (device << 15) | (function << 12) | register
驱动通过 ioremap 将物理地址映射到内核虚拟地址空间,然后直接读写。
/* MMCFG访问配置空间示例(内核驱动) */
static void __iomem *mmcfg_virt_base;
void pcie_mmcfg_init(phys_addr_t mmcfg_phys_base) {
mmcfg_virt_base = ioremap(mmcfg_phys_base, 256 * 1024 * 1024); /* 256MB覆盖所有总线 */
}
uint32_t pcie_config_read_long(uint8_t bus, uint8_t dev, uint8_t func, uint16_t reg) {
void __iomem *addr = mmcfg_virt_base + (bus << 20) | (dev << 15) | (func << 12) | (reg & 0xFFC);
return readl(addr);
}
void pcie_config_write_long(uint8_t bus, uint8_t dev, uint8_t func, uint16_t reg, uint32_t value) {
void __iomem *addr = mmcfg_virt_base + (bus << 20) | (dev << 15) | (func << 12) | (reg & 0xFFC);
writel(value, addr);
}
2.5 两种访问方法对比
| 特性 | I/O访问 (0xCF8/0xCFC) | MMCFG访问 |
|---|---|---|
| 访问方式 | I/O端口指令(in/out) | 内存映射读写(load/store) |
| 性能 | 较低(每次访问需两次I/O操作) | 较高(直接内存访问) |
| 地址范围 | 仅支持前256字节(PCI兼容空间) | 支持全部4KB(含PCIe扩展空间) |
| 平台依赖 | x86原生支持,其他架构需模拟 | 所有架构均支持(需MCFG表) |
| 配置空间大小 | 256字节 | 4096字节 |
| 驱动使用场景 | 旧平台兼容、早期枚举 | 现代系统首选、PCIe扩展功能访问 |
pci_read_config_* 和 pci_write_config_* 系列函数封装了底层访问细节,驱动开发者应优先使用这些内核API,而非直接操作I/O端口或MMCFG地址。内核会根据系统配置自动选择最优访问方法。
2.6 配置空间访问的字节序与对齐
PCIe配置空间使用小端字节序(Little-Endian)。驱动在读写多字节寄存器时,必须注意字节序转换。x86平台原生小端,无需额外处理;ARM等大端平台需使用 le32_to_cpu / cpu_to_le32 等宏。
对齐要求:
- 32位寄存器必须4字节对齐(寄存器偏移为0x00、0x04、0x08等)。
- 16位寄存器必须2字节对齐。
- 8位寄存器无对齐要求。
违反对齐要求可能导致总线错误或数据错位。
readw 而非 readl,否则会读取错误数据或触发异常。