架构学习之AArch64虚拟化

不患寡而患不均,不患贫而患不安。        ---- 孔子

本文翻译自文档AArch64 Virtualzation。

1 AArch64虚拟化

        大多数主流操作系统都建立在一个假设上:系统由一个特权OS运行多个非特权OS应用。但是ARM虚拟化可以使能多个OS共存且运行在相同的系统上。实现这些虚拟core要求更精细化的硬件扩展(用来加速虚拟化之间的切换)和hypervisor软件。

        Hypervisor为运行多个操作系统共享单个硬件处理器的程序。虚拟化hypervisor根据设计分为裸机或host。不管怎么分类,hypervisor的功能角色是相同的,平台资源的仲裁,以最小工作量和运行牺牲实现单个guest操作系统的无缝操作。

        在如下图中,对于type1,裸机hypervisor,每个VM包含一个guest OS。对于type2,托管的hypervisor为host OS的扩展,每个子guest OS包含在单个VM中。这里主要由两个开源hypervisor,KVM和XEN。在这个图中,XEN为type1 hypervisor而KVM为type2 hypervisor。

架构学习之AArch64虚拟化_第1张图片

        裸机虚拟化意味着type1 hypervisor可以直接访问硬件资源,它会导致更好的性能。

        Type2 hypervisor首先要求一个OS被安装。Host OS先启动。hypervisor像一个应用一样被安装到OS上。该方法提供了更好的硬件兼容性,因为OS负责硬件驱动而不是hypervisor。零一方面,托管hypervisor不能直接访问硬件且必须通过OS,这会减低VM性能。因为由很多服务和应用运行在host OS上,hypervisor通常在VM运行在上面时使用硬件资源。

        在相同的系统上使用多个操作系统变成可能,但提供的OS通过多个架构特性决定系统的归属:

  1. hypervisor代码执行在EL2
  2. 支持修改core上下文和状态的陷入异常
  3. 支持路由异常和虚拟化中断
  4. 两stage的转换,第二stage对于hypervisor隔离guest操作系统
  5. hypervisor异常调用HVC

        ARMv8-A架构允许使用AArch32或AArch64异常状态的虚拟化。在EL2上的hypervisor可以运行在AArch32或AArch64异常状态。

        当EL2上的hypervisor运行在AArch64时,这里由几个寄存器有用:

  1. 异常返回状态寄存器:SPSR_EL2和ELR_EL2
  2. 栈指针:SP_EL2和SP_EL0

2 Hypervisor软件

        Hypervisor提供的功能不依赖hypervisor实现的类型。它们包括:

  1. 内存管理
  2. 设备模拟
  3. 设备分配
  4. 异常处理
  5. 指令陷入
  6. 管理虚拟异常
  7. 中断控制管理
  8. 调度
  9. 上下文切换
  10. 内存转换
  11. 管理多个虚拟地址空间

2.1 内存管理

        一个普通的OS管理应用运行的内存,OS定位到的内存。hypervisor负责自己内存的管理,同时也负责guest操作系统的内存管理。整个物理内存由hypervisor直接处理。EL2的MMU被hypervisor用于转换虚拟地址,hypervisor使用虚拟地址定位到物理地址。EL2因此有自己独立的vector table。

        使用寄存器VBAR_EL2设置内存中vector table的位置。

        下列代码例子如下:

ADR X0, vector_table

MSR VBAR_EL2, X0

架构学习之AArch64虚拟化_第2张图片

        除了建立和管理自己的转换表,hypervisor必须创建和管理每个guest的stage2转换表。

        例子代码如下所示:

架构学习之AArch64虚拟化_第3张图片

架构学习之AArch64虚拟化_第4张图片

架构学习之AArch64虚拟化_第5张图片

        由hypervisor建立起的stage2转换表将IPA转换为物理内存地址。尝试在stage2转换地址导致的任何abort会陷入到EL2。 

架构学习之AArch64虚拟化_第6张图片

         Hypervisor负责接受abort并合适处理他们。对于故意且合法的fault,hypervisor会采取补救措施如模拟设备或给guest操作系统分配更多内存。对于非预期fault,hypervisor可以选择中止guest操作系统或将abort报告给guest。

2.2 设备模拟

        平台设备是内存映射的,当虚拟化生效时guest访问设备至少要经过stage2转换,在这种情况下存在一些情况hypervisor在软件中模拟设备,并提供一个替代驱动来访问物理设备或虚拟软件设备。虚拟机的模拟设备由软件实现硬件。它们完全在软件中。

        当超过一个guest能够意识到一个平台设备,设备模拟就非常必要,它使用物理地址,并尝试访问。因为共享的性质,guest不被允许直接访问而没有仲裁。在这种情况下,hypervisor用stage2转换表描述符可以控制所有guest对设备区域的访问。

        Guest可以通过读取ID寄存器或设备树中的询问寄存器来检测平台设备。与之前采用相同的陷入机制一样,hypervisor可以返回guest读的dummy值,并忽略写,给guest影响设备不能存在在平台上。

        当模型有相同的数据传递时,hypervisor发起虚拟IRQ(vIRQ)。Guest OS通过尝试读取硬件寄存器进行回复。hypervisor陷入这些访问并提供模拟回复。

        在当前存在的hypervisor方案上,如KVM或Xen,让guest访问平台设备并不常见。这两个hypervisor都提供了虚拟平台并模拟所有设备,异常仅发生在网络或存储控制器,而不是平台设备。

2.3 设备分配

        设备模拟是必要的但也是昂贵的,因为guest所有访问到设备都会被陷入并在软件中被模拟。Hypervisor可以选择将独立的设备分配给独立的guest操作系统因此guest可以拥有并运行该设备而不需要Hypervisor仲裁。

        挑战在于guest需要隐藏设备位于不同的物理地址,并产生guest期望的不同中断ID。相反,hypervisor可能会选择从一组guest中隐藏设备,或因为设备不存在或已经分配给不同guest。

        透明的stage2映射,中断虚拟化设法回避这些挑战。

2.4 异常处理

        异常被陷入,并路由到EL2,最终由hypervisor处理。这些行为可以通过HCR_EL2的控制位进行选择。

架构学习之AArch64虚拟化_第7张图片

         这些位如下所示:

名称

功能

[3]

FMO

控制FIR异步异常是否被路由到hypervisor

[4]

IMO

控制IRQ异步异常是否被路由到hypervisor

[5]

AMO

控制SError异步异常是否被路由到hypervisor

[6]

VF

虚拟FIQ错误。当HCR_EL2.FMO被设置时虚拟FIQ仅被使能

[7]

VI

虚拟IRQ错误。当HCR_EL2.IMO被设置时虚拟IRQ仅被使能

[8]

VSE

虚拟SError/异步abor

[19]

TSC

控制非安全EL1执行的SMC指令是否被陷入

[27]

TGE

对同步异常使能或禁用陷入

        AArch64异常向量表包含四块。到底哪一块被使用依赖于core是否运行在低异常级别(EL0/EL1)还是运行在EL2。

        如果core运行在低异常级别时,EL1的执行状态决定哪个块被使用。如果core运行在EL2时,当前选择栈指针PSTATE.SPSel决定哪个块。

2.5 指令陷入

        除了陷入异常外,hypervisor也可以被配置为陷入某个指令。这很正常,因为指令可能携带地址或由hypervisor表转换的系统地址修改。这也可以通过HCR_EL2配置。

        当某个指令被陷入时,hypervisor代码读取ESR_EL2来获取陷入指令的某些必要信息。

        下列指令可以被陷入:

  1. 虚拟内存控制寄存器的访问,如TTBRn和TTBCR
  2. 系统指令,如cache和TLB维护指令
  3. ACTLR_EL1的访问
  4. 读取ID寄存器
  5. WFE和WFI指令。比如当当前guest OS尝试进入低功耗状态时,这两个指令用于使能hypervisor修改运行的guest OS。

2.6 虚拟异常

        ARMv8-A提供三种虚拟异常的支持:虚拟SError,虚拟IRQ和虚拟FIQ。虚拟异常为event,如由hypervisor人工产生的中断和abort,或响应一个物理异常,或由于设备模拟人为产生的(不需要回复物理异常)。它们以相同的方式回复对应的物理异常。物理异常被配置由hypervisor进行处理。仅虚拟异常被传递给guest。

        当对应的物理异常被路由到运行在EL2的hypervisor时虚拟异常被发出。

        通过写HCR_EL2将它们进行注册,然后如果没有屏蔽,它们会被guest OS获取。

        这意味着hypervisor控制虚拟异常的屏蔽和产生。当一个真实的物理异常产生且被路由到hypervisor软件时,hypervisor可能会运行一些代码,然后发出一个虚拟异常给当前guest OS。Guest OS处理异常就像处理相同的物理异常一样,它并没有意识到hypervisor已经参与。虚拟异常通过虚拟CPU接口被发出,或使用HCR位。当异常被路由到hypervisor时,PSTATE A,I,F位不再屏蔽物理异常,替代它们在guest OS中屏蔽处理虚拟异常。

        在非安全EL0和EL1中,每个虚拟异常由相应的PSTATE屏蔽位进行屏蔽。运行在EL1的代码可以写PSTATE.{A, I, F}。但是如果物理异常被配置为路由到EL3时,hypervisor不再访问且不能产生虚拟异常来回复。

2.7 上下文切换

        当hypervisor调试另一个guest运行在core上时,它必须发出上下文切换,即保存当前运行guest的上下文到内存中,然后从内存中恢复新的guest的上下文。目的是在唤醒之前在当前代码上创建新的guest的环境,仿佛不会中断执行一样。通过发出上下文切换,hypervisor保证执行环境跟随guest,并提供假像guest一直都占用虚拟core。

        下列Guest上下文必须被保存和恢复:

  1. 代码的通用寄存器包括各个模块的bank寄存器
  2. 为内存管理和访问控制的系统寄存器内容
  3. core上的私有中断的pending和active状态
  4. 如果guest使用core私有timer,timer寄存器必须被保存和恢复因此它们可以在期望的间隔产生中断

        物理内存被分配给guest,然后guest可以看到RAM,它保存且不需要保存或恢复。通过使用内存转换的两个阶段,guest使用的物理内存保持私有且与众不同,即使对于相同的guest。

2.8 内存转换

        转换机制是一个广泛的术语,包括core和转换表的特权和异常级别。在Normal world的虚拟化系统中可能使用多个内存转换机制。

        在这种情况下的转换为基于内存表的内存访问。转换stage包含一组将输入地址转化为输出地址的转换表。输入地址或虚拟地址由编译器和链接器使用,将代码放在内存中。输出地址或物理地址由实际系统硬件使用。

        这些转换使用MMU进行,且转换表结构由软件创建来控制转换。输入输出地址根据转换的stage有不同的名称。

        hypervisor控制两种转换。当代码执行在EL0或EL1时,转换由OS建立和控制。在没有虚拟化的系统中,该机制用于将虚拟地址转化为物理地址。在虚拟化系统中,物理地址被当作IPA,因为它会被放到stage2。

        在stage2存在时,第一级转换被称为stage1。IPA不能用于定位系统内存。虽然被称为IPA,从guest角度来看,该转换输出的即为物理地址。

架构学习之AArch64虚拟化_第8张图片

        转换的第二级使用VTTBR_EL2和VTCR_EL2,它们由hypervisor控制:

          对于hypervisor的虚拟地址空间,使用单个stage转换,由TTBR0_EL2和TCR_EL2控制:

架构学习之AArch64虚拟化_第9张图片

        ARMv8-A虚拟化也引入VMID的概念。每个虚拟机分配一个VMID,它为8位值,保存在VTTBR_EL2中。

        架构不会定义在硬件上VTTBR_EL2.VMID被复位成什么值。这意味着运行在每个active core的引导代码软件必须初始化VTTBR_EL2.VMID为已知值,比如为0,即使转换的第二级不再使用。

你可能感兴趣的:(Learn,the,architecture,虚拟化)