2. GPU内存映射基础:IOMMU/SMMU在SA8155中的作用

在SA8155显示子系统中,GPU与显示控制器通过系统内存管理单元(SMMU)实现虚拟地址到物理地址的转换。SMMU是ARM架构下的IOMMU实现,为GPU等非CPU设备提供地址翻译与内存保护能力。理解这一映射机制是排查显存分配异常、GPU page fault、显示内容错乱等问题的前提。

2.1 为什么GPU需要SMMU?

GPU直接操作物理地址存在三个核心问题:

  • 内存碎片化:物理内存经过长时间分配释放后产生大量不连续区域,GPU需要连续大块显存时难以满足。
  • 安全性:GPU若直接访问物理地址,可能越界读取其他进程或内核数据。
  • 虚拟化支持:多虚拟机场景下,每个VM的GPU需要独立的地址空间隔离。

SMMU通过引入二级页表(Stage 2 translation)解决了上述问题:GPU看到的是连续的虚拟地址空间,SMMU将其映射到分散的物理页帧上,同时提供访问权限控制。

关键概念: SA8155集成的SMMU属于ARM MMU-500系列,支持多达32个上下文bank,每个bank可独立配置页表基地址和地址空间大小。

2.2 SA8155 SMMU架构概览

下图展示了SA8155中GPU与SMMU的硬件连接关系:

SA8155 GPU-SMMU 地址转换架构 GPU Core Adreno 640 虚拟地址请求 SMMU (MMU-500) TBU + TCU VA → IPA → PA DDR Controller 物理内存 上下文Bank (0..31) SCTLR / TTBR0 / TTBR1 MAIR / ASID / VMID 页表基地址寄存器 TLB (快表) 最近转换缓存 Page Table Walk 映射流程 ① GPU发出虚拟地址VA ② SMMU查询TLB ③ TLB Miss则硬件页表遍历 ④ 返回物理地址PA

2.3 虚拟地址到物理地址的映射过程

GPU内存映射分为四级页表遍历(以4KB页粒度为例),SA8155的SMMU支持多种页大小(4KB/16KB/64KB/2MB/1GB)。映射过程如下表所示:

阶段 输入 页表级别 输出 说明
1 VA[47:39] L0 (PGD) L1页表基地址 9位索引,512项
2 VA[38:30] L1 (PUD) L2页表基地址 9位索引,512项
3 VA[29:21] L2 (PMD) L3页表基地址 9位索引,512项
4 VA[20:12] L3 (PTE) 物理页帧号PFN 9位索引,512项
5 VA[11:0] 页内偏移 最终物理地址 与PFN组合
调试提示: 在SA8155上通过 cat /sys/kernel/debug/ion/mm/page_tables 可以查看当前GPU进程的页表映射情况。若出现"Unmapped"或"Invalid"条目,通常对应GPU page fault。

2.4 SMMU关键寄存器与配置

SA8155 SMMU的寄存器基地址位于 0x15000000(GPU SMMU实例)。调试中需要重点关注的寄存器组:

  • SMMU_SCTLR(Stream Configuration Table Location Register):配置页表基地址和格式。
  • SMMU_TTBR0/TTBR1(Translation Table Base Register 0/1):存放L0页表基地址,支持两个地址空间。
  • SMMU_MAIR(Memory Attribute Indirection Register):定义内存属性(缓存策略、共享属性)。
  • SMMU_FSR(Fault Status Register):记录最近一次地址转换错误类型。
  • SMMU_FAR(Fault Address Register):触发错误的虚拟地址。
// 示例:读取SMMU故障寄存器(伪代码)
uint32_t fsr = readl(SMMU_BASE + 0x58);  // SMMU_FSR offset
if (fsr & (1 << 0)) {  // F_ASYNC bit
    uint64_t far = readq(SMMU_BASE + 0x60); // SMMU_FAR
    printf("SMMU Fault at VA: 0x%llx, FSR: 0x%x\n", far, fsr);
}

2.5 GPU显存分配与映射流程

在SA8155的显示栈中,GPU显存分配通常经过以下步骤:

  1. ION分配器:从系统内存中分配连续的物理内存块(通过DMA-BUF)。
  2. DMA-BUF导出:将物理内存封装为DMA-BUF对象,附带SG表(Scatter-Gather list)。
  3. SMMU映射:GPU驱动调用 iommu_map_sg() 将SG表中的物理地址映射到GPU虚拟地址空间。
  4. 页表写入:SMMU驱动遍历页表,将虚拟地址与物理页帧写入L0~L3页表项。
  5. TLB刷新:映射完成后,通过 arm_smmu_tlb_inv_range() 使失效TLB缓存。
常见异常: 若ION分配器返回的物理地址超出SMMU支持的地址范围(SA8155 SMMU支持48位物理地址),或SG表项未按页对齐,会导致映射失败,表现为GPU提交命令时返回 -ENOMEM 或 -EINVAL。

2.6 调试工具与方法

下表总结了SA8155上排查GPU内存映射问题的常用工具:

工具/接口 路径 用途
dmesg 内核日志 查看SMMU page fault、IOMMU错误
trace-cmd /sys/kernel/debug/tracing 跟踪iommu_map/unmap调用
IOMMU debugfs /sys/kernel/debug/iommu 查看每个设备的地址空间映射
GPU devfreq /sys/class/devfreq/... 监控GPU内存带宽使用
kgdb/jtag 硬件调试 直接读取SMMU寄存器、页表内存

以下是一个典型的SMMU page fault调试流程:

# 1. 确认故障类型
dmesg | grep -i "iommu\|smmu\|page fault"

# 2. 查看故障地址和上下文
cat /sys/kernel/debug/iommu/arm-smmu/addresses

# 3. 检查GPU进程的页表
cat /sys/kernel/debug/ion/mm/page_tables | grep <pid>

# 4. 手动触发页表dump(需内核开启DEBUG选项)
echo 1 > /sys/kernel/debug/iommu/arm-smmu/dump_tables

2.7 映射异常案例分析

在SA8155实际项目中,最常见的映射异常是TLB未刷新导致的内存内容错乱。当GPU驱动修改页表后未正确调用TLB invalidation,SMMU仍使用缓存的旧映射,导致GPU读到错误的数据。典型现象:

  • 显示画面出现撕裂或局部花屏。
  • GPU渲染结果与预期不符,但无page fault报错。
  • 重新分配显存后问题消失,但过一段时间复现。
排查要点: 在修改页表后,检查驱动是否调用了 arm_smmu_tlb_inv_range() 或 iommu_tlb_sync()。SA8155的SMMU要求TLB invalidation必须通过写入 SMMU_TLBIVAX 寄存器完成,且需要等待 SMMU_sTLBSYNC 寄存器确认完成。

下图展示了正常映射与TLB未刷新异常的对比:

正常映射 vs TLB未刷新异常 正常流程 ① GPU驱动更新页表项 ② 调用 arm_smmu_tlb_inv_range() ③ 写入 SMMU_TLBIVAX 寄存器 ④ 等待 SMMU_sTLBSYNC 完成 异常流程(TLB未刷新) ① GPU驱动更新页表项 ② 未调用TLB invalidation ③ SMMU仍使用旧TLB缓存 ④ GPU读到错误物理地址 → 花屏 结果:显示正常 GPU访问最新物理内存 结果:花屏/内容错乱 GPU访问过期物理内存

2.8 本章小结

掌握SMMU地址映射原理是SA8155显示调试的基石。核心要点:

  • SMMU为GPU提供虚拟地址到物理地址的转换,解决碎片化、安全性和虚拟化问题。
  • 映射过程涉及四级页表遍历,任何一级页表项错误都会导致page fault。
  • TLB缓存一致性是调试中的高频问题,必须确保页表修改后执行正确的invalidation序列。
  • 利用debugfs、dmesg和trace-cmd可以快速定位映射异常。