2. PCIe 配置空间:配置空间结构、Capability 链表、Type0 与 Type1 配置头
PCIe 配置空间是软件发现、枚举和配置设备的基础。与传统的 PCI 总线不同,PCIe 将配置空间扩展至 4 KB,并引入了基于 Capability 链表的扩展机制。本章将深入剖析配置空间的物理结构、Type0 与 Type1 配置头的差异,以及 Capability 链表的遍历方法。
2.1 配置空间结构概述
每个 PCIe 功能(Function)都拥有独立的 4 KB 配置空间。前 256 字节(Offset 0x00 – 0xFF)与传统的 PCI 配置空间完全兼容,称为 PCI 兼容配置空间。剩余的 3840 字节(Offset 0x100 – 0xFFF)是 PCIe 扩展配置空间,用于存放 PCIe 扩展 Capability 结构。
2.1.1 配置空间布局
| 偏移范围 (Offset) | 大小 | 描述 |
|---|---|---|
| 0x00 – 0x3F | 64 字节 | 配置头(Type0 或 Type1) |
| 0x40 – 0xFF | 192 字节 | PCI 兼容 Capability 结构 |
| 0x100 – 0xFFF | 3840 字节 | PCIe 扩展 Capability 结构 |
2.2 Type0 与 Type1 配置头
配置头位于配置空间的前 64 字节。根据设备类型,配置头分为两种:Type0(用于非桥设备,如 Endpoint)和 Type1(用于桥设备,如 Root Port、Switch Port)。
2.2.1 Type0 配置头结构
Type0 配置头用于标识一个具体的 PCIe 功能(Function)。其关键字段如下:
| 偏移 (Offset) | 寄存器 | 位宽 | 描述 |
|---|---|---|---|
| 0x00 | Vendor ID | 16 位 | 厂商识别码(如 0x8086 为 Intel) |
| 0x02 | Device ID | 16 位 | 设备识别码 |
| 0x04 | Command | 16 位 | 控制命令寄存器(如 IO/内存空间使能) |
| 0x06 | Status | 16 位 | 状态寄存器 |
| 0x08 | Revision ID | 8 位 | 修订版本号 |
| 0x09 | Class Code | 24 位 | 设备类别码(Base Class / Sub Class / Prog IF) |
| 0x0C | Cache Line Size | 8 位 | 缓存行大小 |
| 0x0D | Latency Timer | 8 位 | 延迟定时器 |
| 0x0E | Header Type | 8 位 | 头类型(0x00 为 Type0,0x01 为 Type1) |
| 0x0F | BIST | 8 位 | 内建自测试 |
| 0x10 – 0x24 | Base Address Registers (BARs) | 6 × 32 位 | 基地址寄存器,用于设备内存/IO 空间映射 |
| 0x28 | Cardbus CIS Pointer | 32 位 | Cardbus CIS 指针 |
| 0x2C | Subsystem Vendor ID | 16 位 | 子系统厂商 ID |
| 0x2E | Subsystem ID | 16 位 | 子系统 ID |
| 0x30 | Expansion ROM Base Address | 32 位 | 扩展 ROM 基地址 |
| 0x34 | Capabilities Pointer | 8 位 | Capability 链表指针(指向第一个 Capability 结构) |
| 0x3C | Interrupt Line | 8 位 | 中断线 |
| 0x3D | Interrupt Pin | 8 位 | 中断引脚 |
| 0x3E | Min_Gnt | 8 位 | 最小 Grant |
| 0x3F | Max_Lat | 8 位 | 最大延迟 |
2.2.2 Type1 配置头结构
Type1 配置头用于 PCIe 桥设备。与 Type0 相比,Type1 头包含了用于桥接和总线管理的字段,如主总线号、次总线号、 subordinate 总线号等。
| 偏移 (Offset) | 寄存器 | 位宽 | 描述 |
|---|---|---|---|
| 0x00 – 0x0F | Vendor ID, Device ID, Command, Status, Revision ID, Class Code, Header Type, BIST | 同 Type0 | 与 Type0 相同 |
| 0x10 – 0x14 | BAR0, BAR1 | 2 × 32 位 | 桥设备自身使用的基地址寄存器 |
| 0x18 | Primary Bus Number | 8 位 | 主总线号(桥上游侧) |
| 0x19 | Secondary Bus Number | 8 位 | 次总线号(桥下游侧) |
| 0x1A | Subordinate Bus Number | 8 位 | 下属总线号(桥下游最大总线号) |
| 0x1B | Secondary Latency Timer | 8 位 | 次总线延迟定时器 |
| 0x1C | IO Base / IO Limit | 16 位 | IO 地址范围 |
| 0x20 | Memory Base / Memory Limit | 32 位 | 32 位内存地址范围 |
| 0x24 | Prefetchable Memory Base / Limit | 32 位 | 预取内存地址范围 |
| 0x28 | Prefetchable Base Upper 32 Bits | 32 位 | 64 位预取内存基地址高 32 位 |
| 0x2C | Prefetchable Limit Upper 32 Bits | 32 位 | 64 位预取内存限制高 32 位 |
| 0x30 | IO Base Upper 16 Bits / IO Limit Upper 16 Bits | 32 位 | IO 地址范围高 16 位 |
| 0x34 | Capabilities Pointer | 8 位 | Capability 链表指针 |
| 0x38 | Expansion ROM Base Address | 32 位 | 扩展 ROM 基地址 |
| 0x3C | Interrupt Line / Interrupt Pin / Min_Gnt / Max_Lat | 同 Type0 | 与 Type0 相同 |
2.2.3 Type0 与 Type1 配置头对比
| 特性 | Type0 | Type1 |
|---|---|---|
| 适用设备 | Endpoint、Legacy Endpoint、PCIe-PCI 桥 | Root Port、Switch Upstream Port、Switch Downstream Port |
| BAR 数量 | 6 个(BAR0 – BAR5) | 2 个(BAR0, BAR1) |
| 总线号字段 | 无 | Primary / Secondary / Subordinate Bus Number |
| 地址范围寄存器 | 无 | IO Base/Limit, Memory Base/Limit, Prefetchable Base/Limit |
| Header Type 值 | 0x00 | 0x01 |
2.3 Capability 链表
Capability 链表是 PCI/PCIe 配置空间中的一种扩展机制,用于支持可选或高级功能。每个 Capability 结构以一个 Capability ID 字节开始,后跟一个 Next Capability Pointer 字节,指向下一个 Capability 结构。链表以 Next Capability Pointer 为 0x00 结束。
2.3.1 Capability 链表遍历流程
- 读取配置头偏移 0x34 处的 Capabilities Pointer,获取第一个 Capability 结构的偏移地址。
- 如果 Capabilities Pointer 为 0x00,则链表为空。
- 从当前 Capability 结构偏移读取 Capability ID(1 字节)和 Next Capability Pointer(1 字节)。
- 根据 Capability ID 解析对应的 Capability 结构。
- 将 Next Capability Pointer 作为下一个 Capability 结构的偏移,重复步骤 3-5,直到 Next Capability Pointer 为 0x00。
2.3.2 常见 Capability ID
| Capability ID | 名称 | 描述 |
|---|---|---|
| 0x01 | PM (Power Management) | 电源管理 |
| 0x05 | MSI (Message Signaled Interrupts) | 消息信号中断 |
| 0x09 | PCI-X | PCI-X 相关 |
| 0x10 | PCIe Capability | PCIe 设备核心 Capability |
| 0x11 | MSI-X | MSI-X 中断 |
| 0x12 | Advanced Error Reporting (AER) | 高级错误报告 |
| 0x13 | Virtual Channel (VC) | 虚拟通道 |
| 0x14 | Device Serial Number | 设备序列号 |
| 0x15 | Power Budgeting | 电源预算 |
2.4 PCIe 扩展 Capability 结构
PCIe 扩展 Capability 结构位于配置空间偏移 0x100 之后。每个扩展 Capability 结构以 PCI Express Extended Capability Header 开始,包含 Capability ID(16 位)、Capability Version(4 位)、Next Capability Offset(12 位)和 Capability Length(4 位)。
2.4.1 常见扩展 Capability ID
| 扩展 Capability ID | 名称 | 描述 |
|---|---|---|
| 0x0001 | Advanced Error Reporting (AER) | 高级错误报告 |
| 0x0002 | Virtual Channel (VC) | 虚拟通道 |
| 0x0003 | Device Serial Number | 设备序列号 |
| 0x0004 | Power Budgeting | 电源预算 |
| 0x0005 | Root Complex Link Declaration | Root Complex 链路声明 |
| 0x0006 | Root Complex Internal Link Control | Root Complex 内部链路控制 |
| 0x0007 | Root Complex Event Collector | Root Complex 事件收集器 |
| 0x0008 | Multi-Function Virtual Channel | 多功能虚拟通道 |
| 0x0009 | Virtual Channel (VC) 2 | 虚拟通道 2 |
| 0x000A | RCRB Header | RCRB 头 |
2.5 配置空间结构 SVG 图
2.6 配置空间访问示例
以下示例展示了如何在 Linux 内核中通过 MMCFG 方式读取 PCIe 设备的 Vendor ID 和 Device ID。
#include <linux/pci.h>
#include <linux/io.h>
/* 假设已获取到 MMCFG 基地址和总线/设备/功能号 */
void __iomem *mmcfg_base;
u8 bus, dev, func;
/* 计算配置空间地址 */
u32 offset = (bus << 20) | (dev << 15) | (func << 12);
void __iomem *cfg_addr = mmcfg_base + offset;
/* 读取 Vendor ID (16位) */
u16 vendor_id = readw(cfg_addr + 0x00);
/* 读取 Device ID (16位) */
u16 device_id = readw(cfg_addr + 0x02);
pr_info("PCIe Device: Vendor=0x%04x, Device=0x%04x\n", vendor_id, device_id);
2.7 调试技巧与常见问题
lspci -vvv 命令可以查看所有 PCIe 设备的完整配置空间信息,包括 Capability 链表和扩展 Capability。使用 setpci 命令可以直接读写配置空间寄存器。
2.8 本章小结
- PCIe 配置空间大小为 4 KB,前 256 字节兼容传统 PCI 配置空间。
- Type0 配置头用于 Endpoint 设备,包含 6 个 BAR;Type1 配置头用于桥设备,包含总线号字段和地址范围寄存器。
- Capability 链表通过 Capabilities Pointer 和 Next Capability Pointer 实现链表遍历,支持 PM、MSI、PCIe Capability 等功能。
- PCIe 扩展 Capability 结构位于偏移 0x100 之后,通过扩展 Capability 头中的 Next Capability Offset 链接。
- 配置空间的正确读取是设备枚举和故障排查的基础,需注意链路状态和配置空间访问机制。