1. SMMU 概述与硬件架构

各位同学,咱们今天聊聊 SMMU。这东西说白了,就是给 IO 设备用的 MMU。你想想看,CPU 有 MMU 管理虚拟内存,那 DMA、GPU、网卡这些家伙访问内存时谁来管?嗯,就是 SMMU。

我刚开始接触 SMMU 时,总觉得它就是个「翻版 MMU」。后来踩了几个坑才明白——它比 CPU MMU 复杂得多,尤其是在多级页表、事务拆分和缺页处理上。

1.1 SMMU 在 SoC 中的位置

先看一张图,你就知道 SMMU 长在 SoC 的哪个位置了。

核心要点:SMMU 位于 IO 总线与内存控制器之间,所有 IO 设备发起的 DMA 访问都必须经过它。
SMMU 在 SoC 中的位置 CPU Cluster (MMU 管理) GPU (DMA 引擎) 网卡 (DMA 引擎) SMMU TLB / TCU 微页表 缺页处理 事务拆分 内存控制器 DDR / HBM 所有 IO 设备 DMA 访问 必须经过 SMMU 翻译

从图上你能看到:CPU、GPU、网卡这些家伙都连到 SMMU,SMMU 再统一连到内存控制器。我做过一个项目,网卡 DMA 直接写内存导致系统崩溃,查了两天才发现是 SMMU 没配好——设备地址没做翻译,直接写到了物理地址的保留区域。

避坑指南:我曾经在调试时发现,某些 SoC 的 SMMU 默认是 bypass 模式。如果你不主动开启翻译,设备就能直接访问物理内存——这等于把整个系统的安全大门敞开了。

1.2 主要功能模块

SMMU 内部有三个核心模块:TLB、TCU 和微页表。咱们一个一个说。

1.2.1 TLB(Translation Lookaside Buffer)

TLB 就是地址翻译的缓存。说白了,它存着最近用过的页表项。设备每次 DMA 访问,SMMU 先查 TLB——命中就直接拿物理地址,不命中才去查页表。

我个人习惯把 TLB 分成两级:

  • L1 TLB:容量小(通常 32~64 项),但访问速度极快(1~2 个时钟周期)
  • L2 TLB:容量大(512~1024 项),速度稍慢(5~10 个时钟周期)

我在项目中遇到过一个问题:GPU 频繁切换上下文,每次切换都刷 TLB,导致性能暴跌。后来我们给每个 context bank 配了独立的 TLB 分区,才把性能拉回来。

1.2.2 TCU(Translation Control Unit)

TCU 是 SMMU 的大脑。它负责:

  • 管理页表基地址(每个设备或每个 context bank 一个)
  • 控制翻译模式(1 级页表、2 级页表还是 3 级页表)
  • 处理缺页异常(page fault)
  • 管理缓存一致性协议

嗯,这里要注意:TCU 的配置寄存器非常多,而且不同厂商的寄存器布局差异很大。我建议你拿到芯片手册后,先把 TCU 的寄存器地图画出来,不然调试时很容易找错位。

1.2.3 微页表(Micro Page Table)

微页表是 SMMU 特有的东西。CPU MMU 的页表是软件维护的,但 SMMU 的微页表是硬件自动维护的——它只缓存最近使用的页表项,而且粒度更细。

特性 CPU MMU 页表 SMMU 微页表
维护方式 软件(OS 内核) 硬件自动
页表粒度 4KB / 2MB / 1GB 4KB / 64KB / 2MB
缓存位置 CPU 内部 SMMU 内部
缺页处理 缺页中断 → 内核处理 缺页异常 → 固件或软件处理
上下文切换 CR3 寄存器切换 Context Bank 切换
经验之谈:微页表的粒度选择很关键。我做过一个存储设备项目,用 4KB 粒度导致 TLB 缺失率高达 30%。换成 64KB 粒度后,缺失率降到 5% 以下。但粒度太大又浪费内存——你得在性能和内存开销之间找平衡。

1.3 与 CPU MMU 的异同

很多同学问我:SMMU 和 CPU MMU 到底有啥区别?我一般用三个维度来对比。

1.3.1 相同点

  • 地址翻译流程一样:虚拟地址 → 页表遍历 → 物理地址
  • 都有 TLB:缓存最近使用的页表项
  • 都支持多级页表:ARM 架构下都支持 3 级或 4 级页表
  • 都支持权限控制:读/写/执行权限检查

1.3.2 不同点

差异其实挺大的,我列个表你就清楚了。

维度 CPU MMU SMMU
服务对象 CPU 核心(指令/数据访问) IO 设备(DMA 访问)
事务类型 单次读写(cacheline 粒度) 突发传输(burst,可达 64 字节甚至更大)
缺页处理 同步缺页(CPU 立即阻塞) 异步缺页(设备可能重试或报错)
上下文数量 通常 1 个(当前进程) 多个(每个设备或每个 context bank)
缓存一致性 CPU 内部维护 需要与 CPU 缓存做一致性协议
页表共享 进程独立页表 多个设备可共享同一页表

1.3.3 关键差异详解

事务类型差异:CPU 访问内存通常是 64 字节的 cacheline,但设备 DMA 经常是几百字节甚至几 MB 的突发传输。这意味着 SMMU 的 TLB 必须能处理大范围连续地址的翻译——否则一次 DMA 就要查几十次 TLB,性能直接崩了。

缺页处理差异:CPU 缺页时,内核可以立即调度其他进程。但设备缺页时,设备可能正在做实时数据流——你不能让它等太久。我遇到过视频编解码设备因为 SMMU 缺页处理太慢,导致画面卡顿。后来我们给这个设备配了预取机制,提前把页表项加载到 TLB 里。

上下文数量差异:CPU 一次只能跑一个进程,但 SMMU 可能要同时服务十几个设备。每个设备都有自己的页表。所以 SMMU 的 Context Bank 数量很关键——少了不够用,多了浪费硬件资源。

一句话总结:SMMU 不是 CPU MMU 的简单复制,它是为 IO 设备量身定做的地址翻译引擎。理解它的架构差异,是后面做 TLB 缺失优化和固件配置的基础。

好了,这一章的内容就到这儿。记住这张架构图,后面咱们讲 TLB 缺失处理和微页表配置时,你会反复用到它。