2. PCI设备寻址空间:PCI配置空间、BAR空间、MMIO与IO空间、DMA地址空间概述

PCI/PCIe设备在系统中拥有多个独立的寻址空间,理解这些空间是掌握DMA寻址掩码配置的前提。每个空间服务于不同的总线事务类型,其地址宽度、访问方式和用途均有严格区分。本章将系统梳理PCI设备的四大核心寻址空间:配置空间、BAR空间(MMIO与I/O)、以及DMA地址空间。

核心概念: PCI设备通过配置空间被系统发现和初始化,通过BAR空间暴露其内部寄存器或内存供主机访问,通过DMA地址空间直接与系统内存交换数据。DMA寻址掩码正是用于限定设备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 中断路由信息
关键点: 配置空间中的Command寄存器(偏移0x04)的Bit 0(I/O Space Enable)和Bit 1(Memory Space Enable)控制着设备是否响应I/O或MMIO访问。Bit 2(Bus Master Enable)则控制设备是否允许发起DMA操作——这是DMA功能的总开关。
PCI配置空间结构(标准256字节) 0x00 - 0x3F:配置空间头部(Header Type 0) 0x10 - 0x24:BAR0 ~ BAR5(6个Base Address Register) 0x40 - 0xFF:设备专用扩展空间(Capabilities List) 0x100 - 0xFFF:PCIe扩展配置空间(仅PCIe设备支持) DMA寻址掩码配置依赖BAR空间和Capabilities

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位地址
常见误区: BAR空间的大小是设备请求的大小,而非实际物理内存大小。例如一个网卡可能请求4KB的MMIO空间用于映射其内部寄存器,但该空间并不对应板载内存。DMA操作的目标是系统内存,而非BAR空间。
BAR空间类型与地址宽度判定流程 读取BAR寄存器 检查 Bit 0 Bit 0 = 0 → MMIO空间 Bit 0 = 1 → I/O空间 检查 Bit[2:1] 判定32/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趋势 主流方式 逐渐废弃
MMIO与I/O空间在系统地址域中的位置 系统物理地址空间(0x00000000 ~ 0xFFFFFFFF / 64位扩展) MMIO区域(BAR映射) DRAM(系统内存) I/O空间(独立地址域,0x0000~0xFFFF,仅x86) 映射

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));
}
核心公式: DMA寻址掩码 = (1 << N) - 1,其中N为设备支持的地址位数。例如DMA_BIT_MASK(32) = 0xFFFFFFFF,DMA_BIT_MASK(64) = 0xFFFFFFFFFFFFFFFF。
DMA地址空间与寻址掩码关系 PCI设备 DMA地址空间 系统物理内存 pci_dma_set_mask(pdev, mask) 限制DMA地址空间最大地址 mask = 0xFFFFFFFF → 32位,0~4GB mask = 0xFFFFFFFFFFFFFFFF → 64位 掩码必须与设备硬件能力匹配,否则DMA操作可能失败

2.5 四种寻址空间的关系总结

寻址空间 访问发起方 地址域 与DMA的关系
配置空间 主机(CPU) PCI配置域 用于初始化BAR和DMA控制位(Bus Master)
BAR空间(MMIO) 主机(CPU) 系统物理地址 主机访问设备寄存器,不直接参与DMA
BAR空间(I/O) 主机(CPU) I/O地址域 仅x86,逐渐淘汰
DMA地址空间 设备(Bus Master) 物理地址/IOVA 设备直接读写系统内存,受寻址掩码约束
实践要点: 在编写设备驱动时,应首先通过配置空间获取设备能力(如通过Capabilities List中的DMA地址宽度信息),然后调用pci_dma_set_mask设置正确的掩码。错误的掩码设置可能导致DMA缓冲区分配失败或数据损坏。