3. 地址翻译与页表

好,我们进入SMMU最核心的部分。说实话,很多工程师调了几年SMMU的bug,其实对这几个概念还是模模糊糊的。StreamID是什么?SubstreamID又干嘛用?Context Bank和STE到底怎么映射?今天咱们一次性把它理清楚。

我个人习惯把SMMU的核心架构理解成一个“四层寻址”体系。你想想看,一个DMA发出来的访问请求,最终要找到对应的页表,中间要经过四层索引。哪四层?就是标题里的这四个东西。

核心观点:SMMU的地址翻译过程,本质上是 (StreamID, SubstreamID) → STE → Context Bank → CD → 页表 的逐级查找链。

3.1 StreamID:设备的身

StreamID,说白了就是设备的身份证。每个连接到SMMU的主设备(比如GPU、网卡、存储控制器),都会分配一个唯一的StreamID。

我记得第一次调一个PCIe网卡的SMMU问题,死活翻译不过去。后来发现是StreamID配错了——网卡的Requester ID被当成StreamID直接用了,但没注意PCIe的Bus/Device/Function号到StreamID的映射关系。嗯,这里要注意,StreamID不一定是硬件引脚直接给的,很多时候需要软件配置映射表。

特性 说明
宽度 实现定义,通常8~16位
来源 硬件线号、PCIe RID、平台配置
作用域 全局唯一,标识一个设备或设备组
对应结构 索引STE表,找到该设备的配置

3.2 SubstreamID:设备里的进程

一个设备内部可能有多个独立的地址空间。比如GPU有多个上下文,每个上下文需要独立的页表。这时候光靠StreamID就不够了——它只能定位到设备,定位不到设备里的“进程”。

SubstreamID就是干这个的。它和PCIe的PASID(Process Address Space ID)是对应的。你想想看,一个支持SVA(Shared Virtual Addressing)的系统,用户态程序直接发DMA,SubstreamID就是那个进程的标识。

实战经验:我曾经遇到一个case,SubstreamID的位数配少了,导致高位的PASID被截断。结果两个不同的进程映射到了同一个地址空间,数据全乱套了。查了两天才定位到——嗯,从那以后我每次配SubstreamID宽度都会再三确认。

3.3 STE:Stream Table Entry

STE是SMMU配置的核心数据结构。每个StreamID对应一个STE,STE里存放了该设备的所有翻译配置信息。

STE表存在内存里,SMMU硬件通过StreamID索引找到对应的STE。STE的内容非常丰富,我挑几个关键的说说:

  • Valid位:这个STE是否有效。无效的话,访问直接触发异常。
  • Config域:配置翻译模式——bypass、fault、还是翻译。
  • Context Pointer:指向Context Bank的基地址。
  • SubstreamID配置:是否启用SubstreamID,位数多少。
  • 安全属性:这个设备是安全世界还是非安全世界的。

STE的大小通常是64字节或128字节。为什么这么大?因为要存的东西太多了。我记得有一次看一个芯片的STE定义,光保留位就占了20多个字节——嗯,硬件设计者总是喜欢留一手。

3.4 Context Bank:页表的入口

Context Bank,有时候也叫CD(Context Descriptor)。它才是真正指向页表的东西。

STE里有一个字段指向Context Bank的基地址,然后根据SubstreamID(如果有的话)或者一个固定的索引,找到具体的Context Bank。每个Context Bank里存放了:

  • 页表基地址:一级页表或二级页表的物理地址。
  • ASID:Address Space ID,用于TLB标记。
  • 翻译粒度:4K、16K、64K还是2M/1G的大页。
  • 权限控制:读、写、执行权限。

避坑指南:我曾经调试过一个性能问题,发现TLB miss率特别高。查了半天,发现是Context Bank里配的ASID和操作系统的ASID没同步。每次进程切换,SMMU还在用旧的ASID做TLB查找,命中率能高才怪。记住:ASID必须和软件维护的进程ID保持一致。

3.5 整体查找流程

来,我们画个图把整个流程串起来。下面这张SVG图展示了从设备发起访问到找到页表的完整路径。

SMMU四层寻址流程 SMMU 四层寻址流程 设备发起DMA请求 StreamID (设备身份标识) STE (Stream Table Entry) 配置翻译模式、指向Context Bank Context Bank / CD 页表基地址、ASID、权限 页表 (Page Table) 完成地址翻译 SubstreamID 在 STE 和 Context Bank 之间介入,用于选择具体的 CD

这个流程你记住了,SMMU的调试就有方向了。出问题的时候,按这个链路一层层查:

  1. 查StreamID:设备发出来的ID对不对?有没有被截断或篡改?
  2. 查STE:STE表在内存里有没有正确配置?Valid位有没有置1?
  3. 查Context Bank:STE指向的Context Bank地址对不对?页表基地址有没有写错?
  4. 查页表:页表项有没有建立?权限够不够?

调试技巧:我一般会在STE和Context Bank的地址处加硬件断点。谁改了这些内存,立刻就能抓到。有一次发现是固件在启动阶段覆盖了STE区域,导致SMMU读到全0——嗯,这种问题不抓写者根本查不出来。

3.6 关键配置速查表

最后,我整理了一个速查表。你在调试的时候可以对照着看,哪个字段没配对,一目了然。

配置项 所在结构 常见错误 后果
StreamID宽度 系统集成 配少了,高位被截 设备冲突或找不到STE
STE Valid位 STE 忘记置1 所有访问都fault
Config域 STE 配成bypass 没有翻译,直接透传
Context Pointer STE 地址不对齐 硬件解析出错
ASID Context Bank 和OS不同步 TLB命中率低
页表基地址 Context Bank 物理地址写错 翻译出随机结果

嗯,这一章的内容就到这。StreamID、SubstreamID、STE、Context Bank这四个概念,是SMMU的基石。你把这四层的关系搞清楚了,后面讲TLB结构、缓存一致性、故障定位,都会轻松很多。