1. PCIe 枚举基础回顾:配置空间、BDF 寻址、Type0/Type1 配置头
PCIe 设备枚举是系统软件(BIOS/UEFI、操作系统内核)发现并初始化所有 PCIe 设备的过程。理解枚举的基础——配置空间、BDF 寻址以及 Type0/Type1 配置头——是深入掌握中断路由与资源分配的前提。本节将系统回顾这些核心概念,并建立后续章节的术语与逻辑基础。
1.1 配置空间:设备能力的“身份证”
每个 PCIe 设备(包括 Root Port、Switch、Endpoint)都必须实现一个容量为 256 字节的配置空间(Configuration Space)。该空间由 PCI 规范定义,操作系统通过读取/写入配置空间来识别设备类型、分配资源、配置中断与电源管理。
配置空间的前 64 字节为 配置头(Configuration Header),其结构根据设备类型分为 Type0(Endpoint 设备)和 Type1(Bridge 设备,包括 Root Port 与 Switch Port)。剩余 192 字节为 Capability 结构链表,用于扩展功能(如 MSI、MSI-X、Power Management、ARI 等)。
1.2 BDF 寻址:设备的三维坐标
在 PCIe 体系中,每个设备通过 Bus:Device.Function (BDF) 三元组唯一标识。BDF 是软件访问配置空间的逻辑地址,也是枚举过程中遍历设备树的“坐标系统”。
| 字段 | 位宽 | 范围 | 说明 |
|---|---|---|---|
| Bus Number | 8 位 | 0–255 | 标识 PCIe 总线编号。Root Complex 下通常为 Bus 0,通过 Bridge 扩展子总线。 |
| Device Number | 5 位 | 0–31 | 标识总线上的设备号。每个总线最多支持 32 个设备。 |
| Function Number | 3 位 | 0–7 | 标识设备内的功能号。支持多功能设备(最多 8 个 Function)。 |
枚举时,软件通过向 CF8h/CFCh I/O 端口(x86 传统机制)或 ECAM(Enhanced Configuration Access Mechanism,MMIO 方式)写入 BDF 地址,读取对应设备的配置空间。ECAM 是现代系统的主流方式,它将配置空间映射到一段连续的物理内存地址,每个设备占用 4KB(4096 字节),支持扩展配置空间(256 字节 → 4KB)。
pci_scan_bus() 函数通过遍历 Bus 0 上的 Device 0–31、Function 0–7,读取 Vendor ID 和 Device ID 来发现设备。若 Vendor ID 为 0xFFFF,表示该位置无设备。
1.3 Type0 配置头:Endpoint 设备的“身份牌”
Type0 配置头用于 Endpoint 设备(如网卡、GPU、NVMe SSD)。其前 64 字节结构如下:
| 偏移 | 字段 | 位宽 | 说明 |
|---|---|---|---|
| 0x00 | Vendor ID | 16 位 | 厂商编号(如 Intel 0x8086,Realtek 0x10EC) |
| 0x02 | Device ID | 16 位 | 设备型号编号 |
| 0x04 | Command | 16 位 | 控制设备响应(I/O、Memory、Bus Master 等) |
| 0x06 | Status | 16 位 | 设备状态(中断状态、Capability 链表等) |
| 0x08 | Revision ID | 8 位 | 设备修订版本 |
| 0x09 | Class Code | 24 位 | 设备分类(Base Class、Sub Class、Interface) |
| 0x0C | Cache Line Size | 8 位 | 缓存行大小(通常由 BIOS 设置) |
| 0x0D | Latency Timer | 8 位 | PCI 总线延迟定时器(PCIe 中通常忽略) |
| 0x0E | Header Type | 8 位 | 0x00 表示 Type0,0x01 表示 Type1 |
| 0x10–0x24 | Base Address Registers (BAR0–BAR5) | 6 × 32 位 | 设备 MMIO/I/O 地址空间请求 |
| 0x2C | Subsystem Vendor ID | 16 位 | 子系统厂商编号 |
| 0x2E | Subsystem ID | 16 位 | 子系统编号 |
| 0x30 | Expansion ROM Base Address | 32 位 | 可选 ROM 基址 |
| 0x3C | Interrupt Line | 8 位 | 中断线号(由 BIOS/OS 分配,与 IRQ 关联) |
| 0x3D | Interrupt Pin | 8 位 | 中断引脚(INTA#=1, INTB#=2, INTC#=3, INTD#=4) |
Interrupt Line 字段由操作系统或 BIOS 写入,表示设备连接到的 IRQ 编号。而 Interrupt Pin 是设备硬件决定的,表示设备使用哪个中断引脚。在 PCIe 中,传统 INTx 中断通过 Interrupt Pin 路由,MSI/MSI-X 则完全绕过该字段。
1.4 Type1 配置头:Bridge 设备的“路由表”
Type1 配置头用于 Bridge 设备(Root Port、Switch Upstream/Downstream Port)。其结构与 Type0 类似,但关键区别在于:
- Header Type 字段值为
0x01。 - BAR 字段减少为 2 个(BAR0、BAR1),其余位置被 Bus Number 寄存器和 Memory/IO 基址/限址寄存器替代。
- Bridge 负责将事务从 Primary Bus 转发到 Secondary Bus,或反向转发。
Type1 配置头中与枚举直接相关的关键字段:
| 偏移 | 字段 | 位宽 | 说明 |
|---|---|---|---|
| 0x18 | Primary Bus Number | 8 位 | Bridge 上游总线号 |
| 0x19 | Secondary Bus Number | 8 位 | Bridge 下游总线号(枚举时由 OS 分配) |
| 0x1A | Subordinate Bus Number | 8 位 | Bridge 下游子树的最大总线号 |
| 0x1C | I/O Base / I/O Limit | 16 位 | I/O 地址范围(用于转发 I/O 事务) |
| 0x20 | Memory Base / Memory Limit | 32 位 | 32 位 MMIO 地址范围 |
| 0x24 | Prefetchable Memory Base / Limit | 64 位 | 预取 MMIO 地址范围(支持 64 位) |
1.5 PCIe 设备树与枚举流程 SVG
下图展示了典型的 PCIe 拓扑结构及枚举过程中 BDF 的分配关系。Root Complex 下挂载 Root Port(Bus 0),Root Port 通过 Type1 Header 定义 Secondary Bus(Bus 1),Switch 进一步扩展出 Bus 2 和 Bus 3,Endpoint 设备位于叶子节点。
1.6 枚举过程中的关键数据结构
在 Linux 内核中,枚举结果通过 struct pci_dev 和 struct pci_bus 组织。每个设备对应一个 pci_dev 实例,包含 BDF、配置空间副本、资源列表、中断信息等。每个总线对应一个 pci_bus 实例,包含子设备链表、桥接设备指针、总线号范围等。
// Linux 内核中 pci_dev 结构体(精简)
struct pci_dev {
unsigned int devfn; // 编码了 Device 和 Function
unsigned short vendor;
unsigned short device;
unsigned int class; // Class Code
struct pci_bus *bus; // 所属总线
struct resource resource[DEVICE_COUNT_RESOURCE]; // BAR 资源
unsigned int irq; // 中断号(来自 Interrupt Line)
unsigned char pin; // 中断引脚(来自 Interrupt Pin)
unsigned int msi_enabled:1; // MSI 使能标志
unsigned int msix_enabled:1; // MSI-X 使能标志
// ... 更多字段
};
lspci -vvv 可以查看每个设备的 BDF、Vendor ID、Device ID、Class Code、BAR 地址、Interrupt Line 和 Interrupt Pin。使用 cat /sys/bus/pci/devices/0000:02:00.0/config 可以读取原始配置空间(需 root 权限)。
1.7 本节小结
- 配置空间是 PCIe 设备枚举的基石,256 字节标准空间包含设备身份、资源请求、中断信息。
- BDF 寻址(Bus:Device.Function)提供三维坐标,通过 ECAM 或 CF8h/CFCh 访问配置空间。
- Type0 配置头用于 Endpoint 设备,包含 BAR、Interrupt Line/Pin 等关键字段。
- Type1 配置头用于 Bridge 设备,包含 Bus Number 寄存器和地址转发范围,是递归枚举的核心。
- 枚举采用 DFS 深度优先策略,从 Bus 0 开始,通过 Type1 Header 发现下游总线,最终构建完整的设备树。