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 结构。

关键概念: 配置空间的访问通过配置读写请求(Configuration Read/Write Request)完成。在 x86 系统中,通常通过 IO 端口 0xCF8/0xCFC(传统机制)或内存映射配置访问(MMCFG,Memory-Mapped Configuration)机制访问。

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 链表遍历流程

  1. 读取配置头偏移 0x34 处的 Capabilities Pointer,获取第一个 Capability 结构的偏移地址。
  2. 如果 Capabilities Pointer 为 0x00,则链表为空。
  3. 从当前 Capability 结构偏移读取 Capability ID(1 字节)和 Next Capability Pointer(1 字节)。
  4. 根据 Capability ID 解析对应的 Capability 结构。
  5. 将 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 图

PCIe 配置空间结构图 PCIe 配置空间结构图 4 KB 配置空间 (Offset 0x000 – 0xFFF) PCI 兼容配置空间 (Offset 0x00 – 0xFF) 配置头 (Offset 0x00 – 0x3F, 64 字节) Type0 (Endpoint) 或 Type1 (Bridge) PCI Capability 链表 (Offset 0x40 – 0xFF) Capability ID + Next Pointer → 链表遍历 PCIe 扩展配置空间 (Offset 0x100 – 0xFFF) AER Capability ID=0x0001 VC Capability ID=0x0002 Serial Number ID=0x0003 扩展 Capability 链表遍历 通过 Next Capability Offset (12位) 链接,直到 Offset 为 0x000

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 命令可以直接读写配置空间寄存器。
常见问题: 在枚举过程中,如果读取 Vendor ID 返回 0xFFFF,通常表示该设备不存在或链路未训练成功。此时应检查链路状态寄存器(Link Status Register)确认链路是否处于 Active 状态。

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 链接。
  • 配置空间的正确读取是设备枚举和故障排查的基础,需注意链路状态和配置空间访问机制。