3. PCIe 配置空间与 BAR 空间

PCIe 配置空间是主机软件与端点设备之间最基础的通信接口。通过配置空间,软件可以识别设备类型、分配资源(内存地址、中断)、控制电源状态,并最终使能 DMA 引擎。本章将深入解析 Type 0/Type 1 配置空间结构、BAR 映射机制以及两种 IO 访问方式的差异。

3.1 配置空间结构:Type 0 与 Type 1

PCIe 配置空间大小为 4 KB(传统 PCI 为 256 字节),前 64 字节为配置头,后 4 KB 包含能力链表。根据设备类型,配置头分为两种:Type 0(端点设备)和 Type 1(桥/交换机)。

关键概念: Type 0 设备是 PCIe 树中的叶子节点(如 DMA 控制器、NVMe SSD、网卡);Type 1 设备是内部节点(如 Root Port、Switch),用于转发事务和实现总线层级。

3.1.1 Type 0 配置头结构

偏移 (字节) 寄存器名称 宽度 说明
0x00 Vendor ID / Device ID 16+16 bit 厂商与设备标识
0x04 Command / Status 16+16 bit 控制 IO/MMIO 使能、总线主控、中断等
0x08 Revision ID / Class Code 8+24 bit 设备功能分类(如 0x010802 为 NVMe)
0x0C Cache Line / Latency Timer / Header Type / BIST 8+8+8+8 bit Header Type 0x00 表示 Type 0
0x10–0x24 BAR0–BAR5 6×32 bit 基地址寄存器,共 6 个
0x28 CardBus CIS Pointer 32 bit CardBus 兼容指针(通常为 0)
0x2C Subsystem Vendor ID / Subsystem ID 16+16 bit 子系统标识
0x30 Expansion ROM Base Address 32 bit 可选 ROM 映射
0x34 Capabilities Pointer 8 bit 指向能力链表首项
0x3C Interrupt Line / Interrupt Pin / Min Gnt / Max Lat 8+8+8+8 bit 中断路由与延迟信息

3.1.2 Type 1 配置头差异

Type 1 头用于桥设备,其偏移 0x10–0x24 区域包含 桥专用寄存器:

  • BAR0–BAR1:仅 2 个 BAR,用于桥自身控制寄存器。
  • Primary Bus Number / Secondary Bus Number / Subordinate Bus Number:定义桥下挂的总线范围。
  • Memory Base / Memory Limit:定义桥转发的 32 位内存地址窗口。
  • Prefetchable Memory Base / Prefetchable Memory Limit:定义预取内存窗口。
  • I/O Base / I/O Limit:定义 IO 地址窗口。
工程提示: 在 DMA 驱动开发中,通常只关心 Type 0 设备的 BAR 空间。桥的 BAR 和窗口寄存器用于配置 PCIe 拓扑中的地址路由,驱动开发者一般不需要直接操作。

3.2 BAR 映射机制

BAR(Base Address Register)是设备向主机软件声明资源需求的接口。每个 BAR 可以映射为 Memory Space 或 I/O Space,并指定地址宽度(32 位或 64 位)、预取属性。

3.2.1 BAR 编码规则

Bit 范围 含义 说明
Bit 0 空间类型 0 = Memory Space;1 = I/O Space
Bit 2:1 地址宽度(Memory 类型) 00 = 32 位;10 = 64 位;01/11 保留
Bit 3 预取属性(Memory 类型) 1 = 可预取(Prefetchable);0 = 不可预取
Bit 31:4 基地址 对齐到 16 字节边界,实际对齐由 BAR 大小决定

3.2.2 BAR 大小探测流程

  1. 读取原始 BAR 值:保存当前值。
  2. 写入全 1:向 BAR 写入 0xFFFFFFFF。
  3. 回读:读取 BAR 值,低有效位表示所需地址空间大小。
  4. 恢复原始值:写回保存的原始 BAR 值。
  5. 计算大小:对回读值取反并加 1,得到 BAR 所需地址空间大小。
示例: 假设 BAR0 回读值为 0xFFF00000(低 20 位为 0),则 BAR0 大小为 220 = 1 MB。软件随后在系统物理地址空间中分配 1 MB 连续区域,并将基地址写入 BAR0。

3.2.3 典型 DMA 控制器的 BAR 布局

BAR 编号 地址宽度 大小 映射内容
BAR0 64 位 64 KB 控制/状态寄存器、门铃寄存器、中断控制
BAR1 64 位 4 MB DMA 描述符环形缓冲区(Host Memory 映射)
BAR2 32 位 256 B MSI-X 表与 PBA(可选)
常见错误: 64 位 BAR 占用连续两个 BAR 槽位(如 BAR0+BAR1)。如果设备声明 64 位 BAR,驱动必须将 BAR0 和 BAR1 视为一个整体,不能单独配置。错误配置会导致地址空间分裂,设备无法正常工作。

3.3 内存映射 IO 与 IO 映射 IO

PCIe 支持两种 IO 访问方式:MMIO(Memory-Mapped I/O) 和 PIO(Port I/O / I/O-Mapped I/O)。现代 DMA 控制器几乎全部使用 MMIO。

3.3.1 对比分析

特性 MMIO PIO
地址空间 系统物理地址空间(统一编址) 独立 IO 地址空间(0–64K)
访问指令 普通 load/store(MOV, LDR, STR) 专用 IN/OUT 指令(x86)
性能 高,支持 burst、预取、缓存 低,每次访问需 CPU 参与
地址宽度 32/64 位 16 位(x86)
预取支持 可配置(Prefetchable / Non-Prefetchable) 不支持
DMA 适用性 适合描述符、门铃、状态轮询 仅适合少量控制寄存器
现代趋势 主流,ARM/RSIC-V 仅支持 MMIO 逐渐淘汰,x86 保留兼容

3.3.2 MMIO 访问注意事项

  • Ordering & Coherency:MMIO 写操作必须使用 wmb()(写内存屏障)确保在门铃触发前所有描述符已写入 Host Memory。
  • Non-Prefetchable:控制寄存器(如门铃、中断掩码)必须映射为 Non-Prefetchable,防止 CPU 或硬件预取导致副作用。
  • 访问粒度:DMA 控制器的门铃寄存器通常要求 4 字节对齐写,不支持部分写。
// 典型 MMIO 门铃操作(Linux 驱动片段)
void dma_ring_doorbell(struct dma_dev *dev, int channel)
{
    // 确保所有描述符已写入 Host Memory
    wmb();
    // 写门铃寄存器(MMIO 地址)
    iowrite32(channel, dev->bar0 + DOORBELL_OFFSET);
}

3.4 配置空间与 BAR 映射关系图

下图展示了从主机 CPU 视角,通过 PCIe 配置空间读取 BAR,然后建立 MMIO 映射,最终访问 DMA 控制器内部寄存器的完整路径。

PCIe 配置空间与 BAR 映射关系 PCIe 配置空间与 BAR 映射关系 主机 CPU 配置读 PCIe 配置空间 Type 0 Header BAR0–BAR5 读取 BAR 值 BAR 值 基地址 + 属性 建立映射 MMIO 虚拟地址 load/store DMA 控制器 门铃寄存器 控制/状态寄存器 描述符指针 流程说明: 1. CPU 通过配置读事务读取设备 BAR0,获取基地址和地址空间大小。 2. 操作系统在系统物理地址空间中分配连续区域,将基地址写入 BAR0。 3. 驱动通过 ioremap() 将物理地址映射到内核虚拟地址空间(MMIO)。 4. 驱动通过 MMIO 虚拟地址读写门铃、状态寄存器,触发 DMA 操作。

3.5 配置空间能力链表

PCIe 配置空间扩展了能力链表(Capabilities List),用于支持高级功能。DMA 控制器常用的能力包括:

能力 ID 名称 对 DMA 的意义
0x01 Power Management 控制设备电源状态,DMA 引擎休眠/唤醒
0x05 MSI 传统消息信号中断,用于 DMA 完成通知
0x11 MSI-X 多向量中断,每个 DMA 通道独立中断
0x10 PCI Express Capability 设备类型、链路状态、最大载荷大小
0x12 Advanced Error Reporting DMA 传输错误记录与恢复
0x16 Virtual Channel 多虚拟通道,支持 QoS 和隔离
0x1B Address Translation Services IOMMU 地址翻译,用于用户态 DMA
驱动开发要点: 在初始化 DMA 控制器时,驱动必须遍历能力链表找到 MSI-X 能力,配置中断向量;同时读取 PCI Express Capability 中的 Max Payload Size,以优化 DMA 读写事务的载荷大小。

3.6 本章小结

  • PCIe 配置空间分为 Type 0(端点)和 Type 1(桥),DMA 控制器使用 Type 0。
  • BAR 是设备向主机声明资源需求的接口,支持 32/64 位地址、Memory/IO 空间、预取属性。
  • BAR 大小探测通过“写全 1 回读”实现,驱动必须正确处理 64 位 BAR 的连续槽位占用。
  • MMIO 是 DMA 控制器的主流访问方式,性能高、支持 burst;PIO 仅用于兼容场景。
  • 配置空间中的 能力链表 提供 MSI-X、ATS、VC 等关键功能,驱动必须正确解析。