2. PCI设备寻址空间:PCI配置空间、BAR空间、MMIO与IO空间、DMA地址空间概述
PCI/PCIe设备在系统中拥有多个独立的寻址空间,理解这些空间是掌握DMA寻址掩码配置的前提。每个空间服务于不同的总线事务类型,其地址宽度、访问方式和用途均有严格区分。本章将系统梳理PCI设备的四大核心寻址空间:配置空间、BAR空间(MMIO与I/O)、以及DMA地址空间。
2.1 PCI配置空间(Configuration Space)
配置空间是PCI设备最基础的寻址空间,由PCI总线规范定义。主机通过配置读写事务(Type 0 / Type 1)访问该空间,用于设备枚举、资源分配和中断配置。每个PCI功能(Function)拥有独立的配置空间,标准大小为256字节(PCIe扩展为4KB)。
| 偏移地址 | 寄存器名称 | 位宽 | 关键字段说明 |
|---|---|---|---|
| 0x00 | Vendor ID / Device ID | 16+16 bit | 厂商与设备标识,系统枚举依据 |
| 0x04 | Command / Status | 16+16 bit | I/O空间使能、内存空间使能、总线主控使能 |
| 0x08 | Revision ID / Class Code | 8+24 bit | 设备分类(如网络控制器、存储控制器) |
| 0x10 - 0x24 | Base Address Registers (BAR0 - BAR5) | 32 bit each | 定义设备请求的地址空间类型和大小 |
| 0x3C | Interrupt Line / Pin | 8+8 bit | 中断路由信息 |
2.2 BAR空间:基地址寄存器与地址空间请求
每个PCI功能最多拥有6个BAR(Base Address Register),位于配置空间偏移0x10~0x24。BAR用于向系统声明设备需要映射的地址空间类型(MMIO或I/O)和大小。系统固件(BIOS/UEFI)或操作系统在枚举时,通过向BAR写入全1再回读的方式探测设备所需空间大小,然后分配实际的物理地址基址。
2.2.1 BAR空间类型判定
通过读取BAR的Bit 0可以判定空间类型:
- Bit 0 = 0: MMIO空间(Memory-Mapped I/O),映射到系统物理内存地址域。
- Bit 0 = 1: I/O空间(I/O Port),映射到x86 I/O地址空间(仅x86架构支持)。
对于MMIO BAR,Bit 1~2指示地址宽度:
| Bit[2:1] | 地址宽度 | 说明 |
|---|---|---|
| 00 | 32位 | 标准32位MMIO,地址范围0x00000000~0xFFFFFFFF |
| 10 | 64位 | 占用连续两个BAR寄存器,支持64位地址 |
2.3 MMIO空间与I/O空间
2.3.1 MMIO空间(Memory-Mapped I/O)
MMIO是PCI设备最常用的主机访问接口。系统将设备的内部寄存器或内存映射到CPU的物理地址空间,CPU通过普通的load/store指令即可访问设备。MMIO空间具有以下特征:
- 地址域: 位于系统物理地址空间,由系统固件分配。
- 访问方式: CPU直接读写,无需特殊I/O指令。
- 缓存属性: 通常设置为不可缓存(Uncacheable)或写组合(Write-Combining),避免CPU缓存导致的数据一致性问题。
- 大小: 由BAR指定,通常为4KB、1MB等2的幂次大小。
2.3.2 I/O空间(I/O Port)
I/O空间是x86架构特有的独立地址空间,使用IN/OUT指令访问。PCI设备通过I/O BAR请求I/O端口范围。由于I/O空间大小有限(64KB)且非x86架构不支持,现代PCIe设备已逐渐淘汰I/O BAR,仅保留MMIO方式。
| 特性 | MMIO空间 | I/O空间 |
|---|---|---|
| 地址域 | 系统物理地址空间 | 独立I/O地址空间(x86) |
| 访问指令 | MOV/STR等通用指令 | IN/OUT专用指令 |
| 地址宽度 | 32位或64位 | 16位(0x0000~0xFFFF) |
| 缓存策略 | 不可缓存/写组合 | 无缓存 |
| 跨平台兼容性 | ARM、RISC-V等均支持 | 仅x86 |
| 现代PCIe趋势 | 主流方式 | 逐渐废弃 |
2.4 DMA地址空间
DMA地址空间是PCI设备在发起DMA操作时使用的地址域。设备通过总线主控(Bus Master)能力,直接访问系统内存,无需CPU介入。DMA地址空间与CPU物理地址空间的关系是理解pci_dma_set_mask的核心。
2.4.1 DMA地址空间与物理地址空间的关系
在传统PCI系统中,DMA地址空间直接等于系统物理地址空间。设备看到的DMA地址就是CPU物理地址。但在现代系统中,由于IOMMU(如Intel VT-d、AMD-Vi)的存在,DMA地址空间可能经过地址翻译:
- 无IOMMU: DMA地址 = 物理地址,设备直接访问物理内存。
- 有IOMMU: DMA地址 = I/O虚拟地址(IOVA),由IOMMU翻译为物理地址。
2.4.2 DMA地址宽度与寻址掩码
每个PCI设备支持的最大DMA地址宽度由硬件设计决定。例如:
- 32位设备:只能访问0~4GB的物理内存。
- 64位设备:可以访问整个64位地址空间。
- 40位设备:可访问0~1TB地址空间。
DMA寻址掩码(DMA Mask)正是用于告知内核设备支持的最大地址位宽。内核根据掩码分配DMA缓冲区:
// 设置64位DMA掩码,表示设备可访问整个64位地址空间
int ret = pci_dma_set_mask(pdev, DMA_BIT_MASK(64));
if (ret) {
// 回退到32位掩码
ret = pci_dma_set_mask(pdev, DMA_BIT_MASK(32));
}
2.5 四种寻址空间的关系总结
| 寻址空间 | 访问发起方 | 地址域 | 与DMA的关系 |
|---|---|---|---|
| 配置空间 | 主机(CPU) | PCI配置域 | 用于初始化BAR和DMA控制位(Bus Master) |
| BAR空间(MMIO) | 主机(CPU) | 系统物理地址 | 主机访问设备寄存器,不直接参与DMA |
| BAR空间(I/O) | 主机(CPU) | I/O地址域 | 仅x86,逐渐淘汰 |
| DMA地址空间 | 设备(Bus Master) | 物理地址/IOVA | 设备直接读写系统内存,受寻址掩码约束 |
pci_dma_set_mask设置正确的掩码。错误的掩码设置可能导致DMA缓冲区分配失败或数据损坏。