【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化 (4)

物理时间4ms,每个vCPU运行2ms,如果设置vCPU0在T=0之后的3ms后产生中断,那希望是物理时间的3ms后(也就是vCPU0的虚拟时间2ms)产生中断,还是虚拟时间3ms后产生中断?ARM体系结构支持这两种设置;

运行在vCPU上的软件可以同时访问两种时钟:EL1物理时钟和EL1虚拟时钟;

EL1物理时钟和EL1虚拟时钟:

【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

EL1物理时钟与系统计数器模块直接比较,使用的是wall-clock时间;

EL1虚拟时钟与虚拟计数器比较,而虚拟计数器是在物理计数器上减去一个偏移;

Hypervisor负责为当前调度运行的vCPU指定对应的偏移,这种方式使得虚拟时间只会覆盖vCPU实际运行的那部分时间;

来一张示例图:

【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

6ms的时间段里,每个vCPU运行3ms,Hypervisor可以使用偏移寄存器来将vCPU的时间调整为其实际的运行时间;

2.6 Virtualization Host Extensions(VHE)

先抛出一个问题:通常Host OS的内核都运行在EL1,而控制虚拟化的代码运行在EL2,这就意味着传统的上下文切换,这个显然是比较低效的;

VHE用于支持type-2的Hypervisor,这种扩展可以让内核直接跑在EL2,减少host和guest之间共享的系统寄存器数量,同时也减少虚拟化的overhead;

VHE由系统寄存器HCR_EL2的E2H和TGE两个比特位来控制,如下图:

【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

VHE的引入,需要考虑虚拟地址空间的问题,如下图:

【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

我们在内存子系统分析时提到过虚拟地址空间的问题,分为用户地址空间(EL0)和内核地址空间(EL1),两者的区域不一致,而在EL2只有一个虚拟地址空间区域,这是因为Hypervisor不支持应用程序,因此也就不需要分成内核空间和用户空间了;

EL0/EL1虚拟地址空间也同时支持ASID(Address Space Identifiers),而EL2不支持,原因也是Hypervisor不需要支持应用程序;

从上两点可以看出,为了支持Host OS能运行在EL2,需要添加一个地址空间区域,以及支持ASID,设置HCR_EL2.E2H的寄存器位可以解决这个问题,如下图:

【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

Host OS运行在EL2需要解决的另一个问题就是寄存器访问重定向,在内核中需要访问EL1的寄存器,比如TTBR0_EL1,而当内核运行在EL2时,不需要修改内核代码,可以通过寄存器的设置来控制访问流,如下图:

【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

重定向访问寄存器引入一个新的问题,Hypervisor在某些情况下需要访问真正的EL1寄存器,ARM架构引入了一套新的别名机制,以_EL12/_EL02结尾,如下图,可以在ECH==1的EL2访问TTBR0_EL1:

【原创】Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

Host OS运行在EL2还需要考虑异常处理的问题,前边提到过HCR_EL2.IMO/FMO/AMO的比特位可以用来控制物理异常路由到EL1/EL2。当运行在EL0且TGE==1时,所有物理异常都会被路由到EL2(除了SCR_EL3控制的),这是因为Host Apps运行在EL0,而Host OS运行在EL2。

2.7 总结

本文涉及到内存虚拟化(stage 2转换),I/O虚拟化(包含了SMMU,中断等),中断虚拟化,以及指令trap and emulation等内容;

基本的套路就是请求虚拟化服务时,路由到EL2去处理,如果有硬件支持的则硬件负责处理,否则可以通过软件进行模拟;

尽管本文还没涉及到代码分析,但是已经大概扫了一遍了,大体的轮廓已经了然于胸了,说了可能不信,我现在都有点小兴奋了;

参考

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzfwzz.html