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 等)。

关键点: 配置空间是 PCIe 枚举的“唯一入口”。所有设备发现、资源分配、中断配置都基于对配置空间的读写操作。

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)。

枚举实践: 在 Linux 内核中,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 位)
枚举核心逻辑: 操作系统从 Bus 0 开始,遍历每个 Device 的 Function 0。若读到 Type1 Header,则读取 Secondary Bus Number 和 Subordinate Bus Number,递归扫描下游总线。这个过程称为 DFS(深度优先扫描),最终构建出完整的 PCIe 设备树。

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 设备位于叶子节点。

PCIe 设备树与 BDF 枚举流程 Root Complex Bus 0 Root Port (Type1) Bus 1 Switch Upstream (Type1) Switch Downstream 1 (Type1) Switch Downstream 2 (Type1) Bus 2 Bus 3 Endpoint 1 (Type0) BDF = 02:00.0 Endpoint 2 (Type0) BDF = 03:00.0 枚举顺序(DFS 深度优先) 1. 扫描 Bus 0 → 发现 Root Port (Type1) → 读取 Secondary Bus = 1 2. 扫描 Bus 1 → 发现 Switch Upstream (Type1) → 读取 Secondary Bus = 2, Subordinate = 3 3. 扫描 Bus 2 → 发现 Endpoint 1 (Type0) → 分配 BDF 02:00.0

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 使能标志
    // ... 更多字段
};
调试技巧: 在 Linux 系统中,使用 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 发现下游总线,最终构建完整的设备树。