[Virtualization](三):RISC-V H-extension 与 Guest 执行模式

发布时间:2026/8/1 12:26:06
[Virtualization](三):RISC-V H-extension 与 Guest 执行模式 第二篇从 QEMU 启动路径出发看了一台 RISC-V 虚拟机如何被 QEMU 拼出来、如何通过 device tree 交给 Guest Linux、以及 TCG 和 KVM 在 CPU 执行路径上的差异。第三篇进入架构层RISC-V Hypervisor extension简称 H-extension究竟怎样让 Host Linux 管理 Guest Linux1. 本篇要回答的问题虚拟化最核心的问题之一是权限。Guest Linux 需要像普通内核一样运行管理自己的页表处理中断和异常调度自己的进程访问 supervisor CSR执行sret等特权指令但它又不能真的拥有整台机器的最高控制权。所以问题变成当 Guest Linux 以为自己运行在 S-mode 时RISC-V 硬件和 Host Linux 究竟为它制造了怎样的“虚拟 supervisor 世界”RISC-V H-extension 的目标就是让 guest kernel 尽量保持普通 supervisor kernel 的运行模型同时让 host kernel 可以截获、隔离和管理它。2. 没有虚拟化时的 RISC-V 执行模式先回到没有 H-extension 的普通 RISC-V 系统。常见执行模式可以简化成三层----------------------------- | U-mode | | user applications | ----------------------------- ^ | v ----------------------------- | S-mode | | supervisor OS kernel | ----------------------------- ^ | v ----------------------------- | M-mode | | machine firmware / SBI | -----------------------------每一层大致承担不同职责。M-mode 是最高权限通常运行 firmware 或 SBI implementation例如 OpenSBI。它可以访问 machine-level CSR管理最底层的 trap、timer、IPI、hart 启停等能力。S-mode 通常运行操作系统内核例如 Linux。它管理用户进程、虚拟内存、中断和设备驱动但在需要更底层平台服务时会通过 SBI 调用 M-mode。U-mode 运行普通用户态程序。它不能直接执行 supervisor 或 machine 级别的特权操作。在这个模型里Linux kernel 运行在 S-mode它看到的 supervisor 状态是真实 supervisor 状态。3. 加入 H-extension 后的新模式引入 H-extension 后RISC-V 增加了面向虚拟化的执行语义。最重要的是多出两类 guest 视角的模式----------------------------- | VU-mode | | guest user applications | ----------------------------- ^ | v ----------------------------- | VS-mode | | guest supervisor kernel | ----------------------------- ^ | v ----------------------------- | HS-mode | | host supervisor kernel | ----------------------------- ^ | v ----------------------------- | M-mode | | machine firmware / SBI | -----------------------------这里有两个关键概念。第一HS-mode 不是一个全新的独立 privilege mode而是启用 hypervisor 能力后的 host supervisor 运行环境。Host Linux kernel 通常运行在 HS-mode。第二VS-mode 是 guest supervisor mode。Guest Linux kernel 以为自己运行在 S-mode但硬件会把它放在 virtual supervisor 上下文中。这句话很重要Guest Linux 看到的是 supervisor 世界Host Linux 看到的是被虚拟化的 supervisor 世界。因此 guest kernel 可以继续执行很多原本属于 supervisor kernel 的逻辑而 host kernel 仍能在关键边界上拥有最终控制权。4. Host 与 Guest 的状态分离虚拟化不能只靠“多一个模式”。真正麻烦的是状态。普通 Linux kernel 会使用很多 supervisor 状态例如sstatussiestvecsscratchsepcscausestvalsatp如果 host kernel 和 guest kernel 共用同一组状态就会互相踩踏。H-extension 的做法是引入 virtual supervisor state。Guest Linux 访问的很多 supervisor CSR在虚拟化上下文里会对应到 VS 版本例如vsstatusvsievstvecvsscratchvsepcvscausevstvalvsatp这样一来Host Linux 可以保留自己的 supervisor 状态Guest Linux 则拥有一套看起来像 supervisor 的虚拟状态。可以画成这样Host Linux in HS-mode uses: sstatus, sie, stvec, satp, ... Guest Linux in VS-mode uses: vsstatus, vsie, vstvec, vsatp, ...从 guest kernel 的角度它仍然像普通 Linux 一样设置 trap vector、打开中断、切换页表。从 host/KVM 的角度这些状态属于某个 vCPU context需要在 vCPU 运行前恢复在 vCPU 退出后保存。5. vCPU contextGuest CPU 的软件影子在 KVM 里一个虚拟 CPU 不只是一个线程。它至少需要保存通用寄存器guest supervisor CSRvirtual supervisor CSRguest timer 状态guest interrupt 状态floating point/vector 状态当前 guest PCtrap/exit 相关信息可以把 vCPU context 理解成 Guest CPU 的软件影子。QEMU vCPU thread | | ioctl(KVM_RUN) v Linux KVM vCPU context | | restore guest state v RISC-V CPU runs Guest Linux | | trap / exit v Linux KVM saves guest state当 QEMU 调用KVM_RUN时KVM 会把 vCPU context 装载到硬件可见的状态里然后进入 guest。当 guest 发生 exit 时KVM 再把相关状态保存回来并决定是在内核里处理还是返回 QEMU。6.hstatus控制虚拟化执行环境hstatus是 H-extension 中非常关键的 CSR 之一。它保存和 hypervisor 执行状态有关的信息用来控制或反映 guest 运行时的一些行为。具体 bit 很多第一篇不需要死记。先建立几个方向当前是否处在 virtualized execution 相关上下文中guest 访问某些状态时如何解释trap 返回时要回到什么虚拟化状态guest 使用的 XLEN 等模式信息对读代码来说更重要的不是背下每个 bit而是知道hstatus属于“host 用来控制 guest 执行环境”的那组寄存器。后续读 Linux KVM/RISC-V 时看到 KVM 在 vCPU run 前后设置或保存hstatus要把它理解成Host 正在为 Guest Linux 准备或恢复一个可以运行的 virtual supervisor 世界。7.hedeleg和hideleg哪些 trap 交给 Guest 自己处理虚拟化并不意味着所有 trap 都要回到 host。如果 Guest Linux 自己能够处理某些异常或中断最好让它直接处理否则每次都陷入 host 会非常昂贵。RISC-V 使用 delegation 机制控制 trap 的流向。在 hypervisor 场景里两个关键 CSR 是hedeleghypervisor exception delegationhideleghypervisor interrupt delegation它们的作用是决定哪些异常或中断可以交给 VS-mode 的 guest supervisor 处理。简化理解trap happens while guest is running | -- delegated to VS-mode Guest Linux | -- trapped to HS-mode Host Linux/KVM如果一个 trap 被委派给 guestGuest Linux 会像普通 kernel 一样进入自己的 trap handler。如果没有被委派它会陷入 HS-mode由 Host Linux/KVM 判断该如何处理。这种机制的目标是减少不必要的 host 介入同时保留隔离边界。8.hgatp第二阶段地址翻译的入口内存隔离离不开地址翻译。在虚拟化场景中Guest Linux 管理自己的页表但它管理的是 guest physical address不是真正的 host physical address。完整路径是Guest virtual address | | stage 1: controlled by Guest Linux, via vsatp v Guest physical address | | stage 2: controlled by Host Linux/KVM, via hgatp v Host physical address这里vsatp和hgatp的分工很清楚。vsatp属于 guest 的第一阶段地址翻译。Guest Linux 以为自己像普通内核一样切换页表。hgatp属于 host 管理的第二阶段地址翻译。Host Linux/KVM 用它限制 guest physical address 能映射到哪里。这带来几个关键性质Guest 可以正常管理自己的用户进程地址空间Host 可以把 guest 内存限制在指定 memory slot 中Guest 不能直接访问 host kernel 或其他 VM 的内存KVM 可以通过 stage-2 fault 进行缺页处理、dirty logging、write protection所以hgatp可以看成 RISC-V 虚拟化内存隔离的核心入口。9. Guest trap 的几种走向当 Guest Linux 正在运行时可能发生很多类型的事件。例如guest 用户态程序触发 page faultguest kernel 访问一个不存在的 MMIO 地址guest timer 到期guest 执行 SBI callguest 访问某个需要 host 介入的 CSRstage-2 page fault外部中断到达这些事件不一定走同一条路径。可以粗略分成三类。9.1 Guest 自己处理例如 guest 用户态进程的普通 page fault。这类异常属于 guest OS 的正常职责理想情况下可以直接交给 Guest Linux 处理。Guest user page fault | v Guest Linux page fault handler9.2 KVM 在内核态处理例如某些虚拟 timer、部分 CSR、stage-2 fault。这类事件需要 host 介入但不一定需要返回 QEMU。Guest trap | v Host Linux KVM | | update vCPU state / page table / timer v Resume guest9.3 返回 QEMU 用户态处理例如访问 QEMU 负责模拟的 MMIO 设备。Guest MMIO access | v KVM exit to userspace | v QEMU device model handles access | v KVM_RUN again理解这三类走向非常重要。因为虚拟化性能不是只取决于“有没有硬件辅助”还取决于 exit 频率、exit 去哪里处理、能不能减少往返用户态的次数。10. SBI 在 Guest 虚拟化中的位置RISC-V 系统里SBI 是 supervisor binary interface。普通 Linux 通过 SBI 请求底层 firmware 提供某些服务。在 guest 场景里Guest Linux 也可能执行 SBI call例如设置 timer发送 IPIconsole putchar取决于环境hart state managementsystem reset问题是Guest Linux 看到的 SBI 服务未必直接来自真实 M-mode firmware。可能的路径包括Guest Linux SBI call | -- handled/emulated by KVM | -- forwarded to host SBI / firmware | -- returned to QEMU for userspace handling具体走哪条路径取决于 SBI extension、KVM 实现和平台能力。所以读 RISC-V KVM 时SBI call 是一个很好的观察点它能同时牵出 guest trap、KVM emulation、timer/IPI、以及 host firmware 的边界。11. H-extension 与 QEMU/KVM 的分工现在可以把三层关系重新压缩一下。QEMU - creates VM and vCPU through /dev/kvm - builds machine model and devices - handles userspace exits Linux KVM/RISC-V - owns vCPU context - configures hstatus/hedeleg/hideleg/hgatp - enters and exits guest - handles traps that can stay in kernel RISC-V H-extension - provides VS/VU execution semantics - provides virtual supervisor state - provides two-stage address translation - provides trap routing controlsQEMU 不需要自己模拟所有 privileged behavior。Linux KVM 也不需要自己实现完整设备模型。RISC-V 硬件也不需要知道某个 virtio block 背后是哪个 host 文件。这三者的边界清楚虚拟化系统才容易扩展。12. 读 Linux KVM/RISC-V 代码时可以带着哪些问题进入源码前建议先带着问题读而不是按文件顺序硬啃。几个适合作为入口的问题vCPU 创建时KVM 初始化了哪些 guest CSRKVM_RUN前哪些状态会被写入硬件 CSRguest exit 后KVM 如何判断 exit reason哪些 trap 会被 KVM 直接处理哪些 trap 会返回 QEMUstage-2 page table 在哪里创建和更新timer 和 IPI 是如何注入给 guest 的SBI call 是在哪里被解析和处理的对应源码入口可以先看arch/riscv/kvm/vcpu.carch/riscv/kvm/vcpu_exit.carch/riscv/kvm/vcpu_sbi.carch/riscv/kvm/vcpu_timer.carch/riscv/kvm/mmu.carch/riscv/include/asm/kvm_host.hvirt/kvm/kvm_main.c这些文件的具体函数路径留到后续文章展开。13. 本篇小结这一篇的重点是把 RISC-V H-extension 的作用放进 QEMU/KVM 的整体路径里。第一Guest Linux 并不是直接运行在真实 S-mode 中。它运行在 VS-mode 中看到的是一套 virtual supervisor state例如vsstatus、vstvec、vsepc、vsatp。第二Host Linux/KVM 运行在 HS-mode 中。它负责保存和恢复 vCPU context配置hstatus、hedeleg、hideleg、hgatp并决定 guest trap 的处理路径。第三内存隔离依赖 two-stage translation。Guest Linux 通过vsatp管理自己的第一阶段页表Host Linux/KVM 通过hgatp管理第二阶段页表。第四不同 trap 有不同归宿。有些交给 Guest Linux 自己处理有些由 KVM 在内核态处理有些返回 QEMU 用户态设备模型。可以把本篇压缩成一句话RISC-V H-extension 为 Guest Linux 制造了一个可以相信的 supervisor 世界同时让 Host Linux/KVM 在这个世界之外保留最终控制权。14. 下一篇预告Linux KVM/RISC-V 的 vCPU 运行路径下一篇可以从 Linux kernel 源码视角进入KVM_RUN。会讨论QEMU 如何通过 ioctl 进入 KVMKVM 如何创建 VM 和 vCPURISC-V vCPU context 在 kernel 中如何表示KVM_RUN前后 guest state 如何保存和恢复guest exit reason 如何被分类哪些 exit 留在 kernel哪些返回 QEMU读arch/riscv/kvm/vcpu.c和vcpu_exit.c时应该抓哪条主线