3、SMMU核心架构:StreamID、SubstreamID、Context Bank、STE详解
好,我们进入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的调试就有方向了。出问题的时候,按这个链路一层层查:
- 查StreamID:设备发出来的ID对不对?有没有被截断或篡改?
- 查STE:STE表在内存里有没有正确配置?Valid位有没有置1?
- 查Context Bank:STE指向的Context Bank地址对不对?页表基地址有没有写错?
- 查页表:页表项有没有建立?权限够不够?
调试技巧:我一般会在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结构、缓存一致性、故障定位,都会轻松很多。