跨平台PCIe BAR空间操作:x86与ARM差异对比 - 第2节

2.1 BAR 的核心作用:CPU 如何“看到”外设

在 PCIe 体系中,Base Address Register (BAR) 是连接 CPU 地址空间与设备内部寄存器/内存的桥梁。每个 PCIe 功能(Function)最多可拥有 6 个 BAR(Type 0)或 2 个 BAR(Type 1)。

  • 地址映射:系统软件(BIOS/UEFI 或 OS)通过配置周期向 BAR 写入全 1,读取返回的位域确定设备需要的地址空间大小和对齐方式,然后分配一个基地址回写 BAR。
  • 透明访问:一旦 BAR 被赋值,CPU 对 BAR 映射地址的 load/store 操作会被 PCIe 控制器转换为 Memory 或 IO 读写 TLP(事务层报文),直达设备。
  • 资源枚举:BAR 是 PCIe 枚举过程中最重要的资源声明机制,决定了设备在系统地址图中的位置。
关键概念:BAR 本身是配置空间(Offset 0x10~0x24)中的一组 32 位或 64 位寄存器,其只读位向软件声明设备需求,可写位由软件填入基地址。
BAR 地址映射流程 CPU 核心 系统地址空间 (DDR + MMIO) PCIe RC 设备 BAR ① CPU 发起 load/store → ② 系统地址空间命中 MMIO 区域 → ③ PCIe RC 转发 TLP → ④ 设备 BAR 命中并响应

2.2 BAR 空间类型:Memory 与 IO

PCIe BAR 支持两种地址空间类型,但现代系统(尤其是 ARM)对 IO 空间的支持差异巨大。

属性 Memory Space IO Space
访问方式 CPU load/store 直接访问 专用 IN/OUT 指令(x86)或内存映射 IO(ARM)
地址宽度 32 位或 64 位 32 位(实际仅低 16 位常用)
性能 高,支持 burst、cache、预取 低,串行化访问,不支持 burst
x86 支持 原生支持,主流 传统支持,Legacy 设备仍用
ARM 支持 原生支持,唯一推荐方式 无原生 IO 指令,需 SoC 特殊桥接
典型用途 设备内存、FIFO、控制寄存器 传统 x86 控制/状态端口
跨平台陷阱:ARM 平台通常不支持 PCIe IO Space。如果设备驱动在 x86 上依赖 IO BAR 访问控制寄存器,移植到 ARM 时必须改用 Memory BAR 或通过 SoC 的 IO 桥接模块模拟。建议新设计统一使用 Memory BAR。
Memory Space vs IO Space 访问路径 x86 平台 Memory BAR IO BAR load/store 直接访问 IN/OUT 指令访问 支持 burst/cache 串行化,不支持 burst ARM 平台 Memory BAR IO BAR (桥接) load/store 直接访问 需 SoC 桥接模拟 推荐方式 不推荐,兼容性差 结论:跨平台驱动应优先使用 Memory BAR,避免 IO BAR 带来的移植问题。

2.3 BAR 空间大小与对齐规则

PCIe 规范要求 BAR 空间的大小必须是 2 的幂次,且基地址必须与大小对齐。这是由 BAR 的硬件解码逻辑决定的——设备只检查地址的高位是否匹配,低位用于内部寻址。

2.3.1 大小编码原理

软件通过“写全 1 再读回”的方式探测 BAR 大小:

  1. 向 BAR 寄存器写入 0xFFFFFFFF(32 位)或 0xFFFFFFFFFFFFFFFF(64 位)。
  2. 读回 BAR 值,忽略低 4 位(Type 标识位),从最低有效位开始找到第一个 1 的位置。
  3. 该位置即为 BAR 空间的大小编码:size = 2^n,其中 n 是第一个 1 的位索引。
示例:若读回值为 0xFFFF0000,忽略低 4 位后为 0xFFFF0000,第一个 1 在 bit 16,则 BAR 大小为 2^16 = 64 KB,对齐要求为 64 KB 边界。
BAR 大小探测:写全 1 读回 BAR 寄存器 (32-bit): 0xFFFF0000 bit 31 bit 4 bit 3 bit 0 第一个 1 在 bit 16 大小 = 2^16 = 64 KB,基地址必须 64 KB 对齐

2.3.2 对齐要求与地址分配

系统软件在分配 BAR 地址时,必须保证:

  • 基地址对齐:基地址的低 n 位必须为 0,其中 n = log2(size)。
  • 不重叠:同一 PCIe 域内所有 BAR 映射的地址范围不能重叠。
  • 64 位 BAR 处理:占用连续两个 BAR 槽位(如 BAR0+BAR1),高 32 位寄存器存放高地址位。
BAR 大小 对齐边界 地址低位要求 典型设备
4 KB 4 KB 低 12 位为 0 小控制寄存器组
64 KB 64 KB 低 16 位为 0 网卡 MMIO
1 MB 1 MB 低 20 位为 0 GPU 显存窗口
4 MB 4 MB 低 22 位为 0 NVMe 控制器
64 MB 64 MB 低 26 位为 0 大容量 FPGA 加速器

2.3.3 x86 与 ARM 的地址分配差异

虽然 BAR 大小探测机制在 PCIe 规范层面完全一致,但 x86 和 ARM 在地址分配策略上存在显著差异:

对比项 x86 ARM
地址空间布局 固定:0~4 GB 低区保留给 Legacy,4 GB 以上为 PCIe MMIO SoC 自定义,PCIe MMIO 基地址由 Bootloader/ATF 配置
IO 空间 独立 64 KB IO 地址空间 无独立 IO 空间,需映射到 Memory 空间
64 位 BAR 支持 原生支持,>4 GB 地址自动使用 取决于 SoC 地址总线宽度,通常支持 40~48 位
枚举顺序 BIOS/UEFI 在 POST 阶段完成 U-Boot/EDK2 或 Linux 内核枚举
热插拔 ACPI 配合 OS 动态分配 需内核 PCIe 子系统支持,部分 SoC 有限制
实践建议:在 ARM 平台上开发 PCIe 设备驱动时,不要假设 BAR 地址在 4 GB 以下。许多 ARM SoC 将 PCIe MMIO 放在 0x4000000000(40 位地址)以上。驱动应始终使用 pci_resource_start() 等 API 获取动态分配的地址,而非硬编码。
x86 与 ARM 地址空间布局对比 x86 典型布局 0~4 GB: Legacy + Low MMIO 4 GB~: High MMIO (64-bit BAR) IO Space: 0x0000~0xFFFF BIOS/UEFI 固定分配 ARM 典型布局 SoC 私有外设 (0x~) PCIe MMIO (0x4_0000_0000~) DDR 内存 (0x8_0000_0000~) Bootloader/ATF 动态配置 ARM 的 PCIe MMIO 基地址不固定,驱动必须通过 API 获取,不能硬编码。

2.4 本章小结

  • BAR 本质:配置空间中的地址声明寄存器,实现 CPU 地址到设备内部空间的映射。
  • Memory vs IO:Memory BAR 是跨平台首选,IO BAR 在 ARM 上缺乏原生支持。
  • 大小与对齐:BAR 大小必须是 2 的幂,基地址必须与大小对齐,探测机制为“写全 1 读回”。
  • 平台差异:x86 地址布局固定,ARM 由 SoC 自定义;驱动应使用标准 API 获取 BAR 地址,避免硬编码。