4. 地址空间与映射模型

在 DMA 驱动开发中,设备发起访问时使用的地址并不等同于 CPU 看到的虚拟地址,也不一定等于最终内存颗粒上的物理地址。IOMMU/SMMU 的核心价值,就是在设备地址与系统物理地址之间建立可控、可隔离、可验证的转换关系。理解 PA、VA、IOVA、IPA 这几个地址空间,以及它们之间的转换链路,是排查 DMA 映射失败、SMMU fault、地址越界和性能异常的基础。

一、四个地址空间的基本含义

先明确四个最容易混淆的地址概念:

  • VA(Virtual Address):CPU 侧虚拟地址,由 MMU 通过页表转换为 PA。驱动代码中 kmalloc()、dma_alloc_coherent() 返回的指针通常是 VA。
  • PA(Physical Address):系统物理地址,是内存控制器和总线最终访问 DRAM 或外设寄存器的地址。
  • IOVA(I/O Virtual Address):设备侧虚拟地址,也叫 DMA 地址。设备发起 DMA 时使用 IOVA,由 IOMMU/SMMU 转换为 PA。
  • IPA(Intermediate Physical Address):中间物理地址,主要出现在虚拟化场景。Guest 看到的“物理地址”实际是 IPA,需要 Stage-2 转换才能得到真正的 PA。
关键结论:CPU 用 VA,设备用 IOVA;IOMMU/SMMU 负责把 IOVA 转成 PA。在虚拟化中,Guest 设备地址先经过 Stage-1 得到 IPA,再经过 Stage-2 得到 PA。

二、地址转换关系总览

下图给出从 CPU 侧和设备侧两条路径出发的地址转换模型。CPU 路径是 VA → PA;设备路径是 IOVA → PA。虚拟化场景下,设备路径可拆成 IOVA → IPA → PA 两级转换。

地址空间与 IOMMU/SMMU 转换模型 CPU 侧路径 VA CPU 虚拟地址 PA 系统物理地址 MMU 设备侧路径(DMA) IOVA 设备 DMA 地址 IPA 中间物理地址 PA 系统物理地址 DRAM 内存颗粒 Stage-1 Stage-2 总线访问 IOMMU/SMMU 位于设备与总线之间:拦截 IOVA,查页表,输出 PA;虚拟化时串联 Stage-1 与 Stage-2 无 IOMMU 时:IOVA 直接等于 PA,设备可访问任意物理内存,隔离性差

三、IOMMU/SMMU 的地址转换流程

以 ARM SMMU 为例,设备发起 DMA 写请求时,典型转换流程如下:

  1. 设备将 IOVA 放到总线请求中,SMMU 根据 Stream ID 或 Requester ID 找到对应的转换上下文。
  2. 若启用 Stage-1,SMMU 用 Stream Table Entry 指向的 CD 表,以 IOVA 为索引查 Stage-1 页表,得到 IPA。
  3. 若启用 Stage-2,继续用 IPA 查 Stage-2 页表,得到最终 PA。
  4. 若页表项无效、权限不足或地址越界,SMMU 产生 fault,记录 FSR、FAR 等寄存器,并可能触发中断。
  5. 转换成功后,PA 被送到内存控制器,完成实际读写。

Linux DMA API 中,驱动通常不直接操作 IOVA,而是通过 DMA 映射接口获得 DMA 地址:

/* 一致性映射:返回 VA,同时得到 DMA 地址 */
void *cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
/* dma_handle 即设备侧 IOVA,设备用该地址发起 DMA */

/* 流式映射:已有缓冲区,建立临时映射 */
dma_addr_t dma_addr = dma_map_single(dev, cpu_addr, size, DMA_TO_DEVICE);
/* 使用完成后必须解除映射 */
dma_unmap_single(dev, dma_addr, size, DMA_TO_DEVICE);

四、典型场景:虚拟化下的两级转换

在 KVM 或 Xen 等虚拟化环境中,Guest 内的驱动看到的是 Guest 物理地址,即 IPA。设备直通给 Guest 后,设备发出的 IOVA 需要先经过 Stage-1 转换为 IPA,再经过 Stage-2 转换为 Host 的 PA。此时常见问题包括:

  • Stage-1 页表由 Guest 内核维护,Stage-2 页表由 Hypervisor 维护,两者粒度或属性不一致会导致 fault。
  • Guest 的 IOVA 范围与 Host 的 IPA 范围未对齐,造成映射空洞。
  • 设备直通时 Stream ID 配置错误,SMMU 找不到正确的 CD 表,直接报 translation fault。
排查虚拟化 DMA 问题时,先确认设备是否真的经过 SMMU,再确认 fault 发生在 Stage-1 还是 Stage-2。查看 SMMU 的 FSR/FAR 寄存器,结合 dmesg 中的 SMMU fault 日志,可以快速定位是哪一级页表缺失。

五、地址空间对照与常见误区

地址类型 使用方 转换机构 典型来源 常见误区
VA CPU MMU kmalloc、mmap 把 VA 直接当 DMA 地址传给设备
IOVA 设备 IOMMU/SMMU dma_alloc_coherent 的 dma_handle 认为 IOVA 一定等于 PA
IPA Guest 设备 Stage-1 Guest 页表 把 IPA 当成 Host 的 PA 使用
PA 内存控制器 无(最终地址) 物理内存布局 忽略保留内存与地址空洞
最常见的错误是:驱动用 virt_to_phys() 把 CPU VA 转成 PA,再把这个 PA 直接写入设备寄存器当作 DMA 地址。在使能 IOMMU/SMMU 的系统中,设备需要的是 IOVA,而不是 PA。正确做法是使用 DMA API 获取 dma_handle,并保证映射方向、长度和生命周期匹配。

六、复盘要点

理解地址空间与映射模型,可以归纳为三句话:CPU 走 VA→PA,设备走 IOVA→PA,虚拟化多一层 IPA。IOMMU/SMMU 是设备侧地址转换和隔离的执行者,Stage-1 管 Guest 内部,Stage-2 管 Guest 到 Host。驱动开发中应始终通过 DMA API 获取设备地址,避免手工转换;遇到 SMMU fault 时,先看 Stream ID 和页表层级,再核对 IOVA 范围与权限属性。掌握这条转换链路,后续配置 SMMU 页表、调试 DMA 映射失败和优化大页映射时,才能有的放矢。