这是一篇论文:https://academiccommons.columbia.edu/doi/10.7916/D8D238J2/download
学习ARM虚拟化非常好的材料,这里翻译了其中几个章节。
-----------------------------------------------------------------------------------------------------------
2.1 ARM虚拟化扩展
由于ARM体系结构不是经典的可虚拟化(参见第1章),因此ARM引入了虚拟化扩展(VE)作为ARMv7 [11]和ARMv8 [13]体系结构的可选扩展。 诸如Cortex-A15之类的ARM CPU [10]包括对虚拟化的硬件支持,针对服务器和网络市场的所有ARMv8 CPU都包括虚拟化扩展。 我简要介绍了ARM虚拟化扩展。
2.1.1 CPU虚拟化
要运行VM,必须虚拟化特权CPU模式以保持隔离,并使管理程序保持对物理硬件的控制。如果允许VM完全控制底层硬件,那么它们会妨碍系统上的其他任务和VM运行,关闭CPU,甚至危及系统其他部分的完整性。如第1章所述,可虚拟化体系结构可以通过在非特权用户模式下运行所有VM(包括guest内核)来虚拟化特权CPU模式,并将每个敏感操作捕获到管理程序并在软件中模拟操作。要在非虚拟化架构(如ARM)上支持CPU虚拟化,一种选择是将架构更改为所有敏感操作都陷入,从而使架构可虚拟化。但是,这种方法不一定能提供良好的性能,并且要求管理程序正确有效地实现所有敏感操作的仿真。相反,如果硬件可以通过某种方式改变这些操作,以在虚拟CPU状态而不是物理CPU状态下操作来支持直接在VM中执行敏感操作,则可以实现隔离而不会过于频繁地捕获到管理程序。这是ARM和x86硬件虚拟化支持对许多操作采用的方法。例如,当使用ARM虚拟化扩展运行VM时,当VM禁用中断时,这实际上不会陷入虚拟机管理程序,而是禁用虚拟中断,这将在第2.1.3节中进一步详细说明。 ARM通过引入新的更高权限的CPU模式来运行虚拟机管理程序来实现这些功能。
图2.1和2.2显示了ARM CPU模式和权限级别,包括可选的安全扩展(TrustZone)和新的权限级别和CPU模式,ARMv7的PL2(Hyp)和ARMv8的EL2。实现安全扩展的ARM CPU将执行分为两个世界:安全和非安全。提供特殊模式监视模式,以在安全和非安全世界之间切换。虽然实现安全扩展的ARM CPU总是在安全的环境中启动,但固件通常会在早期阶段转换到非安全的世界。安全世界用于运行可信执行环境(TEE)以支持数字权限管理或身份验证等用例。通过使用安全世界进行虚拟机管理程序执行,TrustZone似乎对虚拟化很有用,但这并不容易,因为安全世界无法控制非安全世界对普通非安全RAM的访问,并且只能配置从非安全到陷阱的陷阱在一些选择操作上保护世界。例如,当OS配置虚拟内存时,没有办法将在非安全世界中执行的一般OS操作捕获到安全世界。因此,以最高非安全权限级别运行的任何软件都可以有效地访问所有非安全物理内存,从而无法隔离在非安全世界中运行的多个VM。
在具有TrustZone地址空间控制器(TSAC)的系统上使用安全扩展来构建虚拟机管理程序是可能的,该机制可以用于禁止对物理内存区域的非安全访问,但是这种方法存在严重的限制,例如 不能通常虚拟化物理内存,但只能静态分区,我认为任何此类解决方案对于通用虚拟化都表现不佳。
ARMv7和ARMv8 CPU保护机制之间的差异不会影响虚拟机管理程序设计,但我强调了完整性的重要变化。差异主要涉及非安全和安全世界与命名之间的相互作用。在ARMv7上,在监控模式下运行时的权限级别是安全PL1,因此监控模式不比PL1内核模式更具特权。在ARMv8上,EL3是一种比安全EL1更具特权的模式。例如,ARMv8定义了几个系统寄存器,这些寄存器只能从EL3访问而不能从安全EL1访问。这种特殊的差异会影响固件如何配置非安全世界的实现,但不会影响仅限于非安全状态的虚拟机管理程序的设计。 ARMv7定义了多个PL1模式;实际上有六种PL1模式,Supervisor,Abort,Undefined,FIQ,IRQ和System。这些模式共享相同的权限级别,但具有许多专用寄存器,例如它们自己的堆栈指针。系统模式很特殊,因为它与用户模式共享相同的寄存器,但在PL1上运行。 ARMv8消除了每个权限级别的多个模式,并降低了单个概念(异常级别)的复杂性。因此,ARMv8有三种非安全CPU模式,EL0,EL1和EL2。当ARMv7中首次引入虚拟化扩展时,虚拟机管理程序CPU模式被命名为Hyp模式,但是当后来引入ARMv8并且只有异常级别的概念时,等效的异常级别称为EL2。因此,我在本文中可互换地使用术语Hyp模式和EL2来指代CPU的管理程序执行模式。
引入了Hyp模式以支持ARM体系结构上的虚拟化,并且旨在尽可能简单地运行虚拟机管理程序。 Hyp模式严格比用户和内核模式更具特权。在Hyp模式下运行的软件可以配置硬件以在各种事件上从内核和用户模式陷入Hyp模式。例如,在Hyp模式下运行的软件可以选择性地选择读取CPU的ID寄存器这个动作是否应该陷阱到虚拟机管理程序,或者是否可以由VM直接读取它们。能够根据软件配置配置这些陷阱很有用,因为虚拟机管理程序可以选择模拟与VM的底层硬件CPU完全相同的CPU,在这种情况下,在ID寄存器读取时,不需要从陷阱中引入额外的性能损失,管理程序也可以选择仅向VM公开硬件CPU功能的子集,在这种情况下,管理程序将要模拟ID寄存器的值。作为另一个示例,管理程序软件可以选择性地选择捕获对浮点寄存器的访问或允许对VM的这些寄存器的完全访问。当在多个VM之间复用单个浮点寄存器库时,这很有用,因为浮点寄存器通常很大,因此保存和恢复到内存很昂贵。允许浮点寄存器访问上的可配置陷入,允许管理程序仅按需上下文切换浮点寄存器,同时一旦寄存器状态属于VM的执行上下文,就允许对VM的浮点硬件的完全访问。从VM到虚拟机管理程序的陷入类似于从用户空间到内核的陷入,并更改CPU模式并跳转到内存中的预定义位置,称为异常向量。
要运行VM,虚拟机管理程序必须从Hyp模式配置陷入(traps)和内存虚拟化。并执行异常返回用户或内核模式,具体取决于VM是要运行其用户空间还是内核。然后,VM在用户和内核模式下正常执行,直到达到需要管理程序干预的某些条件。此时,硬件陷入Hyp模式,从而控制管理程序,然后管理程序可以管理硬件并在VM之间提供所需的隔离。一旦管理程序处理了该条件,管理程序就可以执行另一个异常返回到用户或内核模式,然后VM可以继续执行。并非所有陷阱和异常都由Hyp模式处理。例如,系统调用或页面错误导致的陷阱直接从用户模式陷阱到VM的内核模式,以便客户操作系统处理它们而无需管理程序的干预。这可以避免在每次系统调用或页面错误时进入Hyp模式,从而减少虚拟化开销。同时,可以将硬件中断配置为始终陷入Hyp模式,以便管理程序可以保持对硬件的控制。
ARM体系结构允许对从用户和内核模式到Hyp模式的事件和指令陷阱进行细粒度控制。可以禁用进入Hyp模式的所有陷阱,并且单个非虚拟化内核可以在内核模式下运行并完全控制系统,从而有效地绕过Hyp模式。这是例如Linux在未运行虚拟机管理程序时使用的配置。
ARM围绕与现有内核模式不同的独立CPU模式设计了虚拟化支持,因为架构师在更复杂的丰富OS内核下设想了一个独立的虚拟机管理程序,因为它非常适合架构的现有权限层次结构[38]。为了简化芯片实现并减少验证空间,与内核模式相比,ARM减少了Hyp模式下可用的控制寄存器数量。例如,ARMv7及更高版本的内核模式具有两个页表基址寄存器,为内核和用户空间提供上下虚拟地址空间,但Hyp模式只有一个页表基址寄存器。 ARM还重新定义了Hyp模式使用的页表条目中权限位的含义,因为它们没有设想管理程序与用户空间中运行的软件共享页表。例如,Linux以内核模式运行,但用户模式和内核模式都使用相同的页表,并且通过使用页表中的权限位来防止用户空间访问内核数据和代码,这会阻止用户模式访问内核内存,但允许内核直接访问用户空间内存,这是许多Linux系统调用(如read或write)的一种非常常见的操作。
2.2.1 内存虚拟化
ARM体系结构通过单个地址转换阶段提供虚拟内存支持,该阶段转换使用页表将虚拟地址转换为物理地址。 虽然可以在VM的上下文中重用现有的虚拟内存支持,但它需要捕获VM对虚拟内存控制寄存器的访问并构建影子页表。 已知影子页表会增加性能开销并显着增加实现复杂性[1]。 因此,让VM管理自己的虚拟内存结构而不会陷入虚拟机管理程序,同时允许虚拟机管理程序完全控制物理内存资源非常重要。 通过在管理程序的控制下在硬件中实现第二阶段的地址转换,可以满足这两个要求。
ARM虚拟化扩展提供第2阶段转换,作为在Hyp模式下运行的虚拟机管理程序的机制,以完全控制对物理内存的所有访问。通过两个转换阶段,第一阶段将虚拟地址(VA)转换为访客物理地址(GPA),第二阶段将GPA转换为物理地址(PA)。 ARM体系结构有自己的命名法,与常用术语略有不同。ARM使用术语中间物理地址(IPA)代替GPA。图2.3显示了完整的地址转换序列,它使用阶段1转换的三级分页和阶段2转换的四级分页。每个翻译阶段的数量级别可以根据VA,GPA和PA空间的大小而变化。启用阶段2转换后,使用一组专用页表将所有GPA从内核和用户模式转换为PA。可以独立启用和禁用第1阶段和第2阶段的转换。禁用第1阶段转换时,VA和GPA是相同的,类似地,当禁用第2阶段转换时,GPA和PA是相同的。 ARMv7第2阶段页表使用LPAE [22]页表格式,而在64位模式下执行的ARMv8使用VMSAv8-64转换表格式[13],这是LPAE格式的略微扩展版本。阶段2页表使用与阶段1页表不同的格式。
运行VM时,VM管理自己的第1阶段页表和第1阶段内存控制寄存器,而不会捕获到虚拟机管理程序。第1阶段页表将VAs转换为GPA,然后使用第2阶段翻译系统将其进一步转换为PA。阶段2转换只能从Hyp模式配置,并且可以完全禁用和启用。因此,管理程序管理GPA到PA转换。在内核和用户模式下执行的非虚拟化操作系统可以在禁用第2阶段转换时直接管理物理内存。 Hyp模式使用自己的地址转换机制,它仅使用特殊页表格式(不同于内核模式使用的第1阶段页表格式)仅支持第1阶段转换,仅用于Hyp模式。由于Hyp模式不支持第2阶段转换,因此Hyp模式第1阶段页表直接从VAs转换为PA,而不通过GPA,因为在Hyp模式下运行的管理程序可以完全控制物理内存。阶段2翻译(如阶段1翻译)可以缓存在TLB数据结构中以提高性能。为了避免在VM之间切换时无效TLB,ARM支持VMID,VMID由管理程序基于每个VM配置,并且所有TLB条目都标记有与当前阶段2上下文关联的VMID。
阶段1和阶段2页表都允许使用一组内存权限和属性标记每个页面。 第2阶段页表可以覆盖第1阶段页表上的权限,例如将读/写页标记为只读。 这对于页面重复数据删除和交换等技术非常有用。 内存属性配置内存访问是可缓存还是不可缓存,这意味着它们可以绕过缓存并直接访问主内存。 遗憾的是,ARMv8.4之前的第2阶段页表不会覆盖第1阶段属性的属性,而是以最强的语义优先。 例如,阶段1中的不可缓存映射不能被修改为通过其阶段2映射可缓存,因为不可缓存被认为是更强的存储器属性。 此设计决定限制了模拟某些类型的支持DMA的设备(如VGA帧缓冲器)的能力[87]。
2.1.3 中断虚拟化
中断用作从其他硬件组件到一个或多个CPU的异步通知机制。 例如,I / O设备可以向CPU通知需要CPU注意的某些事件,并且一个CPU可以向另一个CPU发出信号以请求某些操作。 在虚拟化的环境中,管理程序通常管理能够生成中断的所有硬件组件,因此即使在运行VM时,仍然必须通知管理程序来自这些组件的中断。 同时,VM将与一组虚拟设备进行交互,并期望来自这些虚拟设备的通知以中断的形式传递到虚拟CPU。 因此,区分和支持由硬件组件生成并传送到物理CPU的物理中断以及由虚拟设备生成并传送到虚拟CPU的虚拟中断非常重要。
ARM定义了通用中断控制器(GIC)v2和v3架构[9,12],它支持处理物理和虚拟中断。 GIC路由从设备到CPU的中断,CPU查询GIC以发现中断源。 GIC在多核配置中尤为重要,因为它用于从一个CPU到另一个CPU生成处理器间中断(IPI)。 GIC分为两部分:分配器和CPU接口。系统中只有一个分配器,但每个CPU都有一个GIC CPU接口。在GICv2上,CPU接口和分配器都通过内存映射接口(MMIO)进行访问。在GICv3上,通过系统寄存器访问CPU接口,仍然使用MMIO访问分配器。分发器用于配置GIC,例如配置中断的CPU亲和性,完全启用或禁用系统中断,或将IPI发送到另一个CPU。 CPU接口用于确认(ACK)和停用中断(中断结束,EOI)。例如,当CPU接收到中断时,它将读取GIC CPU接口上的特殊寄存器,该寄存器确认中断并返回中断号。在CPU使用从ACK寄存器检索到的值写入CPU接口的EOI寄存器之前,不会再次将中断引发到CPU,从而取消激活中断。
这是一个略微简化的视图,因为GIC支持称为拆分优先级丢弃和停用的概念。
一旦确认了中断,它就会进入激活状态,并且在中断被禁用之前,GIC不会再次向CPU发出中断信号。通常,写入EOI寄存器将禁用中断并执行优先级丢弃,这意味着GIC将接受与最近确认的中断具有相同优先级的其他中断。但是,通过拆分优先级丢弃和取消激活,对EOI寄存器的写操作将仅降低优先级,但不会取消激活中断。这有助于推迟处理中断的某些部分,并确保在某些事件通过之前不会再次看到特定的中断。例如,这可以用于通过将acked中断切换到不同的线程来实现线程中断处理,并且能够获取相同优先级的新中断。通过拆分优先级丢弃和取消激活,软件必须在写入EOI寄存器后写入单独的寄存器DIR寄存器。
GIC可以通过两个独立的信号IRQ和FIQ向每个CPU发出中断信号。软件可以将GIC配置为使用中断组将特定中断发送为IRQ或FIQ。 (我们避免在此处对中断组及其与安全性扩展的关系进行错综复杂的讨论,参考体系结构手册以获取更多信息。)每个信号IRQ和FIQ可以独立配置为陷阱到Hyp或内核模式。将所有中断捕获到内核模式并让内核模式下运行的OS软件直接处理它们是有效的,但是在VM的上下文中不起作用,因为管理程序失去了对硬件的控制。将所有中断捕获到Hyp模式可确保虚拟机管理程序保留控制权,但需要在软件中模拟虚拟中断以向VM发送事件信号。但这非常麻烦而且代价高昂,因为中断和虚拟中断处理的每个步骤(例如确认和去激活)必须通过管理程序。
GIC包括v2.0及更高版本的硬件虚拟化支持,称为GIC虚拟化扩展(GIC VE),它避免了模拟虚拟中断传送到VM的必须性。 GIC VE为每个CPU引入了一个虚拟GIC CPU接口,并为每个CPU引入了相应的管理程序控制接口。 VM配置为查看虚拟GIC CPU接口而不是真正的GIC CPU接口。通过写入GIC管理程序控制接口中的特殊寄存器(列表寄存器(LR))生成虚拟中断,虚拟GIC CPU接口将虚拟中断直接引发到VM的内核模式。由于虚拟GIC CPU接口包括对ACK和EOI的支持,因此这些操作不再需要陷入到虚拟机管理程序以在软件中进行仿真,从而减少了在CPU上接收中断的开销。例如,模拟的虚拟设备通常通过软件API向管理程序引发虚拟中断,管理程序可以通过将模拟设备的虚拟中断号写入列表寄存器来利用GIC VE。进入VM后,GIC VE直接将VM中断到内核模式,并允许客户操作系统确认并停用虚拟中断,而不会捕获到虚拟机管理程序。请注意,仍必须在软件中模拟分发服务器,并且VM对分发服务器的所有访问仍必须陷入到管理程序。例如,当虚拟CPU将虚拟IPI发送到另一个虚拟CPU时,这将导致虚拟机管理程序陷入,该虚拟机管理程序模拟软件中的分发服务器访问,并将IPI作为虚拟IPI提升到接收虚拟CPU。如果接收虚拟CPU正在VM内执行,则涉及将物理IPI发送到正在执行虚拟CPU的CPU,这会导致VM虚拟CPU陷入虚拟机管理程序,检测挂起的虚拟IPI,并编写列表寄存器在接收CPU的GIC管理程序控制接口上使用虚拟IPI。
图2.4显示了使用虚拟化扩展的GICv2的简化概述。 GIC中有三种类型的中断:专用外设中断(PPI),共享外设中断(SPI)和软件产生的中断(SGI)。器件可以向PPI发送PPI或SPI信号,只有每个SPI有一条中断线,每个处理器的每个PPI都有一条线,它们的中断号分配不同。例如,每个CPU可能有一个定时器,并且定时器中断与PPI相关联,因此它可以使用相同的中断号独立地向CPU发送信号。由于希望将IPI从一个CPU发送到另一个CPU的CPU的MMIO命令,SGI从GIC分配器内生成。分发器维护每个中断的状态和配置,并将挂起和启用的中断转发到接收CPU的CPU接口。 CPU接口使用IRQ和FIQ线路向CPU发出信号,但也可以通过MMIO从CPU访问它们,以确认和停用中断。直接在硬件上运行的操作系统访问真实的CPU接口,但VM中的客户操作系统访问虚拟CPU接口。由于GICv2访问都基于MMIO,因此管理程序使用第2阶段页表来确保VM只能访问虚拟CPU接口,并且VM无法访问控制接口。图2.4中显示的MMIO路径(由粗蓝线表示)显示了VM和管理程序配置,其中CPU在执行管理程序时访问管理程序控制接口,并在运行VM时访问虚拟CPU接口。列表通过管理程序控制接口进行控制,将内容提供给虚拟CPU接口,CPU在运行VM时会观察到这一点。
设备可以通过两种方式实现中断信号:边沿触发和电平触发中断。当观察到上升沿时,边沿触发的中断变为挂起,并且当线路降低并随后再次上升时,仅发出新的中断信号。只要线路的电平是高,电平触发的中断就会挂起,当线路的电平为低时,它会丢失它们的挂起状态。这意味着虽然GIC只需要观察边沿触发输入的变化,但它必须不断地对电平触发中断的线路电平进行采样。为了支持在VM上下文中对线路级进行采样,GIC VE在列表寄存器中支持一个额外的位EOI位,当VM EOI设置了该位的中断时,该位会导致到管理程序的陷阱。从GIC VE生成的陷阱使用专用硬件中断(称为GIC维护中断)发出信号。管理程序可以将列表寄存器中的EOI位设置为电平触发中断,以在完成电平触发中断时强制退出VM,然后管理程序可以重新采样相应中断的虚拟线状态,并注入如果电平仍然保持高电平,则会产生新的中断。因此,在VM的上下文中,级别触发的中断比边缘触发的中断更慢且更复杂。
中断实际上可以通过在上升沿或下降沿发出中断信号来实现不同的电气实现,或类似地通过在线路为低或高时考虑电平触发的断言来实现,并且GIC支持两种配置。 这种差异对软件设计或性能没有影响,因此我们将讨论局限于使用上升沿或高电平来断言中断的中断。
GIC VE还在列表寄存器中提供了一个额外的特殊位,称为HW位。此功能特定于ARM体系结构,是虚拟化计时器硬件的基础,因此值得一提。其思想是虚拟中断可以与物理中断相关联,这样当VM停用虚拟中断时,也可以停用相应的物理中断。当管理程序收到应作为虚拟中断转发给VM的物理中断时,管理程序可以选择不停用物理中断,但是将其推迟到VM停用虚拟中断。这是通过将列表寄存器中的HW位设置为用于发送虚拟中断并将虚拟中断号链接到物理中断号来完成的,在列表寄存器中使用两个单独的专用字段。 (请注意,由于VM可能呈现的中断号与物理主机的中断号不同,因此虚拟和物理中断号可能不同。)此机制对于不希望被设备中断直到VM的虚拟机管理程序非常有用。已完成处理先前的中断,这通常是当VM暴露于底层硬件的任何方面时的情况,例如在定时器或直接设备分配的上下文中。
2.1.4 定时器虚拟化
系统软件需要一些机制来保持时间。提供系统时间以及可编程定时器产生的中断对于给应用程序提供服务的OS来讲是非常重要的。例如,图形用户界面(GUI)通常编程定时器以在编辑文本时使光标闪烁,并且OS内核通常在各个检查点检查进程运行的时间,以根据调度策略来调度任务。在运行VM时,虚拟机管理程序对两种计时机制都有类似的要求,并且在VM内运行的guest操作系统也需要保持时间。但是,时间的定义可以取决于底层硬件,可以从提供的时间范围从墙上时间到CPU的周期,或者使用其他可能停止计数或根据硬件系统的电源状态以较慢速率计数的恒定定时器。此外,当运行VM时,VM可能无法连续运行,因为管理程序可能会运行其他任务而不是VM,而不会通知VM,因此,墙上时间可能与虚拟处理时间无关它与原生处理时间有关。
ARM通用定时器架构(也称为架构定时器)包括对定时器虚拟化的支持。 目标是为VM提供对其自己的计时硬件的访问权限,以便VM可以告诉时间和程序计时器而不会陷入虚拟机管理程序,并允许VM分辨虚拟时间和物理时间的某些概念之间的差异[68]。 为了实现这一目标,ARM提供了三个独立的定时器:管理程序计时器,物理计时器和虚拟计时器。 这个想法是管理程序计时器代表实际的时间流逝,并且由管理程序专门拥有,其中物理和虚拟计时器都由VM使用。 物理计时器还表示实际的时间流逝,其中虚拟计时器可以由管理程序调整以提供虚拟时间的度量,例如基于实际允许VM运行的时间。
更具体地说,ARM上的每个定时器包括计数器和计时器。计数器只是一个向上计数的器件,定时器是一个小的逻辑电路,可以对其进行编程,以便在计数器达到预设值时产生中断。管理程序计时器只能从Hyp模式访问,任何从内核或用户模式对其进行编程的尝试都会产生内核模式的异常。管理程序可以配置在VM中执行时是否在访问物理定时器时陷入,或者VM是否可以访问物理定时器。虚拟计时器始终可在VM中访问。管理程序可以编程一个特殊的虚拟计数器偏移寄存器,只能在Hyp模式下访问,它会改变虚拟定时器相对于物理定时器的计数器值。管理程序可以使用此功能,例如,当VM尚未运行时从虚拟计数器中减去时间,并且VM可以使用此信息来调整其调度策略或报告被盗时间(stolen time),对用户来讲是虚拟CPU没有运行的时间。
虽然对定时器的虚拟化支持允许VM直接对定时器进行编程而不进行陷入,但是当定时器触发时,管理程序仍然涉及向VM发送中断信号。当定时器触发时,它们会产生硬件中断。这些硬件中断与系统上的其他硬件中断没有什么不同,无论发出信号的定时器如何,因此遵循系统配置,执行本机操作系统时,可以直接传送到内核模式;当执行VM时,陷入到虚拟机管理程序。当管理程序处理来自分配给VM的计时器设备(例如虚拟计时器)的中断时,它通常使用GIC VE编程虚拟中断以向VM发信号通知计时器已到期。但是,由于定时器输出信号是电平触发的,定时器输出将保持有效,直到可以直接访问定时器硬件的VM取消定时器或将其重新编程为将来触发。如果管理程序代表VM取消激活中断,GIC将从定时器重新对输出行进行采样,并且由于它仍处于断言状态,它将立即向CPU发出新的待处理中断信号,并且CPU不会使任何前进的进展,因为它将忙于处理连续的中断流。 ARM体系结构专门用于将定时器的功能与GIC集成,方法是将分配给VM的定时器的物理中断保持活动状态,从而防止进一步发出中断信号,并将定时器的虚拟中断注入VM,通过设置列表寄存器中的HW位,将其与基础物理中断相关联。这样,当VM运行并观察挂起的虚拟定时器时,它将运行定时器的中断服务程序(ISR),取消定时器,降低定时器输出信号,并最终停用中断,例如通过写入到虚拟CPU接口上的EOI寄存器。由于HW位已设置,因此取消激活虚拟中断会禁用物理和虚拟中断,并且当计时器稍后再次触发时,它将陷入虚拟机管理程序。
每个CPU都有自己的一组专用定时器,在跨物理CPU迁移虚拟CPU时,管理程序负责正确迁移和同步定时器。
2.1.5 Arm VE和 intel VMX的对比
ARM虚拟化扩展与来自Intel和AMD的x86的硬件虚拟化支持之间存在许多相同点和不同点。 Intel VMX [50]和AMD-V [2]非常相似,因此我们将其与ARM VE和Intel VMX的比较限制在一起。差异总结在表2.1中。 ARM通过单独的CPU模式Hyp模式支持虚拟化,Hyp模式是一种独立且严格更高权限的CPU模式,而不是之前的用户和内核模式。相比之下,英特尔具有root和非root模式[50],它们与CPU保护模式正交。虽然ARM上的敏感操作陷阱到Hyp模式,但敏感操作可以从non-root模式陷入root模式,同时在Intel上保持相同的保护级别。两种硬件设计之间的关键区别在于,英特尔的根模式支持与非根模式相同的全范围用户和内核模式功能,而ARM的Hyp模式是一种严格不同的CPU模式,具有自己的一组功能。例如,在Intel根模式下执行的软件使用与在非根模式下执行的软件相同的虚拟内存系统和相同的虚拟地址布局。在ARM上,Hyp模式使用不同的页表,并且与内核模式相比具有有限的VA空间。这些差异表明,英特尔想象任何软件堆栈,类似于现有的操作系统,以root和非root模式运行,其中ARM想象一个较小的独立虚拟机管理程序以root模式运行。
ARM和Intel都陷入了各自的Hyp和root模式,但Intel为内存中的VM控制结构(VMCS)提供了特定的硬件支持,用于在切换到root模式和从root模式切换时自动保存和恢复CPU的执行状态。 VMX转换VM Entry和VM Exit。这些转换在硬件中定义为单个原子操作,用于在guestOS和管理程序执行上下文之间切换时,使用VMCS自动保存和恢复所有CPU的执行状态。 ARM是一种RISC风格的体系结构,它具有更简单的硬件机制,可以在EL1和EL2之间进行转换,但需要通过软件来决定需要保存和恢复哪个状态。与在x86上的根和非根模式之间切换相比,这在EL1和EL2之间转换时需要完成的工作量提供了更大的灵活性,但对管理程序软件实现提出了不同的要求。例如,与在优化的x86服务器硬件上从根模式转换到非根模式的数百个周期相比,在第一代ARM VE硬件上捕获ARM的Hyp模式仅花费数十个周期。 ARM和英特尔在支持虚拟化物理内存方面非常相似。两者都引入了一组额外的页表来将guest虚拟机转换为主机物理地址。 ARM在第二阶段翻译中得益于后见之明,而英特尔在其第二代虚拟化硬件之前不包括其等效的扩展页表(EPT)支持。
ARM对虚拟定时器的支持没有真正跟x86对应。 x86计时世界由无数的计时设备组成,部分原因在于PC平台的历史和遗产。现代x86平台通常支持8250系列PIT用于传统支持和本地APIC(LAPIC)计时器。英特尔对虚拟化的硬件支持增加了VMX-Preemption计时器,该计时器允许虚拟机管理程序独立于其他计时器的编程方式对VM的退出进行编程。添加了VMX-Preemption计时器以减少计时器触发和注入虚拟计时器中断的管理程序之间的延迟。这是因为管理程序不必处理来自LAPIC计时器的中断,而是可以直接从VM出口告知抢占计时器已到期。与ARM相反,x86不支持完全控制VM的各个定时器硬件。 x86允许VM直接读取时间戳计数器(TSC),而ARM允许访问虚拟计数器。 x86 TSC的分辨率通常高于ARM计数器,因为TSC由处理器的时钟驱动,其中ARM计数器由专用时钟信号驱动,尽管频率最低为50 MHz。
最近的英特尔CPU还包括对APIC虚拟化的支持[50]。 与ARM的GIC VE虚拟中断支持类似,英特尔的APIC虚拟化支持(APICv)还允许虚拟机完成虚拟中断,而不会陷入虚拟机管理程序。 与ARM类似,APICv允许VM直接访问中断控制器寄存器而无需捕获到管理程序。 在ARM上,GIC虚拟CPU接口支持直接访问所有CPU接口寄存器。 APICv在内存中为VM的虚拟APIC状态提供后备页面。 在没有APICv的Intel上,完成VM中的虚拟中断陷阱到root模式。
ARM和x86都引入了对直接传递虚拟中断的支持。 直接传送意味着执行VM的CPU可以观察到新的虚拟中断,该虚拟中断直接从直通设备向VM发送信号,而无需从VM捕获到管理程序。 英特尔已将已发布的中断作为英特尔定向I / O架构的一部分[51]。 发布中断支持直接从物理设备或其他CPU传递虚拟中断。 ARM推出了GIC架构4.0版本(GICv4)[12],它支持从信令MSI的物理设备直接传递虚拟消息信号中断(MSI)。 遗憾的是,在撰写本论文时,没有可用的GICv4硬件,因此我专注于GICv2和GICv3,可以使用它们来验证和评估虚拟化软件。
2.2 KVM/ARM系统架构
KVM / ARM不是在虚拟机管理程序中重新实现复杂的核心功能(这样做的话潜在地引入棘手和致命的错误),而是构建在原有KVM上并利用Linux内核中的现有基础架构。虽然独立的虚拟机管理程序设计方法具有更好的性能和更小的可信计算基础(TCB)的潜力,但这种方法在ARM上不太实用。 ARM硬件在很多方面比x86更加多样化。硬件组件通常由不同的设备制造商以非标准方式紧密集成在ARM设备中。例如,许多ARM硬件缺乏硬件发现功能,例如标准BIOS或PCI总线,并且没有建立在各种ARM平台上安装低级软件的机制。但是,几乎所有ARM平台都支持Linux,并且通过将KVM / ARM与Linux集成,KVM / ARM可以在运行最新版本Linux内核的任何设备上自动使用。这与Xen [26]等裸机方法形成对比,后者必须积极支持他们希望安装Xen管理程序的每个平台。例如,对于Xen希望支持的每个新SoC,开发人员必须在核心Xen虚拟机管理程序中实现SoC支持,以支持在串行控制台上简单地输出内容。在ARM平台上引导Xen通常还涉及手动调整引导加载程序或固件,因为Xen需要同时引导两个映像,Xen和Linux Dom0内核映像。另一方面,KVM / ARM与Linux集成,并遵循现有支持的Linux内核引导机制。
虽然KVM / ARM在可移植性和硬件支持方面与Linux集成得益,但我们必须解决的一个关键问题是ARM硬件虚拟化扩展旨在支持独立的虚拟机管理程序设计,其中虚拟机管理程序与任何标准内核完全分离功能,如2.1节所述。 以下部分描述了KVM / ARM的新颖设计如何能够从与现有内核的集成中受益,同时利用硬件虚拟化功能。
2.2.1 分离模式虚拟化
完全在ARM的Hyp模式下运行虚拟机管理程序很有吸引力,因为它专门设计用于运行虚拟机管理程序,并且比用于运行VM的其他CPU模式更具特权。 但是,由于KVM / ARM利用现有的内核基础结构(如调度程序和设备驱动程序),因此在Hyp模式下运行KVM / ARM需要在Hyp模式下运行整个Linux内核。 这有问题至少有两个原因:
首先,Linux中的低级体系结构相关代码被编写为在内核模式下工作,并且在Hyp模式下不能未经修改地运行,因为Hyp模式是与正常内核模式完全不同的CPU模式。 例如,内核的异常进入路径,进程切换逻辑和许多其他内核子系统访问内核模式寄存器,并且必须进行修改才能在Hyp模式下工作。 修改完整的操作系统内核(如Linux内核)以在Hyp模式下运行是非常有争议性的。更重要的是,为了保持与没有Hyp模式的硬件兼容并将Linux作为客户操作系统运行,低级代码必须是兼容在Hyp和内核模式下工作,可能导致缓慢和复杂的代码路径。 举个简单的例子,页面错误处理程序需要获取导致页面错误的虚拟地址。 在Hyp模式下,该地址存储在与内核模式不同的寄存器中。
其次,在Hyp模式下运行整个内核会对本机性能产生负面影响。例如,Hyp模式有自己独立的地址空间。内核模式使用两个页表基寄存器来提供用户和内核虚拟地址空间之间的分割,而Hyp模式使用单页表基址寄存器,因此不能轻易地直接访问用户地址空间。支持来自内核的用户访问将涉及使用来自不同翻译机制的相同页表,这放松了隔离保证并增加了TLB无效频率,或修改了常用功能以访问用户存储器以在软件中行走处理页表并明确地映射用户空间数据进入内核地址空间,随后执行必要的页表拆卸和TLB维护操作。这两个选项都可能导致ARM本机性能不佳,并且难以集成到主线Linux内核中。第4章更详细地探讨了在ARMv8等效于Hyp模式(EL2)中运行Linux的挑战。