引子:
虚拟化实现中,如果将一个设备透传给GuestOS直接使用,必须配置SMMU中的页表,使得guestOS在使用这个设备的DMA能力时不会访问到不该访问的地方。那谁在什么时候去配置的SMMU呢?本文主要就是分析了QEMU + KVM方面的代码得到的一点启示,和大家一起分享(时间、能力有限,如果讲的不对的对方还请指正)。阅读本文前,需要有基本的arm virtualization概念,知道SMMU,VFIO等基本知识,知道虚拟化中所谓的IPA, HPA等概念
QEMU对于IO 直通的实现:
QEMU+KVM在实现直通时, 配置给guest OS的虚拟硬件环境并没有SMMU,换句话说guest OS根本感知不到SMMU的存在,所以 guest OS根本不需要配置SMMU页表,也就不存在hypervisor截获了。关键问题就来了,guest OS 中使用的IPA是怎么被SMMU转换为HPA的(这里只是说明SMMU对于地址的转换,MMU的转换机制有一些不一样)?
QEMU中SMMU实现IPA到HPA的方法:
假设真实的硬件环境如下图,同时假设QEMU模拟出来的guest 环境是0 -- 2GB memory(GPA, 也叫IPA)。 QEMU在使能这个Guest 模拟环境之前,会调用mmap( )向kernel 锁定(kernel中叫pin)真实的2GB物理内存。简单代码描述是hva = mmap( 2GB ),hva就是QEMU这个user mode APP得到的虚拟地址; mmap( )调用到kernel时真正锁定(pin)的地址假设是【1GB—3GB】,这个地址也叫HPA,也即真实的物理地址。注意这个时候,物理地址只是Pin住,并没有固定分配给了谁来使用。现在QEMU知道了Guest 虚拟地址环境是0 – 2GB(IPA),可以映射的物理地址范围是[1GB –3GB] (HPA)。 QEMU就直接调用了VFIO框架中的配置SMMU的IOCTL接口,将IPA---》HPA这个映射关系直接写入了SMMU stage 1 CD页表描述中,也 就是说QEMU一上来就直接把2GB的地址空间全部映射好了。GUEST OS中的DMA操作所用到的IPA会直接被SMMU转换到HPA。同时HPA都是kernel所管理的,保证了guest OS中使用DMA的安全。
整个流程说明(没有验证过,请保持谨慎阅读):
- QEMU APP为了模拟一个2GB的虚拟硬件环境,使用mmap(2GB) 调用让kernel 锁定了2GB的物理地址空间(HPA),假设kernel返回的地址值是1GB – 3GB
- QEMU 调用VFIO接口配置SMMU 页表的映射 [0 –2GB](IPA) --》 [1GB --- 3GB ](HPA), 再次说明这里的HPA物理地址只是被pin住了,并没有真正被谁所使用
- QEMU + KVM启动了模拟的虚拟机,Guest OS driver尝试去配置设备DMA,比如driver 调用dma_alloc( )来申请Guest OS中的IPA(这种DMA类的API会同时返回虚拟地址VA和它所认为的物理地址GPA)
- 尝试使用这个IPA地址时,会造成MMU page fault(不是SMMU),hypervisor 处理这个page fault,将这个IPA对应的物理地址HPA(之前pin住的)真正的使用起来。
- Guest OS driver启动设备DMA时,由于之前dma_alloc( )使用的IPA已经被SMMU映射到HPA了,IO 直通完成。
以上QEMU只是默认实现了Stage 1的SMMU直通操作,stage 2没有去做。
如果期望实现stage 1 + stage 2,也就是说在guest OS里面也要有机会去配置SMMU,我觉得就需要QEMU去模拟一个SMMU设备,然后guest OS在配置模拟的SMMU时,hypervisor截获这类操作,再去配置SMMU stage1, stage2 页表
参考链接(强烈推荐)