第5章 鲲鹏虚拟化 前几章介绍了Intel x86架构中硬件辅助虚拟化的设计与实现。本章将目光聚焦于另一种主流架构——ARMv8的虚拟化硬件支持。本章同样涵盖虚拟化的四个基本要素: CPU、内存、I/O和时钟。5.1节简要介绍ARM虚拟化架构,5.2和5.3节分别介绍ARMv8 CPU虚拟化以及中断虚拟化,5.4节简要介绍ARMv8架构下的内存虚拟化,5.5节主要介绍ARMv8架构下的I/O虚拟化,5.6节简要介绍ARMv8架构下的时钟虚拟化。 5.1鲲鹏虚拟化框架 鲲鹏系列处理器是基于ARMv8架构设计的面向服务器市场的处理器,下面先简要介绍ARMv8对于虚拟化的支持。 5.1.1鲲鹏虚拟化简介 由于ARMv8需要对ARMv7进行兼容和升级,所以先介绍ARMv7的特权级和工作模式,并和ARMv8以及x86进行对比。 ARMv7是ARM的A系列处理器对应的32位ARM架构。该架构提供了7种工作模式,分别是用户(USR)模式、系统(SYS)模式、一般中断(IRQ)模式、快速中断(FIQ)模式、管理(SVC)模式、中止(ABT)模式和未定义指令终止(UND)模式。七种模式中,用户模式称为非特权模式,其他模式称作特权模式,有权访问所有的系统资源。特权模式中除系统模式外的其余五种模式又称为异常模式。在这七种模式中用户模式的特权级最低,用户的应用程序运行在此模式下,无权访问硬件资源,只能通过模式切换、软中断或者异常等方式使CPU进入特权模式对硬件资源进行间接访问。ARMv8架构在ARMv7的基础上进行了升级,支持64位架构并增加了异常级。 类似于x86架构的Ring0~Ring3特权级,ARMv8提供了EL0~EL3四个异常级(Exception Level)。不同的是,在ARM中EL0的权限级别最低,而EL3权限级别最高。相比于ARMv7只有非特权模式和特权模式的两种特权级别,ARMv8的四个异常级实际上是对特权级的一种扩充,用户程序运行在EL0异常级,操作系统则运行在EL1异常级。为了兼容v7运行模式,v8将v7中的特权模式映射在EL1异常级。而更高的异常级EL2和EL3则分别运行Hypervisor和安全监视器(Secure Monitor)。ARMv8整体异常级架构与对应的模式分布如图51所示。 图51ARMv8异常级与工作模式分布 ARMv8还为各异常级增加了对应的寄存器(如*_EL1、*_EL2等),并将Hypervisor运行在比客户机操作系统更高一级的EL2异常级上,为ARM虚拟化提供了硬件支持。EL2异常级的存在也实现了x86架构特权级“1级”的预想方案。 5.1.2EL2虚拟化框架 类似于x86的虚拟化框架模型,ARMv8虚拟化也有两种类型: Type Ⅰ和Type Ⅱ。Type Ⅰ类似于Hypervisor模型,Type Ⅱ类似于宿主机模型。 (1) Type Ⅰ: 在这种模型中,Hypervisor运行在EL2异常级,控制运行在EL1异常级中的虚拟机,对虚拟机进行隔离和必要的资源共享管理,如图52所示。在这种模式下,虚拟机要想获取全局资源就需要产生异常从而陷入位于EL2中的Hypervisor进行处理。这样虽然对虚拟机进行了有效的隔离,但是特权级切换时引入的上下文切换造成了虚拟机的性能损耗。ARM为此设计了VHE(Virtualization Host Extensions,虚拟化主机拓展)技术,也就是Type Ⅱ模型。 图52ARMv8的Type Ⅰ虚拟化模型 (2) Type Ⅱ: 与Type Ⅰ不同的是,在Type Ⅱ模型中EL2异常级上运行的是宿主机操作系统,Hypervisor则依赖于宿主机操作机系统,利用宿主机操作系统运行环境来管理虚拟机。当发生异常时,异常级会直接由EL0切换到EL2,略去了到EL1的切换步骤,节省了上下文切换的开销,提升了虚拟化性能,详见5.2.3小节VHE虚拟化。 5.2鲲鹏CPU虚拟化 第2章提出了CPU虚拟化所面临的三个挑战以及Intel VTx相应的解决方案,在某种程度上,这也是ARMv8硬件辅助虚拟化需要解决的问题。本节将介绍ARMv8的解决方案以及在设计上与Intel VTx的不同之处。 5.2.1CPU虚拟化 如前所述,CPU虚拟化本质上通过创建QEMU线程来创建vCPU。那么vCPU是如何运行以达到和真正的CPU一样的效果呢?鲲鹏处理器使用VHE扩展,将KVM和Linux内核代码直接运行在EL2,管理控制运行在EL0中的QEMU线程,对外呈现出独立的CPU特性,其具体实现如下。 首先是Hypervisor初始化,设置一些与虚拟化相关的寄存器并开启虚拟化模式,然后开始运行vCPU。vCPU的主要任务是模拟真实的CPU,在鲲鹏虚拟化中采取的是QEMU和KVM相结合的方式。为了隔离多个虚拟机的运行环境,vCPU执行的指令将受到Hypervisor的管理与限制。但是如果每一条指令都要经过Hypervisor处理必然会造成性能损耗。因此将指令分为敏感指令和非敏感指令。非敏感指令交由运行在EL0级别的QEMU线程直接执行,敏感指令则交由Hypervisor进行处理。敏感指令的识别交由硬件完成,CPU自动识别指令是否是敏感指令。对于普通的指令(用户ISA),CPU可以直接运行,而对于敏感指令的执行(系统ISA),vCPU处理较为复杂,下面举例说明。当执行到一条敏感指令时, CPU会自动识别这条指令,然后触发指令陷入。由于鲲鹏处理器支持VHE技术,所以敏感指令将会直接陷入EL2级别的Hypervisor中。在陷入的过程中,CPU需要依次完成如下工作。 (1) 保存上下文。在虚拟机退出之前要保存上下文,以便执行完敏感指令之后继续运行虚拟机。 (2) 执行敏感指令。为了保证虚拟机之间的隔离性,敏感指令不能在EL2级直接执行,而是通过模拟的方式执行。以关机、休眠等指令为例,Hypervisor会通过模拟的方式使虚拟机关机或者休眠,而不会影响主机。 (3) 恢复上下文。将第一步中保存的上下文恢复到相应的寄存器中,具体过程不再赘述。 经过上述三个步骤,vCPU可以模拟敏感指令的执行。然而,CPU虚拟化技术不仅仅是提供指令的执行单元这么简单,还可以使vCPU数目超过物理CPU的数量。Hypervisor通过调度不同的vCPU来使用物理CPU完成对CPU的虚拟,类似于操作系统调度任务时采用的分时复用思路。vCPU的数量可以超出真实CPU的数量,因为vCPU就是QEMU中的一个线程。但是如果vCPU的数量过多,反而会降低整个虚拟机的性能,因为Hypervisor在调度vCPU时,上下文切换会带来一定的时间损耗。 5.2.2EL2异常级 继Intel和AMD相继推出硬件辅助虚拟化拓展后,ARM于2012年推出了自己的硬件虚拟化拓展。与Intel VT-x类似,ARMv8硬件辅助的CPU虚拟化旨在解决“虚拟化漏洞”问题,使所有的敏感指令都能触发虚拟机下陷,从而使Hypervisor能够截获并模拟敏感非特权指令的执行。Intel VTx通过引入根模式与非根模式两种操作模式,并改变非根模式下敏感非特权指令的语义使其触发VMExit,解决了“虚拟化漏洞”问题。ARMv8则引入了EL2来解决上述问题,ARMv8异常级架构如图53所示。 图53ARMv8异常级架构 为了保障系统的安全,ARMv8引入了安全状态(Security State)这一概念。处理器可以处于安全态(Secure State)或非安全态(Nonsecure State),二者都有EL0~EL2ARMv8.4引入了安全态EL2(Secure EL2),将SCR_EL3.EEL2置1开启安全态EL2。异常级以及独立的物理地址空间,安全状态与异常级别共同组成了处理器的当前状态。对于安全态,它拥有完整的物理资源控制权限,可以访问两种状态下的地址空间以及系统寄存器,而非安全态只能访问自己的地址空间以及部分系统寄存器。通常将可信的操作系统和应用运行在安全态,普通的操作系统和应用,如Linux和Android操作系统,则运行在非安全态,从而避免了不可信应用带来的安全隐患。异常级则类似于x86中特权级的概念,其中EL0异常级最低,EL3认为EL3始终处于安全态,安全态的切换由EL3完成。异常级最高。当ARMv8未开启虚拟化拓展时,应用程序运行在EL0,操作系统则运行在EL1,有权访问所有的硬件资源。而当开启了虚拟化拓展后,Hypervisor运行在EL2,客户机操作系统运行在EL1,客户机应用程序运行在EL0。ARMv8提供了HCR_EL2寄存器,用于控制虚拟机行为,类似于x86 VMCS中的VMExecution控制域。以HCR_EL2.TWI为例,在EL0和EL1中执行WFI指令会导致其陷入EL2中。在物理环境下,WFI指令会使当前CPU进入低功耗状态; 而在虚拟环境下执行WFI指令将会令Hypervisor调度另一个vCPU运行。尽管硬件辅助虚拟化已大大减少了虚拟化的开销,但是频繁的虚拟机下陷引起的异常级切换仍会引入巨大的虚拟化开销。ARMv8对操作系统频繁访问的寄存器进行了进一步优化,如MIDR_EL1和MPIDR_EL1寄存器。MIDR_EL1保存着处理器的类型,MPIDR_EL1则保存着处理器亲和性相关的信息。对于这两个寄存器,Hypervisor更希望客户机操作系统能够直接读取到虚拟机中相应寄存器的值,而无须陷入Hypervisor中。故ARMv8提供了VPIDR_EL2和VMPIDR_EL2寄存器,Hypervisor在运行虚拟机之前配置好这两个寄存器的值,当虚拟机读取MIDR_EL1/MPIDR_EL1时,会自动返回VPIDR_EL2/VMPIDR_EL2的值。此外,不同于Intel VTx使用VMCS保存虚拟机上下文,ARMv8直接为EL1和EL2提供了两套系统寄存器,这样虚拟机下陷时便无须保存虚拟机寄存器状态,降低了上下文切换的开销。而运行在EL2中的Hypervisor可以直接读写EL0/EL1中的寄存器。 即便如此,当运行在EL2的Hypervisor需要操作系统内核服务时,仍需切换到EL1异常级进行操作,这样还是会有大量的异常级切换损耗。为此在ARMv8.1中新增了虚拟化主机扩展VHE。 5.2.3VHE 硬件层面上,VHE主要增加了以下几个部分。 (1) 在EL2级别的Hypervisor配置寄存器中增加了用于指示是否开启VHE的控制位E2H。 (2) 在EL2级别新增了TTBR1_EL2、CONTEXTIDT_EL2寄存器供宿主机操作系统使用。 (3) 增加了新的虚拟计时器。 当宿主机内核启动后,首先会调用stext,stext调用el2_setup根据内核是否配置了CONFIG_ARM64_VHE来决定是否开启了VHE。开启VHE后,宿主机操作系统会直接运行在EL2。当产生虚拟机下陷时,由于宿主机运行在EL0(宿主机应用程序)和EL2(宿主机操作系统)两个异常级,所以会直接从虚拟机运行的EL0异常级直接切换为EL2异常级。 图54展现了ARMv8虚拟化架构,Hypervisor运行在EL2异常级,虚拟机则运行在EL0/EL1异常级,但是该架构只支持Type Ⅰ类型的虚拟机,不支持Type Ⅱ类型的虚拟机。这是因为Linux等操作系统开发时假定其运行在EL1异常级,部分EL1的寄存器在EL2中并不存在; 而在Type Ⅱ类型的虚拟机中,Hypervisor很大程度上依赖于宿主机操作系统提供的接口,因此就出现了宿主机操作系统运行在EL1,而Hypervisor运行在EL2,二者异常级不一致的问题。以KVM为例,为了解决上述问题,系统开发人员提出将KVM划分为上层监控器(Highvisor)和底层监控器(Lowvisor)两部分,其中底层监控器运行在EL2,上层监控器运行在EL1,架构如图54所示。 注: ①超级调用(hypercall); ②系统调用(syscall)。 图54ARM KVM架构图 当发生虚拟机下陷时,首先会进入EL2中,底层监控器进行必要的处理,当它需要使用Linux内核的功能时,则切换到EL1中的上层监控器进行处理。通常底层监控器相对精简,只进行必需的处理,将大部分工作留给上层监控器处理。分离模式虚拟化解决了在ARMv8上运行Type Ⅱ类型虚拟机的问题,但是这种分层模式造成了大量的上下文切换,严重影响虚拟化性能,于是VHE应运而生,它允许宿主机操作系统运行在EL2,从硬件层面解决了上述问题。引入VHE前后Type Ⅱ类型虚拟机运行如图55所示。VHE由HCR_EL2. E2H和HCR_EL2. TGE控制,E2H用于使能VHE,而TGE用于在使能VHE时区分虚拟机应用程序和物理机应用程序。 注: ①超级调用(hypercall); ②系统调用(syscall)。 图55引入VHE前后的虚拟化架构 在ARMv8的虚拟化中,虚拟化组件有两个: 一个是运行在EL0异常级的QEMU,另一个是运行在EL2异常级的KVM。类似于x86下的KVM架构,ARM的虚拟化也是通过QEMU线程来模拟vCPU,通过ioctl命令在QEMU和KVM之间交互。在QEMU需要获取全局资源的时候,执行VMExit操作切换到EL2异常级进行全局资源的处理。同时ARM在虚拟化设计中也有自己独特之处。 在VHE中,由于宿主机操作系统需要运行在EL2异常级,所以就需要访问EL2的寄存器。但是由于现有的操作系统都是运行在EL1异常级上面的,它们会默认访问EL1的寄存器。为了能够不加修改地在EL2级运行现有的操作系统内核,就需要对宿主机操作系统进行寄存器重定向操作。具体的方法为: 根据是否开启VHE的标志位E2H,来判断是否需要对寄存器进行重定向。当E2H为1时,运行在EL2异常级的指令进行寄存器重定向,而当E2H为0时则不用重定向。 但是重定向又引入了一个新的问题: 如果运行在EL2异常级的Hypervisor确实需要访问EL1的寄存器,那么重定向就会将其定向到EL2的寄存器上。为此ARM架构引入了新的别名机制: 以_EL12或者_EL02结尾。当访问这些别名时,就可以正常访问EL1的寄存器。 5.3鲲鹏中断虚拟化 由于鲲鹏服务器搭载ARM架构的芯片,所以其使用的中断控制器为ARM架构采用的GIC(Generic Interrupt Controller, 通用中断控制器)。GIC发展至今已历经四代,本节主要侧重于最新的GICv3/GICv4架构,将不会深入讲述GICv1/GICv2架构。 5.3.1GICv1 GICv1是ARM最早推出的中断控制器,现在已经弃用,其架构如图56所示。GICv1最多支持8个PE(Processing Element,处理器单元)和1020个中断源。中断控制器的加入使得处理器可以及时地响应外部设备发送的请求。当外围设备发送中断请求时,中断控制器可以及时捕获并在经过仲裁后将中断信号发送给CPU,然后等待CPU对于中断的处理结果。遗憾的是GICv1并不支持中断虚拟化。 图56GICv1架构 5.3.2GICv2 GICv2增加了对中断虚拟化的支持,但是仍只支持8个PE,其架构如图57所示。 图57GICv2架构 上面的中断控制器主要分为以下几个部分: 中断分发器(Distributor)、CPU接口(CPU Interface)和CPU虚拟接口(CPU Virtual Interface),其中CPU虚拟接口主要用于中断的虚拟化。各部分的主要功能如下。 (1) 分发器: 主要用于处理中断的优先级,并将中断分发给对应的CPU接口。 (2) CPU接口: 主要用来处理中断相关事务,如中断优先级屏蔽、中断抢占以及与CPU之间的通信等。每个CPU核都有一个CPU接口。 (3) CPU虚拟接口: 主要用在虚拟化环境,是虚拟CPU的CPU接口。在虚拟化场景中,需要将HCR_EL2.IMO设置为1,此时所有的中断信号都将会陷入Hypervisor中,并由Hypervisor判断是否将该中断信号插入vCPU中。 GICv2共支持1020个中断源,根据中断的编号将中断分为以下三类。 (1) SGI(Software Generated Interrupt,软件生成中断): 由编号为0到编号为15的中断源组成。这种中断由CPU直接写对应的寄存器触发,而非硬件触发,所以叫作软件产生的中断。这种中断主要用于ARM核间通信。 (2) PPI(Private Peripheral Interrupt,私有设备中断): 由编号为16到编号为31的中断源组成。该中断源为CPU私有的中断源,类似于x86中的LAPIC。 (3) SPI(Shared Peripheral Interrupt,共享设备终端): 由编号为32到编号为1019的中断源组成。该中断源是所有CPU共享的中断源,类似于x86中的IOAPIC。而编号为1020到编号为1023的中断源预留做其他用途。 5.3.3GICv3/GICv4 相较于GICv2,GICv3增加了许多新功能,而GICv4相较于GICv3则变化不大。在后文中,未经特殊说明,GIC都是指GICv3架构。图58中不同颜色的矩形表示GIC架构中的组件,箭头则表示中断传递的流程。GICv3架构主要包括四个组件: 中断分发器(Distributor)、中断再分发器(Redistributor)、CPU接口(CPU Interface)和ITS。在正式介绍这四个组件的功能之前,需要先了解GIC中的一些基本概念和机制。 注: ①可能存在0个或多个ITS; ②SGIs由PE产生,由中断分发器路由。 图58GICv3中断架构 1. 中断类型 除了GICv2定义的三种中断类型外,GICv3还引入了一种新的中断类型LPI(Locality-specific Peripheral Interrupt,特殊设备中断)。LPI是GICv3引入的一种新的基于消息的中断类型,可以兼容PCIe总线的MSI和MSI-X机制。 2. 中断ID GIC为每个中断指定一个INTID(Interrupt ID,中断ID),类似于x86的中断向量号。但是不同于x86中段向量号暗含中断优先级,GIC显式地为每个中断都指定了一个中断优先级号。通常中断优先级号越小,优先级越高。GIC允许高优先级中断抢占低优先级中断。GIC将中断优先级号分为两部分: 优先级组号(Group Priority)和次优先级号(SubPriority),中断抢占需要满足以下两个条件。 (1) 阻塞中断的优先级组号小于CPU当前的运行优先级组号,即当前正在被处理的最高优先级中断的优先级组号。 (2) 阻塞中断的优先级组号小于当前CPU的屏蔽优先级组号(Priority Mask)。 SGI/PPI类型中断的优先级号保存在中断再分发器的相关寄存器中,SPI类型中断的优先级号保存在中断分发器的相关寄存器中,LPI类型中断的优先级号则保存在内存中的LPI配置表中。 3. 中断分组 GIC还引入了中断分组(Interrupt Grouping)机制使得特定的中断只能被特定的异常级处理,从而保障系统安全。为了兼容如图51所示的异常级架构,GICv3引入了中断分组机制,它将物理中断分为三组。 (1) 组0(Group 0): ARMv8期望这些中断在EL3处理。 (2) 安全组1(Secure Group 1): ARMv8期望这些中断在安全态EL1(Secure EL1)中处理。 (3) 非安全组1(Nonsecure Group 1): 在虚拟化环境下,ARMv8期望这些中断在非安全态(Nonsecure EL2)中处理; 在非虚拟化环境下,ARMv8期望这些中断在非安全态EL1(Non-secure EL1)中处理。 SGI/PPI类型中断的分组保存在中断再分发器的相关寄存器中,SPI类型中断的分组保存在中断分发器的相关寄存器中,而LPI类型中断一定属于非安全组1。 4. ITS ITS是GICv3新引入的组件,它输出LPI类型的物理中断。外设只需要提供设备号(Device ID)和事件号(Event ID)就能触发一个LPI中断,其中事件号需要写入ITS中的GITS_TRANSLATER寄存器,而设备号的传输是由架构具体实现所定义的。ITS在内存中维护了四种类型的表: DT(Device Table,设备表)、ITT(Interrupt Translation Table,中断翻译表)、CT(Collection Table,集合表)和vPE Table(Virtual PE Table,虚拟PE表)。其中vPE Table是GICv4添加的支持,使得ITS还可以输出LPI类型的虚拟中断,且无需Hypervisor介入便可以将虚拟LPI中断注入vCPU。各表之间的关联如图59所示。 图59ITS表 各类型表的主要功能如下。 (1) 设备表: 维护了设备号和中断翻译表基地址的映射,ITS为每个设备单独维护一个中断翻译表。 (2) 中断翻译表: 中断翻译表可以维护两种类型的映射。对于物理环境而言,中断翻译表通过事件号得到该事件对应的物理LPI中断的INTID和ICID(Interrupt Collection ID,中断集合ID),然后ITS用ICID索引集合表得知该中断的目的中断再分发器。对于虚拟环境而言,中断翻译表通过事件号得到该事件对应的虚拟LPI中断的INTID和vPEID,然后ITS用vPEID索引虚拟PE表得知该虚拟中断的目的vCPU,将该虚拟中断发送至vCPU所在物理CPU对应的中断再分发器。若当前vCPU没有运行,ITS将给物理CPU发送一个门铃中断(Doorbell Interrupt),使其调度vCPU运行。 (3) 集合表: 如上所述,维护ICID和中断再分发器的映射。 (4) 虚拟PE表: 如上所述,维护vPEID和中断再分发器的映射。 5. GIC组件 前面提到,GIC中断架构主要包括四个部分: 中断分发器、中断再分发器、CPU接口和ITS。其中中断分发器、中断再分发器和ITS实现在GIC内部,故CPU通过MMIO的方式访问其内部寄存器。而CPU接口则位于CPU核内部,可以直接通过系统寄存器访问。中断分发器和ITS由所有CPU核共享,中断再分发器则与CPU核一一对应,每一个CPU接口都有一个中断再分发器与其相连,各部分具体功能如下。 (1) 中断分发器: 主要维护SPI类型中断的相关信息,如中断优先级、中断分组、中断路由信息和中断状态等。此外,SGI类型中断也通过中断分发器路由。GICv3引入了亲和性路由(Affinity Routing)机制,使得SPI路由至指定CPU或指定CPU集合中的某一个,而SGI则会被路由至指定CPU集合中的每一个CPU。 (2) 中断再分发器: 维护PPI/SGI类型中断的相关信息,如中断状态、中断优先级、中断触发方式等。 (3) ITS: ITS是GICv3新引入的组件,它输出LPI类型的中断。外设只需要向ITS的GITS_TRANSLATER寄存器写入一个事件号(EventID)就能触发一个LPI中断。而在GICv4架构中,ITS还提供了Virtual LPI直接注入机制,类似于APICv提供的分布中断机制,无需Hypervisor参与便能直接注入虚拟中断。 (4) CPU接口: 主要负责中断优先级检测、优先级仲裁(选择优先级最高的中断处理)和中断应答等。 6. GIC中断处理 在GIC中断架构中,中断存在以下四种状态。 (1) 非活跃态(Inactive): 当前没有待处理或正在处理的中断。 (2) 阻塞态(Pending): 当前中断正在等待CPU处理。 (3) 活跃态(Active): 当前中断已经被CPU响应,该中断正在被CPU处理。 (4) 活跃阻塞态(Active and Pending): 当前中断正在被处理时,又收到一个相同INTID的中断。 四种中断状态在中断处理过程中的变化如下所述。 (1) 中断产生: 外部设备或系统程序通过中断连接线或写入GIC相关寄存器触发中断,此时中断从非活跃态变为阻塞态。 (2) 中断分发: GIC通过前述寄存器或内存控制结构确定该中断的优先级、中断分组等信息,并通过亲和性路由机制将中断发送给目标CPU接口。 (3) 中断交付: CPU接口将中断递交给CPU。 (4) 中断激活: CPU应答该中断,表明该中断正在被处理,该中断由阻塞态变为活跃态。在此过程中如果有相同INTID的中断到达,则中断变为活跃阻塞态。 (5) 运行优先级降低: CPU处理完该中断后,首先修改当前的运行优先级,使得低优先级中断可以被响应,此时中断仍处于活跃态。 (6) 中断无效: 将当前中断状态置为非活跃态,使得后续处于阻塞态的同INTID中断能够被处理。 5.3.4GICv3/GICv4中断虚拟化 GIC为中断虚拟化提供了以下硬件支持: 虚拟CPU接口寄存器直接访问、虚拟中断注入以及虚拟LPI中断直接注入。 1. 虚拟CPU接口寄存器直接访问 在早期GIC架构中,CPU接口也位于GIC内部,同中断分发器等组件一样,CPU通过MMIO的方式访问其内部寄存器。而在虚拟环境中,为了避免虚拟机直接写入CPU接口中的寄存器,Hypervisor通常会将接口寄存器映射区域设置为不可访问,从而截获所有访问并陷入Hypervisor中进行相应的模拟。这一处理方式同前述早期x86中断控制器的访问类似。而GICv3将CPU接口移入CPU内部并提供相关的系统寄存器(ICC_*)供CPU使用,大大提升了中断响应速度。而为了应对虚拟化环境,GICv3还为这些系统寄存器提供了相应的虚拟CPU接口寄存器(ICV_*),当Hypervisor将HCR_EL2.IMO和HCR_EL2.FMO设置为1时,运行在EL1中的虚拟机操作系统对CPU接口系统寄存器(ICC_*)的访问将被重定向到相应的ICV_*寄存器,从而避免了中断处理过程中访问CPU接口寄存器造成的虚拟机下陷。 2. 虚拟中断注入 GICv3可以配置使得所有的物理中断路由到EL2,此时Hypervisor对该中断进行检查,若该物理中断的目标为Hypervisor,则Hypervisor按照前述流程处理该物理中断; 若该中断目标为vCPU,则Hypervisor会向vCPU中注入一个虚拟中断。GICv3提供了寄存器(ICH_LR_EL2)保存虚拟中断的INTID、中断优先级、中断状态以及相关联的物理中断INTID等。当vCPU恢复运行时,硬件将根据这些寄存器中的信息向vCPU注入一个虚拟中断,并调用相应的中断处理函数。 3. 虚拟LPI中断直接注入 GICv4引入了虚拟LPI中断注入机制,无需Hypervisor参与便可以将虚拟LPI中断注入vCPU,这是通过前述GIC的ITS组件完成的。ITS通过查询设备表、中断翻译表和虚拟PE表得到虚拟中断目的vCPU所处的物理CPU所对应的中断再分发器,将虚拟LPI中断信息插入位于内存中的虚拟LPI配置表(Virtual LPI Configuration Table)和虚拟LPI状态表(Virtual LPI Pending Table)中。然后vCPU会将表中记录的虚拟LPI中断与前述ICH_LR