在虚拟机场景下,需要解决的问题之一就是设备隔离。在DMA的场景下,设备将数据拷贝到VM 内存时在没有SMMU 的情况下,设备可以将数据拷贝到任意物理地址,这就导致了对其他虚拟机的影响,这就引入了SMMU组件,该组件可以进行IOVA->PA的地址翻译。
这样做的优点:
1、可以分配非连续的物理地址,IOMMU 可以将连续的虚拟地址映射到物理地址
2、支持32位设备可以寻址4GB 以上的物理内存
3、虚拟化场景的地址隔离
4、还可以进行中断重映射
5、IOMMU 还可以支持外围设备内存缺页,使用PCI-SIG PCIe地址转换服务(ATS)PRI扩展的外围设备可以检测并通知内存管理服务。
这样做的缺点:
额外的性能和内存开销,地址翻译和缺页处理会增加额外的性能开销,IO页表分配空间增加内存开销。有些情况下IO页表和CPU页表可以共享,避免页表开销。
SMMU 可能有多个设备会使用,则需要区分不同的设备。解决方案是为每个设备引入一个Stream ID,stream ID(sid) 指向一个STE(stream table entry),STE以数组的形式组织起来,SMMU 记录STE 数组的首地址。
+-------+
strtab_base ----- | STE 0 |
| STE 1 |
StreamID[n:0] -> | STE 2 |
| STE 3 |
+-------+
STE 保存了IOVA->PA地址翻译的整个过程,为了适配虚拟化场景,和EPT一样搞了两级翻译,stage1 IOVA->IPA stage2 IPA->PA。对于非虚拟化场景,只需要走stage1 翻译,那smmu 如何知道需要走两级翻译还是一级翻译呢?
因为多个进程可能同时访问一个设备,则需要区分不同进程。解决方案是引入了CD(context descriptor)上下文描述符,由S1ContextPtr 指向CD 的基地址,CD表也是一个数组,然后通过ssid(sub stream id) 来进行访问,SSID也叫做PASID(process address sub stream id)。pasid
是与进程关联的id,可以区分不同的进程。
Stream Table Entry (STE)
+-----------------------+
| Config | S1ContextPtr | -> CD -> Stage 1 translation tables
+-----------------------+
| VMID | S2TTB | -> Stage 2 translation tables
+-----------------------+
| Other attributes, |
| configuration |
+-----------------------+
找到CD表之后,也就找到stage1 的翻译页表,存在TTB0 和 TTB1 中。这里的TTB0 和 TTB1 是什么?
+-------+
S1ContextPtr ---- | CD 0 |
| CD 1 |
SubStreamID -> | CD 2 |
| CD 3 |
+-------+
Context Desctriptor (CD)
+-----------------------+
| Configuration | TTB0 |
+-----------------------+
| ASID | TTB1 |
+-----------------------+
总结一下上述的地址翻译过程,通过sid找到STE,STE 中保存了翻译页表基地,则进行一级页表翻译VA->IPA,如果STE 中存在二级页表,则进行二级页表翻译,IPA->PA。
未完待续。
参考: