虚拟化技术概述
虚拟机特性:
- 指令模拟 (guest 和 host 可以是不同的isa)
- 本地指令直接执行( guest 和 host isa是相同的)
系统资源虚拟化:
- cpu虚拟化
- 内存虚拟化
- io 虚拟化
常见的虚拟机软件:
- vmware
- virtual box
- xen
- linux-kvm qemu
Hypervisor Virtual Machine Manager(VMM) 的功能
- 控制所有的系统资源(CPU 内存 网络 存储等)
- 创建虚拟机并分配响应的资源
- 管理虚拟机的生命周期
VMM 调度程序和操作系统的调度进程类似, 操作系统调度的基本单位是进程/线程, VMM调度的单位是虚拟处理器. 当虚拟cpu被调度到时, VMM调度程序负责将vcpu上下文装载到物理处理器上, 然后vcpu所对应的guest os 指令开始真正被执行. 当时间片用完或虚拟处理器主动让出, 调度程序被触发. 调度程序根据调度策略, 挑选下一个vcpu继续运行.
与操作系统一样, VMM的调度策略可以有多种, 如平均时间片策略, 或按vcpu的权重分配时间片进行调度等.
虚拟机之间可以通信, VMM需要实现对应的通信机制, 并向虚拟机提供对应的api(可以是时间通知, 共享内存等), 需要严格的安全权限检查.
虚拟机环境管理包括创建/删除/暂停/查询/迁移等, 由虚拟机管理软件和VMM 管理接口组成.
物理资源的管理
- 处理器管理
包括系统启动检测获取所有物理处理器, 对每个处理器进行初始化, 如设置运行模式, 设置页表, 设置中断处理函数等; 将所有的处理器纳入调度序列, 由调度程序对其进行调度. 还支持hot plug, 当有处理器插入时, vmm获得通知, 将其纳入调度序列. 当处理器拔出时, vmm 得到通知, 将该处理器上执行的任务迁移到其他处理器上, 并将其从管理队列中删除.
云服务存在多个处理器节点, 当某些节点出现故障时, 其上运行的guest os 不会宕机, 而是转移到其他正常工作的cpu节点上;
同样为了安全的扩充处理器资源, 在不断电的基础上进行cpu的扩充进而降低系统负载, 对服务器来说也是必要的;
2. 内存管理
系统启动时VMM检测并获取所有内存, 对获得的内存进行初始化, 包括分页设置页表等; 提供内存分配的接口, 给虚拟机分配内存, 并且维护虚拟机物理地址和实际物理地址的映射关系
3. 中断管理
根据中断来源, 或直接处理, 或转发给guest os 处理
4. 系统时间维护
VMM 拥有和时间相关的硬件资源, VMM 负责维护系统时间, 同时向各guest os 提供虚拟化的时间
5. 设备管理
所有的外设都属于VMM, VMM需要包含所有设备的驱动程序. 在混合模型下, 大部分的外部设备属于guest os, 少部分的设备属于VMM
虚拟化的优点:
- 更高的系统安全性和可用性
- VMM 作为监视层, 运行在比os更高的特权层
- 控制过滤虚拟机的行为
- 监控虚拟机状态, 故障快速恢复
- 最大化硬件资源使用率
- 在一个物理主机上创建多个虚拟机共享主机资源, 节约硬件成本
- 系统易扩展
- 修改虚拟机的配置来适应业务的负载变化
- Aggregation 聚合技术
- 方便的可移植性
- 虚拟机的系统消除物理主机的硬件差异
- 虚拟机以文件镜像的格式封装
- 硬件级别的隔离特性
- 硬件沙盒机制
不同类型的VMM:
baremetal vmm (type1)
启动时bootloader/BIOS 直接将执行权限交给hypervisor
直接运行在硬件上, 不依赖基础操作系统
可以控制所有的guest os
交互少, 性能好, 稳定
无法直接利用现有操作系统生态, 硬件兼容性差, 驱动开发工作量大
VMM根据产品定位, 有选择的挑选一些io 设备来支持, 如面向服务器市场, 只会挑选服务器上的io设备来开发驱动, 另外调度和电源管理等的很多功能需要在VMM中重新实现
典型代表: Xen
hosted vmm (type2)
- 启动时bl/BIOS 先启动host os, hypervisor/VMM 相当于host os中跑的一个应用
- 需要通过host os 来访问硬件资源
- vmm 只能控制guest os, 不能控制host os中的其他部分
- guest os 和 host os 交互调用链长, 影响性能
- 攻击窗口多, 安全性差
- 可以直接利用现有操作系统生态, 硬件兼容性好
- 典型代表: linux kvm
KVM的思想是在Linux内核的基础上添加虚拟机管理模块,重用Linux内核中已经完善的进程调度、内存管理、IO管理等代码,使之成为一个可以支持运行虚拟机的Hypervisor
混合模型
上述两种模式的集合体, VMM依然位于最底层, 拥有所有的物理资源, 与type1 模式不同的是, VMM会让出大部分io设备的控制权, 将它们交给guest os 控制, 相应的, VMM虚拟化的职责被分担, 处理器和内存的虚拟化仍由VMM 完成, 而IO虚拟化则由VMM和guest os 合作完成.
Hypervisor 的实现
没有硬件虚拟化技术之前,Hypervisor 的实现主要有两种,一种是半虚拟化,一种是全虚拟化
半虚拟化:
- 将guest os 降权, 使其无法直接访问系统特权资源
- vmm 提供访问系统特权资源的hyper call api
- 修改guest os, 用hyper call api 访问系统特权资源
- 高效轻量, 性能好
- guest os 修改量大, 使用不便
全虚拟化:
- 将guest os 运行在vmm 创建的独立环境里
- vmm 将内核特权访问操作翻译成一系列对vmm 的请求 (软件方案)
- guest os对虚拟化环境不感知, 不需要修改guest os
- vmm 实现复杂
- vmm和guest os 之间翻译产生的负载比较大, 性能差
硬件虚拟化 (简化hypervisor, 提高性能)
- intel VT-x VT-d
- AMD svm
- ARM smmu等
CPU 虚拟化
虚拟CPU上下文
- 类似于进程上下文的概念,当虚拟机发生退出时,需要保存虚拟CPU中各寄 存器的状态
- 发生虚拟CPU调度时,需要保存当前虚拟CPU的上下文并加载待调度虚拟 CPU上下文
软件方案
不同指令集架构上的软件解决方案
解释执行
解释器将程序二进制解码后调用指令相应的模拟函数
采用软件模拟的方式逐条模拟虚拟机指令的执行
二进制翻译
区别于解释执行技术, 二进制翻译技术以基本块为单位, 将虚拟机指令批量 翻译后保存在代码缓存中
基本块中的敏感指令会被替换为一系列其他指令
相同指令集架构上的软件解决方案
扫描与修补
x86 虚拟化技术早期, VMM 运行在特权级, guest os运行在非特权级(用户态), 这种方式称为特权级压缩(Ring Compression. guest os 上内核运行特权指令时, 通常会触发异常, 进入特权级, 由VMM 截获异常并进行处理, 但是有一些非特权敏感指令
并不会触发异常, 这种状态下, VMM就需要扫描guest os的内核的所有的这些不会触发异常的敏感指令, 将其翻译成支持虚拟化的指令(会触发异常的指令), vmm 再去处理这些翻译后指令触发的异常.
修改系统资源的,或者在不同模式下行为有不同表现的,都属于敏感指令
硬件辅助方案
软件方案是非常低效的, 且vmm 实现过于复杂, Intel VT-x、AMD SVM、ARM EL2、RISC-V H-Extension, 加入了一层特权级别, guest os 运行在这一层上, 这一层上所有特权指令均会触发异常, 可以被VMM截获处理
中断虚拟化
中断控制器的模拟
需要为每一个虚拟机维护一个虚拟中断控制器
物理主机, 设备中断处理流程
- 设备产生一个水平/边缘触发中断信号
- 中断控制器响应该信号, 让cpu el1 进入中断异常模式
- os 调用中断服务程序, 通过irq number 找到对应驱动中断处理函数
- 完成中断处理
虚拟化系统中, 设置中断处理流程
- 设备产生一个水平/边缘触发中断信号
- 中断控制器响应该信号, 让cpu el2 进入中断异常模式
- CPU 调用VMM 中断服务程序, 通过IRQ number 找到对应的guest os, 通过
中断注入程序
向guest os 注入virtual irq - guest os el1 cpu进入中断异常模式
- guest os 调用中断服务程序, 通过virtual irq number 找到对应的驱动中断处理函数
- 完成中断处理
内存虚拟化
常见的内存虚拟化技术
- 地址空间分区
- 简单, 但不灵活
- 半虚拟化 shadow page table
- 虚拟地址模拟物理地址, 性能好, 需要修改guest os
- mmu 二级地址转换
- 通过mmu硬件地址翻译
- 对guest os 保持透明, 不需要修改原有mmu 代码
- 运行时软件不介入翻译
硬件虚拟化- 二级地址翻译
实际上是扩展了影子页表技术, 硬件辅助化的措施: 新增寄存器,供硬件MMU读取, 由硬件mmu 完成二级页表翻译, 避免由软件翻译
- IPA (intermediate physical address) guest 物理地址
- 虚拟机物理地址空间由IPA 描述, 不直接指向真实物理地址
- 每个VM的IPA地址独立, 可以相同, 可以重叠, 也可以完全不同
- 通过两级地址翻译找到真实物理地址
- VA->IPA (guest os)
arm64 使用TTBRn_EL1寄存器和页表, riscv 使用stap寄存器 - IPA->PA (VMM)
arm64 使用VTTBR_EL2寄存器和stage2 页表, riscv 使用hgatp 寄存器
- VA->IPA (guest os)
各指令集架构内存硬件虚拟化辅助新增的寄存器
Intel:EPT,起始地址:EPTP(in VMCS)
AMD:NPT
ARM:Stage-2 页表,起始地址:VTTBR_EL2
RISCV: Stage-2 页表,起始地址:hgatp
IO 模拟
在没有虚拟化的系统中,由BIOS或者操作系统通过遍历PCI总线上的所有设备完成设备发现过程,而在虚拟化系统中,则由VMM决定向虚拟机呈现哪些设备。具体过程要根据设备是否存在于物理总线上来进行。对于一个真实存在于物理总线的设备,如果是不可枚举的类型,例如PS/2键盘,由于这类设备是硬编码固定的,驱动程序会通过其特定的访问方式来检查设备是否存在,因此VMM只要在相应端口上模拟出该设备,虚拟机即可成功检测到它;如果是可枚举的类型,譬如PCI设备或者PCIe设备,这种设备通常定义了完整的设备发现方法,并允许BIOS或者操作系统在设备枚举过程中通过PCI配置空间对其资源进行配置。因此VMM不仅要模拟这些设备本身的逻辑,还要模拟PCI总线的一些属性,包括总线拓扑关系及相应设备的PCI配置空间,以便虚拟机OS在启动时能够发现这些设备
所谓设备模拟就是模拟设备的功能,内容十分多样且复杂。对于像PS/2键盘、鼠标这样的设备,VMM需要根据设备的接口规范模拟设备的所有行为,才能够无需修改驱动就在虚拟机上展现出设备应有的效果。而对于磁盘存储系统,则不必受限于实际的磁盘控制器以及具体磁盘类型和型号。比如,对IDE硬盘其I/O端口虚拟化时,底层可以是一块磁盘,可以是一个分区,也可以是不同格式的文件; 然后在其上实现一个专门的块设备抽象层; 最后在块设备上使用文件系统,并引入一些真实硬件没有的高级特性,例如:加密、备份、增量存储等。
系统的物理设备需要同时接受来自多个虚拟机的I/O请求。因此,VMM还要将多个虚拟机的I/O请求合并为单独一个I/O数据流发送给底层设备驱动。当VMM收到来自底层设备驱动完成I/O请求的中断时,VMM还要能够将中断响应结果转发给正确的虚拟机,以通知其I/O操作结束。同时VMM在调度各个虚拟机发送来的I/O请求处理时,必须依据一定的算法确保虚拟机I/O的QoS与设备共享的公平性。
对于type2 类的拥有完整生态的操作系统, vmm 作为一个内核模块, 客户机发生io后, vmm将其拦截后, 会通过用户态接口传给用户态的设备模型处理, 设备模型运行在host os 上的用户态层, 可以使用相应的系统调用及运行库. host os及运行库, 构成了设备模型的运行环境
而对于type1 类的baremental os, 通常不存在用户态, 设备模型是位于虚拟机设备驱动程序和实际设备驱动之间的一个模块, 由guest os发过来的io请求先通过设备模型模块转化为物理io设备请求, 在通过调用物理设备驱动来完成相应的io操作. 真实的设备驱动将io操作结果通过设备模型模块返回给guest os的虚拟设备驱动.
type2 类 用户态设备模型:
QEMU/KVM
用户态设备模型, 运行库生态健壮, 可复用性高
多次上下文切换
type1 类 baremental 设备模型:
xvisor xen
减少了多次上下文切换
缩短io模拟路径
移植性差
设备类型
- 端口io
通过特定指令访问设备相关寄存器 (x86 专用的端口访问指令,IN/OUT) - MMIO
特定物理内存区域映射了设备的寄存器, os通过页表以访问内存的方式访问设备寄存器, RISCV 仅支持MMIO - DMA
无需CPU控制, DMA控制器接管地址总线 - PCI
或端口io方式或 MMIO方式,不占用固定地址-> 热插拔
IO 虚拟化基本任务
- 访问截获
- 提供设备接口
- 虚拟设备接口, 如暴露虚拟pci 设备
- 直通设备接口, 如intel VT-d 技术
- 对虚拟机完全“透明”
- 实现设备功能
- type1 类baremental VMM 需要实现设备驱动, 设备模型;
- type2 类需要实现用户态设备模型 运行库等
端口IO 模拟
虚拟机陷入过程
- IN/OUT、INS/OUTS -> VM-Exit
- 保留端口号、访问数据宽度、数据传输 方向、数据传输方向
VMM 处理过程:
- I/O端口对应的处理函数在设备模型初始 化时会被注册到hypervisor中
- 函数指针被组织成数组
- 根据I/O端口号和访问数据宽度寻找相应 端口IO处理函数
MMIO模拟过程
虚拟机陷入过程
- 访存指令,非特权指令
- 页表不存在相应页表项 -> 缺页异常 -> 陷入VM
Hypervisor中的处理
- MMIO内存区域较大,通常不采用PIO中的函数数组形式
- 为MMIO区域注册一个MMIO处理函数
- 处理函数定位到需要访问的I/O端口
DMA 模拟
- guest 驱动程序配置DMA 相关寄存器, 源地址 目的地址 (GPA) 及 长度
- 陷入VMM, 设备模型用VMM 提供的
内存管理功能
将源地址目的地址 (GPA) 翻译为 HPA, 配置物理DMA 的地址寄存器为HPA, 同时需要建立HPA 与 HVA的映射关系, 对其进行占位, 防止别的进程或vcpu把这块物理内存给抢走. 这个地方纯软件实现比较复杂, 涉及到映射给 guest 的虚拟设备地址与真实设备地址之间的转换, 如果为memory->memory, 会简单一些 - guest 客户机驱动程序通过配置虚拟DMA的 命令控制寄存器发起 DMA操作
- 陷入VMM, 设备模型截获这个操作后, 配置物理DMA命令控制寄存器
- DMA 自行在HPA 间搬运数据
- DMA 搬运完毕后, 通过中断通知vmm 设备模型, vmm设备模型返回到guest os中, DMA 请求结束
设备直通
软件实现I/O虚拟化的技术中,所有的虚拟机都共享物理平台上的硬件设备。如果物理条件好,有足够的硬件,就可以考虑让每个虚拟机独占一个物理设备,这样无疑会提高系统的性能。把某一个设备直接分配给一个虚拟机,让虚拟机可以直接访问该物理设备而不需要通过VMM或被VMM截获,这就是设备直通技术。
intel 的 VT-d与AMD的IOMMU技术 arm的smmu 技术。尽管这两种技术在一定程度上提高了I/O访问性能,但代价却是限制了系统的可扩展性
- 不发生或发生少量陷入VM
- 性能接近裸机
ARM 硬件虚拟化技术:
CPU 特权层扩展
- EL2 层(arm64) / HYP (arm32) 模式下运行vmm
- guest os 运行在原有的特权模式, 不需要修改guest os
- vmm所在层权限更高, 可以控制guest os 访问硬件的权限
模式切换
虚拟机 → Hypervisor
- EL1 → EL2
- 敏感指令触发(可通过HCR_EL2寄存器细粒度 控制)
Hypervisor → 虚拟机
- EL2 → EL1
- eret指令触发
上下文切换
- EL1与EL2各自有一套系统寄存器
- 虚拟CPU调度时,需要将原虚拟CPU系统寄 存器保存至内存并从内存中加载目标虚拟 CPU寄存器
因为有了硬件虚拟化的支持,所以hypervisor的实现 基本是基于硬件的 trap 和 软件的emulator 来实现的。guest os 访问一些特权寄存器或者指令,会进到 hypervisor ,然后会调用特权寄存器的访问函数来访问特权寄存器。如果是要访问硬盘,或者网络,会通过io 模拟器,来访问具体的模拟器。
CPU的虚拟化,就是让多个Guest os 分时的运行在同一个CPU上,都有自己独立的物理地址空间,让 hypervisor在EL2 层来帮助多个VM 来进行上下文的切换,这个和linux 进程的概念非常的相似,不过保存的上下文寄存器不一样,这里有两个重要的寄存器,HCR_EL2
和 ESR_EL2
。HCR_EL2 是用来配置VM的参数,就是产生trap的条件,什么情况下会产生trap , 什么情况下不会产生trap,右边是一个运行两个VM的例子。WFI指令是说明自己工作做完了,是idle状态了。
ARM CPU虚拟化通过硬件trap和软件模拟完成
- HCR_EL2 hyper配置寄存器
- 配置vm产生硬件trap的条件
- 有非常丰富的组合, 如TLB/cache的操作, 一些特殊指令
- ESR_EL2 异常寄存器
- 当trap发生时, 确定vm产生硬件trap的原因
执行特权指令示例:
与特权级无关的一般的指令和通用寄存器在任何特权级都可以任意执行。而每个特权级都对应一些特殊指令和 控制状态寄存器 (CSR, Control and Status Register) ,来控制该特权级的某些行为并描述其状态。当然特权指令不只是具有有读写 CSR 的指令,还有其他功能的特权指令。
如果低优先级下的处理器执行了高优先级的指令,会产生非法指令错误的异常,于是位于高特权级的执行环境能够得知低优先级的软件出现了该错误, 进而陷入到高特权级处理该指令
访问特定寄存器示例:
使用陷入来虚拟化操作需要大量计算。比如功能寄存器ID_AA64MMFR0_EL1,不经常被操作系统访问。当将对这些寄存器的访问捕获到虚拟机监控程序中以模拟读取时,计算是可以接受的。
但是对于访问频率高的寄存器,比如MPIDR_EL1,或者在性能关键代码中,需要尽可能地优化陷入,对这些寄存器,ARM提供了其它策略,hypervisor可以在进入VM时先配置好这些寄存器的值。例如,当VM中读到MPIDR_EL1
时会自动返回VMPIDR_EL2
的值而不发生陷入
MMU虚拟化支持
- LPAE(arm32 大地址拓展技术) , stage2 translation
物理直通设备MMIO
阶段-2页表包含物理空间地址 与虚拟机IPA之间的映射
不触发VM-Exit
虚拟设备MMIO
阶段-2 缺页异常
- 将IPA地址填充到HPFAR_EL2寄存器中
- 访问相关信息[Read,4 bytes, x0],填充到ESR_EL2寄存器中
调用emulate_access函数,完成 MMIO的模拟
ERET指令将控制流返回给vCPU
GIC 虚拟化
- vcpu interface, hypervisor interface
虚拟CPU接口直接访问
- 将CPU接口置于CPU内部,通过寄存器访问
- 为虚拟CPU接口提供专用寄存器(ICV_*),区别于物理CPU接口
- HCR_EL2.IMO和HCR_EL2.FMO设置为1时,运行在EL1中的Guest OS对CPU接口系 统寄存器(ICC_*)的访问将被重定向到相应的ICV_*寄存器而不会触发虚拟机陷入
虚拟中断注入
- GICv3配置使得所有的物理中断路由到EL2
- Hypervisor检查中断目标是否为vCPU
- 根据
ICH_LRn_EL2
寄存器中保存的虚拟中断信息向虚拟机中注入中断
虚拟LPI中断直接注入
- GICv3引入组件中断翻译服务
ITS
,该组件通过查询若干内存表将物理中断转化为 虚拟LPI中断注入相应的虚拟机
arch_timer 虚拟化
- hypervisor timer, virtual timer
时钟虚拟化要为guest os提供两种设备资源:
- 计时资源:clock_source设备
- 时钟定时器: clock_event设备
对于clock_event设备,ARM64平台每个处理器有4个timer,3个物理timer和1个虚拟timer。物理机用物理timer,虚拟机用虚拟timer,相互之间并无资源冲突
对于clock_source设备,按照ARM64的timer的设计,有物理counter和虚拟counter,物理机使用物理counter,虚拟机使用虚拟counter
smmu
stage2 translation for DMA
一种DMA重映射机制
扩大设备DMA寻址范围, 当系统无法提供大块连续物理内存时,也可以通过SMMU转换让设备可以访问分散物理内存
IOMMU在ARM-V8架构下的解决方案,与VT-d类似
SMMU与MMU共用一套阶段-2页 表
设备直通
- 设备透传就是由虚机直接接管设备,虚机可以直接访问MMIO空间,VMM配置好IOMMU之后,设备DMA读写请求也无需VMM介入
为每个虚拟机划定可用的设备, 起到隔离保护作用
物理机DMA 示例
- 网卡模块将数据从物理地址填入网卡DMA 寄存器
- DMA模块通过物理地址访问物理内存
虚拟机网卡如何进行DMA?
网卡驱动将数据的guest 物理地址填入网卡DMA 寄存器
通过IPA 访问总线会产生故障
- 调用hypervisor api 直接转换VA->PA
缺点: 修改驱动, API 调用产生额外性能开销 - 为外设添加额外虚拟化支持
不需要修改驱动, 驱动无感知, 无额外api调用性能开销
SMMU 为系统除CPU 之外的任何具有DMA 能力的设备提供地址翻译服务和保护功能- PCIE DMA 设备
- platform DMA 设备
- GPU/VE 加速器
- 调用hypervisor api 直接转换VA->PA
SMMU for DMA 概述
流表:每个流表项对应一个设备
- VMID:设备所属虚拟机
- S2TTB:阶段-2转换页表基地址
- S1上下文指针:指向阶段-1上下文描述符表(CD 表)
CD表:
- TTB0 TTB1:分别保存用户空间和内核空间阶段-1页表
- ASID:用于标记进程的地址空间
SMMU从I/O 事务中获取设备标识符,即 StreamID
SMMU从SMMU_STRTAB_BASE
寄存器中获取流表
的基地址,并通过StreamID
获取对应的STE
(流表项)
在开启阶段-1转换的情况下,通过SubstreamID
定位到对应的CD
,进而获取ASID
和阶段-1页表 基地址
。在开启阶段-2转换的情况下,在STE
中获取VMID
和阶段-2页表基地址
以及Stream World配置信息
SMMU根据DMA地址、ASID、VMID、Stream World 查 询TLB。如果TLB命中,可以直接获得目标物理地址以及访问权限信息。如果TLB未命中,通过相应地址翻译过程获得对应的目标物理地址, 并将映射关系填充到TLB中
设备根据目标物理地址进行数据传输
RISCV 虚拟化技术
已有的RISC-V虚拟化软件实现
目前已有的实现有Xvisor和KVM,Xvisor是1类虚拟化软件,而KVM属于2类。
RISC-V规范定义了RISC-V H-extension,在原来的3级特权架构的基础上,对原有的Supervisor模式进行了扩展,引入了Hypervisor-Extended Supervisor mode (HS)
虚拟化H扩展定义了一个硬件状态位,称作V状态,可以为0或1,V状态不同,定义和访问的CSR寄存器也不同。
- 当V为0时
- 以“s”开头的CSR寄存器表示当前操作系统的状态
- “hs”开头的用于支持和实现虚拟化软件
- “vs”开头的代表运行在虚拟化技术上的系统状态。
- 当V为1时
- “s”开头的寄存器指向了前文以“vs”开头的寄存器。
模式切换
- 虚拟机 → Hypervisor
- VS mode → HS mode(先进入M mode,再由M mode 转发给HS mode)
- 敏感指令触发
- Hypervisor → 虚拟机
- HS mode → VS mode
- sret指令触发
上下文切换
- 为VS-mode提供VS CSR
- 虚拟CPU调度时,同样需要从内存中保存和加载相应 的寄存器
In HS-mode (V=0):
- s 开头的寄存器含义不变
- h开头的寄存器指示hypervisor 的能力
- vs 开头的寄存器指示vs 模式下的状态 (context switch时使用)
VS-mode(V=1): - s开头的寄存器指向 vs 开头的寄存器
2级mmu 地址转换
使用stap 与hgatp
寄存器完成2级 地址转换, 原理一样
va->ipa (guest os) satp
ipa -> pa (host os) hgatp
中断
hedeleg 虚拟异常代理寄存器
hideleg 虚拟中断代理寄存器
默认状态下, 在各级代理寄存器未设置时, 所有的trap 和 中断都被指向到 M 模式的trap (即mtvec 指定的入口函数处), 在指定了 medeleg
和 mdieleg
后, 相应bit位的trap 和 中断 指向到 HS 模式的trap (即stvec 指定的入口函数处), 进一步, 在指定了 hedeleg
和 hideleg
后, 相应bit位的trap 和 中断指向到 VS 模式下的trap(即vstvec 指定的入口函数处)
hedeleg 中, 9-11 bit, 20-23 bit 是readonly的, 只能是0
第0, 3, 8, 12, 13, 15 为推荐设置bit
hideleg 中, 0-15 中, 只有10, 6, 2 能被设置. 当hideleg中, bit 10被设置后, 10号中断来了后, 被代理到VS-mode后, code 10 会被自动转换为 code 9; 同样的 6号中断被自动转换为 5, 2号中断被自动转换为 1号. 这样做的目的是为guest os 中的 kernel 不用进行额外的修改来适配虚拟机.
除此之外, HS模式可以使用hvip
寄存器,来向VS模式注入虚拟的中断。
Hypervisor Status Register (hstatus)
When VTSR=1, an attempt in VS-mode to execute SRET raises a virtual instruction exception.
When VTW=1 (and assuming mstatus
.TW=0), an attempt in VS-mode to execute WFI raises a virtual instruction exception if the WFI does not complete within an implementation-specific, bounded time limit.
When VTVM=1, an attempt in VS-mode to execute SFENCE.VMA or to access CSR satp
raises a virtual instruction exception.
The VGEIN (Virtual Guest External Interrupt Number) field selects a guest external interrupt source for VS-level external interrupts. VGEIN is a WLRL field that must be able to hold values between zero and the maximum guest external interrupt number (known as GEILEN), inclusive.
When VGEIN=0, no guest external interrupt source is selected for VS-level external interrupts. GEILEN may be zero, in which case VGEIN may be hardwired to zero.
Guest external interrupts are explained in Section 1.2.4, and the use of VGEIN is covered further in Section 1.2.3.
Field HU (Hypervisor User mode) controls whether the virtual-machine load/store instructions, HLV, HLVX, and HSV, can be used also in U-mode. When HU=1, these instructions can be executed in U-mode the same as in HS-mode. When HU=0, all hypervisor instructions cause an illegal instruction trap in U-mode.
hip & hie
Registers hip
and hie
are HSXLEN-bit read/write registers that supplement HS-level’s sip
and sie
respectively. The hip
register indicates pending VS-level and hypervisor-specific interrupts, while hie
contains enable bits for the same interrupts. As with sip
and sie
, an interrupt i will be taken in HS-mode if bit i is set in both hip
and hie
, and if supervisor-level interrupts are globally enabled.
Bits hip
.SGEIP and hie
.SGEIE are the interrupt-pending and interrupt-enable bits for guest external interrupts at supervisor level (HS-level).
SGEIP is read-only in hip
, and is 1 if and only if the bitwise logical-AND of CSRs hgeip
and hgeie
is nonzero in any bit.
Bits hip
.VSEIP and hie
.VSEIE are the interrupt-pending and interrupt-enable bits for VS-level external interrupts. VSEIP is read-only in hip
, and is the logical-OR of these interrupt sources:
- bit VSEIP of
hvip
; - bit of
hgeip
selected byhstatus
.VGEIN - any other platform-specific external interrupt signal directed to VS-level.
Bits hip
.VSTIP and hie
.VSTIE are the interrupt-pending and interrupt-enable bits for VS-level timer interrupts. VSTIP is read-only in hip
, and is the logical-OR of:
hvip
.VSTIP- any other platform-specific timer interrupt signal directed to VS-level.
Bits hip
.VSSIP and hie
.VSSIE are the interrupt-pending and interrupt-enable bits for VS-level software interrupts. VSSIP in hip
is an alias (writable) of the same bit in hvip
.
Hypervisor Guest External Interrupt Registers (hgeip
and hgeie
)
hgeip indicates pending guest external interrupts for this hart.
hgeie contains enable bits for the guest external interrupts at this hart.
Guest external interrupts represent interrupts directed to individual virtual machines at VS-level.
只有在设备直通场景下使用
interrupts from the device are intended for a specific virtual machine. Each bit of hgeip
summarizes all pending interrupts directed to one virtual hart, as collected and reported by an interrupt controller. To distinguish specific pending interrupts from multiple devices, software must query the interrupt controller.
Support for guest external interrupts requires an interrupt controller that can collect virtual-machine-directed interrupts separately from other interrupts.
中断控制器需要收集virtual guest external interrupt, 和 物理cpu的external interrupt 区分开, guest 只能接收virtual external guest interrupt
The number of bits implemented in hgeip
and hgeie
for guest external interrupts is GEILEN (hstatus.VGEIN)
bits GEILEN:1 shall be writable in hgeie
, and all other bit positions shall be hardwired to zeros in both hgeip
and hgeie
.
Guest external interrupt number i at one physical hart is typically expected not to be the same as guest external interrupt i at any other hart.
For any one physical hart, the maximum number of virtual harts that may directly receive guest external interrupts is limited by GEILEN.
A hypervisor is always free to emulate devices for any number of virtual harts without being limited by GEILEN. Only direct pass-through (direct assignment) of interrupts is affected by the GEILEN limit, and the limit is on the number of virtual harts receiving such interrupts, not the number of distinct interrupts received. The number of distinct interrupts a single virtual hart may receive is determined by the interrupt controller.
The enable bits in hgeie
do not affect the VS-level external interrupt signal selected from hgeip
by hstatus
.VGEIN.
vsip & vsie
hideleg not zero, vsip
.SEIP and vsie
.SEIE are aliases of hip
.VSEIP and hie
.VSEIE.vsip
.STIP and vsie
.STIE are aliases of hip
.VSTIP and hie
.VSTIE.vsip
.SSIP and vsie
.SSIE are aliases of hip
.VSSIP and hie
.VSSIE.
小结
hideleg 开启时,
- vsip.SEIP = hip.VSEIP vsip.STIP = hip.VSTIP vsip.SSIP = hip.VSSIP
- VSEIP is read-only in
hip
, and is the logical-OR of these interrupt sources:- bit VSEIP of
hvip
; - bit of
hgeip
selected byhstatus
.VGEIN - any other platform-specific external interrupt signal directed to VS-level.
- bit VSEIP of
- VSTIP is read-only in
hip
, and is the logical-OR of:hvip
.VSTIP- any other platform-specific timer interrupt signal directed to VS-level.
- VSSIP in
hip
is an alias (writable) of the same bit inhvip
.- 即 vsip.SSIP = hip.VSSIP = hvip.VSSIP
- VSEIP is read-only in
- vsie.SEIE = hie.VSEIE vsie.STIE = hie.VSTIE vsie.SSIE = hie.SSIE
真正影响guest os的是vsip , guest os 运行时处于V=1 mode, vsip被替换为sip,
- vsip software 中断来自于hvip (来自于HS hypervisor), 或者guest os 自己.
- vsip timer 中断来自于hvip (来自于 HS hypervisor), 或guest os 自己, 或 platform-specific timer interrupt signal directed to VS-level
- vsip guest external 中断来自于hvip (来自于HS hypervisor), 或hstatus.VGEIN -> hgeip 或 any other platform-specific external interrupt signal directed to VS-level
下面这两句应该怎么理解?
- Bits
hip
.SGEIP andhie
.SGEIE are the interrupt-pending and interrupt-enable bits for guest external interrupts at supervisor level (HS-level). - Bits
hip
.VSEIP andhie
.VSEIE are the interrupt-pending and interrupt-enable bits for VS-level external interrupts.
直译:
SGEIP 和 SGEIE 是给HS 用的, 重点看 SGEIP, 这个是hgeip & hgeie and的结果, 代表的有guest external 中断待处理.
hip.VSEIP 和 hip.VSEIE 是给VS 用的, 这个好理解, 这两个直接反应到vsip的相应bit上了.
结合这句:
if GEILEN is nonzero, bits GEILEN:1 shall be writable in hgeie
Only direct pass-through (direct assignment) of interrupts is affected by the GEILEN limit, and the limit is on the number of virtual harts receiving such interrupts, not the number of distinct interrupts received
大概猜测, 在设备直通场景时, 0 - hstatus.VGEIN - GEILEN(物理cpu上托管的vcpu的数量), 不同的vcpu可以运行不同guest os.
一个物理cpu 有三套寄存器
- v 开头的 vsip vsie 等
- h 开头的 hvip hip hie hgeie hgeip 等
- s 开头的 sip sie 等
当V=0 -> V=1 时, v开头的寄存器会替换成 s 开头的寄存器, 此时变成vcpu的执行环境
V=1 时, 只能访问 s 开头的寄存器.
需要HS vmm 对 hgeie hstatus操作, 对应物理cpu上托管的vcpu
如当前物理cpu上托管了8个vcpu, 正在运行的是第2个vcpu
- 对 hstatus.VGEIN 设置为2
- 对 hgeie 的前8个bit 置1, 表示物理cpu 管了8个vcpu, 这8个vcpu都要处理guest external interrupt.
在前面前提下, 硬件需要将hip.VSSIP 与 hgeip的状态区分.
前面hip.VSSIP 的来源处说的比较模糊: bit of hgeip
selected by hstatus
.VGEIN
不太好理解, 大概猜测:
PLIC 在设备直通场景(设置了hstatus.VGEIN时), guest 外部中断源需要区分给哪个vcpu, 导致的直接结果就是要设置hgeip 的哪个bit, 同时硬件应该将 hip.VSEIP 置为hgeip 与 hstatus.VGEIN 逻辑与 的结果. 而hip.SGEIP 置为 hgeip & hgeie 逻辑与的结果.
虚拟机接管, 因为vsip.SEIP = hip.VSEIP, 假如中断控制器要发给第三个vcpu, 就需要将hgeip 的第三个bit 置1, 则hip.VSEIP = 0, 而 hip.SGEIP 为 1, 表明有待处理的guest external interrupt.
在第二个vcpu 因tick到期 退出到hypervisor vmm 后, vmm 需要check hip.SGEIP, 此时有待处理的虚拟外部中断, 进而查hgeip, 查到是第三个vcpu的, 则切换到第三个vcpu 运行, 切换前将hstatus.VGEIN 设置为3. 此时vsip.SEIP = hip.VSEIP 会被置1, 第三个vcpu 陷入V=1 mode, 处理虚拟外部中断. 如guest os kernel 将sie.SEIE 置过位, 则guest os 会处理guest external 中断(10号中断会换成9号中断), guest os 需要查询plic 中断控制器, 判断外部中断是谁的, 该由谁的中断处理函数处理. 处理完后将sip.SEIP 清0, 返回到 hypervisor vmm 后, 因为 sip.SEIP -> vsip.SEIP = hip.VSEIP 同样已经被置0了.
备注
hgeip read-only csr, 由硬件操作
hgeie rw csr, 由软件操作
hip.SGEIP read-only bit, 由硬件操作
hip.VSEIP read-only bit, 由硬件操作
陷入模拟
为了虚拟数据吞吐,HS模式可以使用“陷入-模拟”法。即在访问内存 映射外设对应的地址时,产生相应的中断,通过模拟外设的运行来实现后续的过程。这种方式可以模拟PLIC外设、VirtIO外设和其它一些软件模拟的吞吐外设。
RISC-V可以通过核的CSR寄存器注入中断,因此不需要为虚拟化而特殊设计中断控制器外设。RISC-V的时钟和核间中断可通过SBI软件辅助完成,而Aarch64需要特殊设计的计时器外设来支持虚拟化功能。
不足
RISC-V对虚拟化的支持仍然只集中在CPU的虚拟化上。H-extension已经实现了与KVM和QEMU中的Xvisor的功能完备性。然而,目前还没有任何硬件实现是公开的,而且在可预见的未来,具有硬件虚拟化支持的商业RISC-V内核也不会被发布。RISC-V在虚拟化方面仍有一些差距。
在ISA层面上,需要cache管理操作这样的功能。
- 尽管现有的管理程序层提供了逻辑上的CPU和内存隔离,但由于虚拟机(VM)之间共享的微架构资源(如最后一级的缓存、互连和内存控制器)造成的相互干扰,恶意的虚拟机可以通过增加对共享资源的消耗来实施拒绝服务(DoS)攻击, 或者利用现有的定时侧信道来间接访问其他虚拟机的数据 (Intel Cache Allocation Technology)
缺少对虚拟化至关重要的组件是IOMMU。需要一个IOMMU来实现高效的虚拟化,通过允许直接分配DMA能力的设备给虚拟机,同时保证虚拟机和管理程序本身之间的强隔离。提供设备直通的能力. 这方面最近才有正式的文档发布, 距离开源实现还需要一些时间.
https://github.com/riscv-non-isa/riscv-iommu/releases