2. PCIe配置空间
PCIe配置空间是软件与硬件交互的“控制面板”。所有设备的功能发现、资源分配、中断配置、电源管理以及错误报告,都通过读写配置空间完成。本章深入解析配置空间的物理结构、两种Header类型、Capability链表机制以及ECAM访问方法。
2.1 配置空间结构概述
PCIe兼容传统PCI配置空间规范,并将大小从256字节扩展至4KB。前256字节(Offset 0x00~0xFF)称为PCI兼容配置空间,后3840字节(Offset 0x100~0xFFF)称为PCIe扩展配置空间。
| 区域 | 偏移范围 | 大小 | 内容 |
|---|---|---|---|
| PCI兼容区 | 0x00 ~ 0x3F | 64字节 | Type0/Type1 Header(固定字段) |
| PCI兼容区 | 0x40 ~ 0xFF | 192字节 | Capability链表、设备特定寄存器 |
| PCIe扩展区 | 0x100 ~ 0xFFF | 3840字节 | PCIe扩展Capability(AER、VC、SR-IOV等) |
2.2 Type0 Header与Type1 Header
PCIe设备分为两类:Endpoint(端点)和Switch/Bridge(交换器/桥)。Endpoint使用Type0 Header,Switch/Bridge使用Type1 Header。两种Header的前16字节(Vendor ID、Device ID、Command、Status等)完全相同,从第16字节开始出现差异。
2.2.1 Type0 Header 关键字段
- Vendor ID (0x00): 厂商识别码,由PCI-SIG分配。0xFFFF表示无效设备。
- Device ID (0x02): 设备型号识别码。
- Command (0x04): 控制设备行为,如IO空间使能、内存空间使能、总线主控使能等。
- Status (0x06): 反映设备状态,如中断状态、Capability链表存在标志。
- Header Type (0x0E): Bit7=0表示Type0,Bit7=1表示Type1;Bit6=0表示单功能设备,Bit6=1表示多功能设备。
- BAR0~BAR5 (0x10~0x24): 基地址寄存器,用于声明设备需要的内存/IO空间大小和类型。
2.2.2 Type1 Header 特有字段
| 偏移 | 字段 | 说明 |
|---|---|---|
| 0x18 | Primary Bus Number | 桥上游(靠近Root Complex侧)的总线号 |
| 0x19 | Secondary Bus Number | 桥下游(远离Root Complex侧)的总线号 |
| 0x1A | Subordinate Bus Number | 桥下游子树中最大的总线号 |
| 0x1C | I/O Base / I/O Limit | 桥下游IO地址范围 |
| 0x20 | Memory Base / Memory Limit | 桥下游32位内存地址范围 |
| 0x24 | Prefetchable Memory Base/Limit | 桥下游预取内存地址范围 |
2.3 Capability链表
PCI/PCIe使用Capability链表机制来扩展配置空间的功能。每个Capability结构包含一个ID字节、一个Next Pointer字节以及若干功能特定寄存器。链表从配置空间Offset 0x34处的Capabilities Pointer字段开始。
2.3.1 常见Capability ID
| ID | 名称 | 说明 |
|---|---|---|
| 0x01 | PM (Power Management) | 电源管理状态控制 |
| 0x05 | MSI (Message Signaled Interrupt) | 传统MSI中断 |
| 0x10 | PCIe Capability | PCIe设备核心能力寄存器 |
| 0x11 | MSI-X | 增强型MSI中断 |
| 0x13 | TPH Requester | TLP处理提示 |
| 0x15 | L1 PM Substates | L1低功耗子状态 |
2.4 PCIe扩展配置空间(ECAM)
传统PCI配置空间只有256字节,无法满足PCIe丰富的功能需求。PCIe将配置空间扩展至4KB,并通过ECAM(Enhanced Configuration Access Mechanism)实现访问。ECAM将整个PCIe配置空间映射到一段连续的内存地址区域。
2.4.1 ECAM地址映射公式
ECAM将Bus:Device:Function三元组线性映射到内存地址。每个设备Function占用4KB空间,每条总线最多32个设备×8个Function = 256个Function,占用1MB空间。
ECAM地址 = BaseAddress + (Bus × 256 × 4KB) + (Device × 8 × 4KB) + (Function × 4KB) + Offset
其中:
BaseAddress = MCFG ACPI表中指定的ECAM基地址
Bus = 8位总线号 (0~255)
Device = 5位设备号 (0~31)
Function = 3位功能号 (0~7)
Offset = 12位配置空间偏移 (0x000~0xFFF)
2.4.2 Linux中ECAM访问示例
Linux内核通过pci_generic_config_read和pci_generic_config_write函数访问ECAM空间。驱动开发者通常使用pci_read_config_* / pci_write_config_*系列API,无需直接操作ECAM地址。
#include <linux/pci.h>
/* 读取配置空间DWORD */
int pci_read_config_dword(struct pci_dev *dev, int offset, u32 *val);
/* 写入配置空间DWORD */
int pci_write_config_dword(struct pci_dev *dev, int offset, u32 val);
/* 示例:读取PCIe Capability结构中的Device Capabilities寄存器 */
u32 devcap;
pci_read_config_dword(dev, 0x44, &devcap); // 0x44 = PCIe Capability + 0x04
2.5 配置空间访问流程总结
- 系统启动:BIOS/UEFI或内核通过ACPI MCFG表获取ECAM基地址。
- 总线枚举:内核遍历Bus 0开始,递归读取Type1 Header的Bus Number字段发现所有桥下游总线。
- 设备发现:对每个Bus:Device:Function读取Vendor ID,非0xFFFF表示设备存在。
- 资源分配:读取BAR寄存器确定设备所需内存/IO空间大小,分配地址后写回BAR。
- Capability遍历:从Cap Pointer开始,沿链表读取每个Capability的ID和Next Pointer。
- 驱动绑定:根据Vendor ID、Device ID、Class Code匹配驱动,驱动通过pci_read/write_config_*访问配置空间。