Page 50 - 《软件学报》2020年第10期
P. 50
3026 Journal of Software 软件学报 Vol.31, No.10, October 2020
间的正确翻译.同时,本步骤使得第⑤步在虚拟机间切换地址空间时,仅在虚拟机用户态执行一条 VMFUNC 指
令就可以达到 CR3 替换的等价效果.
② 虚拟机监视器会在服务端虚拟机的页表中,将客户端虚拟机 LSTAR MSR(model specific register)的
GVA 值映射到服务端虚拟机系统调用入口代码页的 GPA.本步骤的目的是,当执行流处于服务端虚拟机的地址
空间中时,能够在用户态应用程序发起系统调用后正确地执行系统调用.现代操作系统大都使用 SYSCALL 指
令进行系统调用,在执行 SYSCALL 指令时 CPU 会根据 LSTAR MSR 的值跳转到系统调用的入口.默认配置下
在虚拟机中修改 LSTAR MSR 会触发虚拟机下陷由虚拟机监视器完成操作,因此本步骤使用添加映射的方式避
免了后续控制流切换过程中造成的虚拟机下陷问题.在添加了上述映射后,代理执行过程中发起系统调用时可
以凭借客户端 LSTAR MSR 的值访问到服务端系统调用的入口,从而正常地与内核进行交互.
③ 虚拟机监视器中预先存放了一份跳板代码,配对时会将两个虚拟机高地址空间的某个相同的 GVA 映
射到这份跳板代码页.本步骤的目的是保证虚拟机地址空间切换前后 CPU 指令流能够正确过渡.CPU 的程序计
数器(program counter)是根据虚拟地址获取当前指令的,当 VMFUNC 指令执行完后程序计数器的值会增加对
应的长度,下一条指令已经处于服务端虚拟机的地址空间中.本步骤在两个虚拟地址空间中的相同位置提供了
相同的指令,因此在地址空间切换前后 CPU 执行的仍然是连续的正确指令.
然后,客户端虚拟机返回到用户态,调用跳板代码页提供的接口,开始执行控制流切换相关的代码.下列④~
⑥步需要在每次代理执行过程中重复,所以要求每个步骤都是零下陷以提高跨虚拟机通信的性能.
④ 在切换地址空间之前,客户端进程需要临时修改 FS.base 和 GS.base 两个 MSR 的值为服务端虚拟机中
对应的 MSR 的值.本步骤的目的是保证在代理执行过程中,段寄存器的访问机制在服务端虚拟机中可以正常工
作.现代操作系统中有大量数据需要经过段寄存器机制进行访问,它们的基地址存储在对应虚拟机的 VMCS 中
的某些域中,不会随着地址空间的变化而改变,如果不做对应的调整,在代理执行期间就会根据错误的地址访问
到错误的数据.本步骤预先替换好了正确的 MSR 的值,这样一来在地址空间切换前后段寄存器访问机制取得的
地址均为合法地址.虽然本步骤采用了替换 MSR 的值而非添加映射的方法,但是虚拟机监视器默认对于虚拟机
内读写 FS.base 和 GS.base 两个 MSR 的操作是不做拦截的,所以依然不会引发任何的虚拟机下陷.
接下来,客户端虚拟机的执行流真正地切换到了虚拟机地址空间中.
⑤ 继续执行跳板代码页中的 VMFUNC 指令以切换地址空间,从下一条指令开始,CPU 上仍然是客户端虚
拟机的 VMCS,但其中的扩展页表指针已经指向服务端虚拟机的扩展页表.本步骤真正进入了服务端虚拟机的
地址空间中,当前生效的数据和资源如图 4 中红色边框的区域所示.此时,CPU 的程序计数器指向共享的跳板代
码页中的下一条指令,可以正确地继续执行余下的代码.得益于第①步添加的映射,在切换至目标地址空间时,
无需像传统方案那样显式地修改 CR3 寄存器的值,从而规避了虚拟机调用特权指令而造成的下陷开销.
最后,客户端虚拟机进入服务端的应用程序中开始代理执行.
⑥ 获取跳板代码页的代理执行入口地址,跳转到服务端虚拟机中的后端处理程序中,通过原生的驱动程
序与加速器进行交互.本步骤终于将原本运行在客户端虚拟机中的控制流顺利地切换到了服务端虚拟机中开
始了代理执行,得益于上述 5 步的准备工作,后续的系统调用、中断等与加速器交互必需的复杂操作均可以正
常地进行.
当代理执行的任务完成后需要从服务端虚拟机返回到客户端虚拟机中,此时,倒序执行④~⑥步的逆向操
作即可,在此不再赘述.按照本小节的设计,通过初始化阶段的一次性预先配置,后续频繁的跨虚拟机控制流切
换操作并不会触发任何一次虚拟机下陷,为代理执行的快速、高效,提供了保障.
3.3 本设计的技术点
通过总结上一小节中的 6 步操作,我们提出了以下技术点.
(1) 跨虚拟机地址空间的执行流快速切换.
主动式的跨虚拟机通信需要在执行流切换前后满足两点:切换前后地址翻译机制的正确以及切换前后
CPU 指令流的过渡.原本的一条 VMFUNC 指令只负责切换 GPA 到 HPA 的映射,因此需要有另外的措施负责切