SMMU 技术文档整理
> 来源整理:基于 Linux 内核 IOMMU 文档与 arm-smmu-v3 驱动代码的技术总结。
一、背景:为什么需要 SMMU
在 SoC 或服务器平台里,外设(网卡、GPU、NPU、NVMe、DMA 控制器等)会直接发起 DMA 访问内存。问题是:
- 如果没有地址隔离,设备可以“看见”它不该访问的物理内存。
- 虚拟化场景下,设备 DMA 地址如何映射到 Guest/Host 的正确内存区域,是一个核心安全与性能问题。
- 多设备并发下,DMA 地址空间管理复杂,驱动很难直接操作物理地址。
SMMU(System Memory Management Unit)本质上就是 ARM 体系下的 IOMMU:
- 给设备 DMA 提供地址翻译(IOVA -> PA)
- 给设备访问提供隔离与权限控制
- 支撑虚拟化(尤其是 Stage-2)和共享虚拟地址(SVA)等高级能力
如果一句话总结: SMMU 把“设备 DMA”纳入了和 CPU MMU 类似的受控内存管理体系。
二、SMMU 的核心概念
1. Stream 与 Stream Table
设备请求进入 SMMU 时,通常带有 Stream ID(SID)。SMMU 根据 SID 在 Stream Table 里找到对应配置(STE, Stream Table Entry),决定该请求:
- 用哪套页表
- 用哪种翻译阶段(Stage-1 / Stage-2 / Bypass)
- 开启哪些权限检查
你可以把它理解为“设备侧的进程上下文入口”。
2. Context Descriptor(CD)
当启用 Stage-1 时,STE 会进一步引用 Context Descriptor:
- 指定 TTBR/TCR 等上下文信息
- 指定 ASID/VMID 等标识
- 指定缓存与一致性相关参数
这让同一设备下不同地址空间(如 SVA/PASID 场景)成为可能。
3. 翻译阶段
- Stage-1:设备 IOVA -> IPA/PA(更偏进程/设备地址空间)
- Stage-2:IPA -> PA(更偏虚拟化 Hypervisor 管控)
- 嵌套翻译:Stage-1 + Stage-2 组合
在云和虚拟化平台里,Stage-2 对隔离边界尤其关键。
4. 队列模型(v3 重点)
SMMUv3 典型是队列驱动模型:
- CMDQ:软件下发配置/失效命令
- EVTQ:硬件上报事件与故障
- PRIQ:页面请求队列(高级场景)
队列化模型有利于高并发吞吐和更清晰的软硬件接口。
三、Linux 内核里的 SMMU(arm-smmu-v3)
Linux 中 ARM SMMUv3 的核心驱动位于:
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.cdrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
驱动工作大致分为:
1. 枚举与探测:从 ACPI/IORT 或 DeviceTree 获取 SMMU 实例与设备关系。 2. 域管理:创建并管理 iommu_domain,配置翻译模式。 3. 映射管理:通过通用 IOMMU API 维护 IOVA 映射。 4. 命令与失效:向 CMDQ 提交命令,执行 TLB/缓存失效。 5. 故障处理:消费 EVTQ/PRIQ,上报并恢复(或隔离)异常。
一个抽象流程可以写成:
设备发起 DMA(IOVA)
-> SMMU 根据 SID 找到 STE/CD
-> 执行页表遍历与权限检查
-> 输出 PA 并访问内存
-> 若缺页/权限错,事件进入 EVTQ/PRIQ
四、和 DMA API / IOMMUFD 的关系
在 Linux 驱动开发里,设备通常不直接“操纵 SMMU 寄存器”,而是走上层抽象:
- DMA API(最常见)
- IOMMU framework(域、映射、附着)
- 新版用户态场景常见 IOMMUFD
因此,SMMU 在很多项目中属于“平台透明但性能/隔离关键”的基础设施。
五、详细机制讲解(工程视角)
1. 为什么要频繁失效(invalidate)
页表更新后,SMMU 侧缓存(TLB/ATC 等)不立即失效会导致设备继续用旧翻译。
后果包括:
- DMA 访问到旧地址
- 权限变更不生效
- 虚拟化场景下出现“偶发脏数据”或越界
所以“映射更新 + 正确失效时序”是稳定性的核心。
2. 故障分类常见点
典型 fault 原因:
- IOVA 未映射(translation fault)
- 权限不匹配(读写执行权限)
- 上下文配置错误(SID/STE/CD 对不齐)
- 失效时序不完整导致旧条目命中
调试时通常结合:
- IOMMU fault 日志
- 设备驱动 DMA 生命周期
- 映射与 unmap 时序
- 事件队列消费路径
3. 性能与隔离的平衡
SMMU 打开后不是“白送性能”:
- 页表深度、页尺寸、TLB 命中率会影响延迟
- 失效频率高会影响吞吐
- 但关闭隔离通常不可接受(安全与稳定风险)
工程上通常目标是: 在满足隔离前提下,把映射策略和失效策略做成可预测、可批量化。
六、典型应用场景
场景 A:虚拟化与多租户
- 给不同 VM/租户设备 DMA 建立隔离边界
- 阻断跨 VM 内存访问
- 支撑 vIOMMU / SVA 等高级能力
场景 B:高性能设备(GPU/NPU/DPU)
- 大量 DMA 访问下,SMMU 决定可见内存范围
- 正确配置可避免“随机内存踩踏”类疑难问题
- 配合页表与批量失效优化可减少性能抖动
场景 C:安全与合规
- 防止设备固件/驱动异常导致越权访问
- 是可信执行环境、车规、云基础设施的重要基础组件
七、落地建议(可直接用)
1. 先画清 DMA 数据流:谁发起 DMA、IOVA 从哪里来、映射谁负责。 2. 明确域模型:哪些设备共享域,哪些必须独立域。 3. 审计失效路径:map/unmap/权限变更后是否都触发了正确 invalidate。 4. 建立故障字典:把常见 fault code 映射到可执行排查步骤。 5. 压测观察:吞吐、尾延迟、fault rate 与失效率关联分析。
八、总结
SMMU 不是“可选优化项”,而是现代 ARM 平台设备内存访问的关键控制面。
它同时承担三件事:
- 地址翻译
- 安全隔离
- 虚拟化支撑
对内核/驱动工程来说,真正的难点不在“知道它是什么”,而在把映射、失效、故障处理三条路径做成稳定且可验证的系统工程。
---
参考资料
- Linux kernel: IOMMU Userspace API
- Linux kernel: Dynamic DMA mapping Guide
- Linux arm-smmu-v3 driver source (codebrowser)
- Linux IOMMUFD docs
https://www.kernel.org/doc/html/next/userspace-api/iommu.html
https://www.kernel.org/doc/html/next/core-api/dma-api-howto.html
https://codebrowser.dev/linux/linux/drivers/iommu/arm/arm-smmu-v3/
https://www.kernel.org/doc/html/latest/userspace-api/iommufd.html
评论