登录后台

页面导航

本文编写于 377 天前,最后修改于 377 天前,其中某些信息可能已经过时。

敏感非特权指令的处理

在现代计算机架构中,CPU通常拥有两个或两个以上的特权级,其中操作系统运行在最高特权级,其余程序则运行在较低的特权级。而一些指令必须运行在最高特权级中,若在非最高特权级中执行这些指令将会触发特权级切换,陷入最高特权级中,这类指令称为特权指令。在虚拟化环境中,还有另一类指令称为敏感指令,即操作敏感物理资源的指令,如I/O指令、页表基地址切换指令等。

在虚拟化环境中,Hypervisor应当替代操作系统运行在最高特权级,管理物理资源并向上提供服务,当虚拟机执行敏感指令时必须陷入Hypervisor(通常称为虚拟机下陷)中进行模拟,这种敏感指令的处理方式称为“陷入-模拟”方式。“陷入-模拟”方式要求所有的敏感指令都能触发特权级切换,从而能够陷入Hypervisor中处理,通常将所有敏感指令都是特权指令的架构称为可虚拟化架构,反之存在敏感非特权指令的架构称为不可虚拟化架构。

敏感非特权指令的软件解决方案主要包括解释执行、二进制翻译、扫描与修补以及半虚拟化技术。

(1)解释执行技术。解释执行技术采用软件模拟的方式逐条模拟虚拟机指令的执行。解释器将程序二进制解码后调用指令相应的模拟函数,对寄存器的更改则变为修改保存在内存中的虚拟寄存器的值。

(2)二进制翻译技术。区别于解释执行技术不加区分地翻译所有指令,二进制翻译技术则以基本块为单位,将虚拟机指令批量翻译后保存在代码缓存中,基本块中的敏感指令会被替换为一系列其他指令。

(3)扫描与修补技术。扫描与修补技术是在执行每段代码前对其进行扫描,找到其中的敏感指令,将其替换为特权指令,当CPU执行翻译后的代码时,遇到替换后的特权指令便会陷入Hypervisor中进行模拟,执行对应的补丁代码。

(4)半虚拟化技术。上述三种方式都是通过扫描二进制代码找到其中敏感指令,半虚拟化则允许虚拟机在执行敏感指令时通过超调用主动陷入Hypervisor中,避免了扫描程序二进制代码引入的开销。

6884C2A2-2752-4DA1-8896-02283E01FCE5.jpeg

这几种方案通过软件模拟解决了敏感非特权指令问题,但却产生了巨大的软件开销。敏感非特权指令究其本质是硬件架构缺乏对于敏感指令下陷的支持,近年来各主流架构都从架构层面弥补了“虚拟化漏洞”,解决了敏感非特权指令的陷入-模拟问题

最简单的一种办法是更改现有的硬件架构,将所有的敏感指令都变为特权指令,使之能触发特权级切换,但是这将改变现有指令的语义,现有系统也必须更改来适配上述改动。另一种办法是引入虚拟化模式。未开启虚拟化模式时,操作系统与应用程序运行在原有的特权级,一切行为如常,兼容原有系统;开启虚拟化模式后,Hypervisor运行在最高特权级,虚拟机操作系统与应用程序运行在较低特权级,虚拟机执行敏感指令将会触发特权级切换陷入Hypervisor中进行模拟。

EB7833A7-99BE-4F80-8771-48BA1037340B.jpeg

虚拟化模式的引入解决了敏感非特权指令的陷入-模拟问题,但是由于引入了新的特权级,也带来了上下文切换的问题。

虚拟机上下文切换

在操作系统的进程上下文切换中,操作系统与用户态程序运行在不同的特权级中,当用户态程序发起系统调用时,需要将部分程序状态保存在内存中,待系统调用完成后再从内存中恢复程序状态。而在虚拟化环境下,当虚拟机执行敏感指令时,需要陷入Hypervisor进行处理,Hypervisor与虚拟机同样运行在不同的特权级中,因此硬件应当提供一种机制在发生虚拟机下陷时保存虚拟机的上下文。等到敏感指令模拟完成后,当虚拟机恢复运行时重新加载虚拟机上下文。

4C83F019-C7E2-4519-AB77-7ED3702B5B00.jpeg

当vCPU 1时间片用尽时,Hypervisor将会中断vCPU 1执行,vCPU 1陷入Hypervisor中。在此过程中,硬件将vCPU 1的寄存器状态保存至固定区域,并从中加载Hypervisor的寄存器状态。Hypervisor进行vCPU调度,选择下一个运行的vCPU,保存Hypervisor的寄存器状态,并加载选定的vCPU 2的寄存器状态,而后恢复vCPU 2运行。

ARM v8为EL1和EL2提供了两套系统寄存器,因此单纯发生虚拟机下陷时,无须保存寄存器状态;但是虚拟机下陷后,若要运行其他vCPU,则需要将上一个vCPU状态保存至内存中。

中断虚拟化

在虚拟化环境下,设备与中断控制器均由Hypervisor模拟,相应寄存器的状态对应于内存中某些数据结构的值。当虚拟机执行I/O指令时,会陷入Hypervisor中进行处理,Hypervisor调用相应的设备驱动完成I/O操作。在上述过程中,Hypervisor不仅知道发起I/O操作的虚拟机的详细信息,还能通过设置虚拟设备和虚拟中断控制器的寄存器状态从而完成中断注入。因此一个理想的方案是将该物理中断交给Hypervisor处理,再由Hypervisor设置虚拟中断控制器,注入一个虚拟中断到虚拟机中。

87374E21-1E9A-4C9E-814F-8341A8956FAC.jpeg

仍以读磁盘为例,虚拟机发起一个读磁盘请求触发虚拟机下陷进入Hypervisor进行处理,Hypervisor向物理磁盘发起读磁盘请求,物理磁盘完成数据读请求后产生一个中断并递交给物理中断控制器,Hypervisor执行相应的中断服务例程完成数据读取,并设置虚拟磁盘和虚拟中断控制器相应寄存器的状态,而后Hypervisor恢复虚拟机运行,虚拟机发现有待处理的中断,调用相应的中断服务例程。上述流程对硬件有如下要求:①物理中断将会触发虚拟机下陷进入Hypervisor中处理;②虚拟机恢复运行时要先检查虚拟中断控制器是否有待处理中断。

通过虚拟机下陷将物理中断转换为虚拟中断解决了多虚拟机系统中物理中断的路由问题,但是对于直通设备却很不友好。直通设备是指通过硬件支持将一个物理设备直通给某个虚拟机使用,该设备由这个虚拟机独占,故该设备产生的中断也应当由这个虚拟机处理,无须经过Hypervisor路由。

为了解决这个问题,ELI(Exitless Interrupt,不退出中断)引入了Shadow IDT(Shadow Interrupt Descriptor Table,影子中断描述符表),区分直通设备产生的中断与其他物理中断,直通设备产生的中断直接递交给虚拟机处理,无须Hypervisor介入。

DID(direct interrupt delivery 直接中断交付)则进一步将虚拟设备产生的中断转换为物理中断IPI(Inter-Processor Interrupt,处理器间中断)直接递交给虚拟机进行处理。

相较于上述软件方案,硬件厂商提供了新的硬件机制,直接将中断注入正在运行的虚拟机且不会引发虚拟机下陷,如Intel公司的发布-中断(Posted-Interrupt)机制和ARM公司的ITS(Interrupt Translation Service,中断翻译服务)机制。