3. BAR空间基础:BAR的作用、物理意义与地址解码
基地址寄存器(Base Address Register,BAR)是PCIe设备配置空间中最重要的字段之一,它直接决定了主机CPU如何“看见”并访问设备内部的存储或I/O资源。理解BAR的物理意义及其与地址解码的关系,是掌握ioremap映射机制的前提。
3.1 BAR的核心作用
BAR的本质是设备资源在PCIe总线地址空间中的“门牌号”。每个PCIe Function最多可拥有6个BAR(Type 0 Header)或2个BAR(Type 1 Header),用于向系统声明设备需要映射的地址范围及资源类型。
| 作用维度 | 具体说明 |
|---|---|
| 资源声明 | 设备通过BAR向系统固件(BIOS/UEFI)报告所需的地址空间大小和类型 |
| 地址分配 | 系统枚举PCIe总线时,将可用的总线地址段写入BAR,完成物理地址分配 |
| 访问路由 | CPU发出的内存或I/O访问请求,通过BAR中存储的基地址路由到具体设备 |
| 驱动绑定 | 驱动程序读取BAR值,获得设备寄存器的物理基地址,进而调用ioremap建立映射 |
关键认知:BAR本身是一个可读写的配置寄存器,但它的值由系统固件在启动时写入,驱动通常只读取而不修改。驱动开发者关注的是BAR中存储的物理基地址和地址范围长度。
3.2 BAR的物理意义:从配置空间到内存空间
BAR的物理意义可以从三个层次理解:
- 配置空间层:BAR位于PCIe配置空间(Offset 0x10~0x24),每个BAR占4字节(32位)或8字节(64位)。
- 总线地址层:BAR中存储的值是PCIe总线地址空间中的基地址,该地址经过桥接和转换后,最终映射到CPU的物理地址空间。
- 系统物理地址层:对于现代x86/ARM系统,PCIe总线地址与CPU物理地址通常采用直接映射或通过MMU重映射两种方式,驱动最终通过ioremap将这段物理地址映射到内核虚拟地址空间。
物理地址转换链路:
设备内部寄存器 → 设备内部地址 → PCIe总线地址(BAR值) → 主机桥(Host Bridge)地址转换 → CPU物理地址 → ioremap → 内核虚拟地址
3.3 BAR与地址解码的关系
地址解码是PCIe总线体系中的核心机制,它决定了哪个设备响应CPU发出的哪个地址范围的访问请求。BAR在其中扮演了“地址比较器”的角色。
3.3.1 地址解码的基本流程
- CPU发起访问:CPU执行内存访问指令,目标地址为某个物理地址。
- 主机桥转发:主机桥(Host Bridge)判断该地址是否落在分配给PCIe域的总线地址范围内。
- 总线遍历:PCIe总线上的每个设备(或桥)检查访问地址是否落在自己的BAR所定义的地址范围内。
- 设备响应:匹配成功的设备将访问请求路由到其内部寄存器或内存区域。
常见误区:BAR中存储的基地址必须是对齐的。例如,一个需要4KB空间的BAR,其基地址的低12位必须为0。系统固件通过向BAR写入全1再回读的方式,自动检测设备需要的地址空间大小和对齐要求。
3.4 BAR的格式与类型识别
BAR的低4位(bit0~bit3)用于标识资源类型和属性,驱动在读取BAR值时必须解析这些标志位。
| Bit位 | 含义 | 取值说明 |
|---|---|---|
| bit0 | 空间类型 | 0=内存空间,1=I/O空间 |
| bit1 | 保留/类型扩展 | 内存空间时:0=32位,1=64位;I/O空间时:保留 |
| bit2 | 预取属性 | 0=非可预取,1=可预取(仅内存空间有效) |
| bit3~bit31 | 基地址 | 实际基地址的高位,低位置0保证对齐 |
驱动解析BAR的典型代码模式:
/* 读取BAR0原始值 */
u32 bar_val = pci_read_config_dword(dev, PCI_BASE_ADDRESS_0);
/* 判断空间类型 */
if (bar_val & PCI_BASE_ADDRESS_SPACE_IO) {
/* I/O空间:屏蔽低2位 */
phys_addr = bar_val & PCI_BASE_ADDRESS_IO_MASK;
size = ...; /* 通过写入全1回读计算 */
} else {
/* 内存空间:屏蔽低4位 */
phys_addr = bar_val & PCI_BASE_ADDRESS_MEM_MASK;
/* 检查是否为64位BAR */
if (bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* 读取下一个BAR作为高32位 */
u32 bar_high = pci_read_config_dword(dev, PCI_BASE_ADDRESS_1);
phys_addr |= ((u64)bar_high << 32);
}
}
3.5 BAR空间大小检测机制
系统固件和驱动通过写入全1再回读的方法检测BAR所需的地址空间大小,这是PCI/PCIe协议定义的标准机制。
- 写入阶段:向BAR写入0xFFFFFFFF(32位)或0xFFFFFFFFFFFFFFFF(64位)。
- 回读阶段:读取BAR值,设备会将不需要的地址位强制置0,需要的地址位保持为1。
- 大小计算:对回读值取反后加1,得到地址空间大小;或者直接取回读值的最低有效1位的位置。
驱动开发提示:Linux内核提供了
pci_resource_start()、pci_resource_end()和pci_resource_len()等封装函数,驱动开发者无需手动解析BAR格式,直接调用这些API即可获取已分配的资源信息。
3.6 本章小结
BAR空间是PCIe设备与系统软件之间的“资源契约”:设备通过BAR声明需求,系统通过BAR分配地址,驱动通过BAR找到设备。理解BAR的物理意义和地址解码机制,是后续学习ioremap映射、DMA操作和中断处理的基础。