[Virtualization](一):从 RISC-V、QEMU 到 Linux KVM 的虚拟化入口

[Virtualization](一):从 RISC-V、QEMU 到 Linux KVM 的虚拟化入口
这个系列准备从 RISC-V 架构、QEMU 设备模型和 Linux kernel/KVM 实现三条线一起看虚拟化。第一篇先不深入某一个函数或寄存器而是建立一张地图Guest Linux 如何运行在 QEMU/KVM 之上RISC-V 硬件虚拟化扩展解决了什么问题Linux kernel 又在其中扮演什么角色。1. 为什么从 RISC-V 视角看虚拟化很多虚拟化资料默认从 x86 开始讲ring、VMX、VM exit、EPT、APIC、PCI passthrough。这当然重要但如果目标是理解现代虚拟化的结构RISC-V 是一个很好的切入口。原因有三个。第一RISC-V 特权架构相对清晰。它把不同执行层级、异常委派、页表格式、中断控制和虚拟化扩展写得比较直白。理解 RISC-V 的 M-mode、S-mode、U-mode再过渡到 HS-mode、VS-mode 和 VU-mode会比一开始陷入历史包袱更容易建立结构感。第二QEMU 对 RISC-V 支持完整而且适合实验。我们可以用qemu-system-riscv64跑一个 RISC-V Linux也可以在支持 KVM 的 RISC-V 平台上让 QEMU 作为 userspace VMM把 CPU 执行交给 Linux KVM把设备模拟留在用户态。第三Linux kernel 中的 RISC-V KVM 代码相对年轻。它没有 x86 KVM 那么厚的历史层很多路径更适合阅读vCPU 创建、run loop、trap 处理、stage-2 page table、interrupt 注入都能比较清楚地对应到架构概念。2. 本系列想回答的问题这一系列不只是介绍“怎么启动一个虚拟机”而是想沿着一个具体问题往下挖当我们在 QEMU 里启动一个 RISC-V Linux guest 时CPU、内存、中断和设备分别是谁在虚拟化围绕这个问题后续文章会分成几条主线。2.1 RISC-V 特权架构与 H-extension这条线关注架构本身提供了什么能力。后续会讨论M-mode、S-mode、U-mode 的基本职责Hypervisor extension简称 H-extensionHS-mode、VS-mode、VU-modehstatus、hedeleg、hideleg、hgatp等关键 CSRguest trap 与 virtual supervisor traptwo-stage address translationSBI 与虚拟化的关系2.2 QEMU 在虚拟化中的角色这条线关注用户态 VMM 做了什么。后续会讨论qemu-system-riscv64的启动流程virtmachine 提供了哪些虚拟硬件QEMU 如何加载 kernel、initrd 和 device treeTCG 与 KVM 两种执行后端的区别virtio 设备如何连接 guest driver 和 host backendQEMU/KVM run loop 如何把控制权交给 kernel2.3 Linux kernel/KVM 的 RISC-V 实现这条线关注内核如何把硬件虚拟化能力暴露给 QEMU。后续会讨论/dev/kvmABIVM 与 vCPU 的创建KVM_RUNioctlRISC-V KVM vCPU contextstage-2 page table 管理guest exit reason 的处理timer、IPI、interrupt injection与 host scheduler、mm、irq 子系统的交互2.4 内存、中断与设备这条线关注虚拟机最容易变复杂的部分。后续会讨论guest virtual address、guest physical address、host physical addressRISC-V two-stage translationhgatp与 stage-2 page tablePLIC、CLINT、AIA/IMSIC 的虚拟化问题virtio-mmio 与 virtio-pciIOMMU 与设备直通DMA remapping 与内存隔离3. 一个 RISC-V QEMU KVM 的最小模型先把运行关系画出来。-------------------------------------------------- | Guest Linux | | user space / guest kernel / virtio drivers | ------------------------------------------------- | | traps, MMIO, hypercalls, interrupts v -------------------------------------------------- | QEMU userspace VMM | | machine model / device model / migration / I/O | ------------------------------------------------- | | /dev/kvm ioctls v -------------------------------------------------- | Linux kernel KVM | | vCPU run / stage-2 page table / exit handling | ------------------------------------------------- | | RISC-V H-extension v -------------------------------------------------- | RISC-V hardware | | CPU / MMU / interrupt controller / devices | --------------------------------------------------这个图里有几个关键点。Guest Linux 认为自己运行在一台 RISC-V 机器上。QEMU 负责构造这台“机器”的外观CPU 拓扑、内存布局、virtio 设备、device tree、串口、块设备、网卡等。Linux KVM 负责把硬件虚拟化能力变成内核接口创建 VM、创建 vCPU、进入 guest、处理 exit、维护 stage-2 页表。RISC-V H-extension 负责提供真正的硬件边界让 guest kernel 可以在 VS-mode 中运行同时让 host kernel 保持最终控制权。4. QEMU 的两种运行方式TCG 与 KVM看 QEMU 时要先区分两种完全不同的执行后端。4.1 TCG软件翻译执行TCG 是 Tiny Code Generator。当 QEMU 使用 TCG 时guest 指令不会直接在硬件上执行而是被 QEMU 翻译成 host 可以执行的代码。这种模式的特点是不依赖 host 支持同架构虚拟化可以在 x86 host 上跑 RISC-V guest适合开发、调试、bring-up性能通常比 KVM 慢很多例如在普通 x86 开发机上运行 RISC-V Linux通常走的就是 TCG。qemu-system-riscv64\-machinevirt\-nographic\-kernelImage\-appendroot/dev/vda ro consolettyS0\-drivefilerootfs.ext4,formatraw,ifvirtio这里 QEMU 同时负责 CPU 指令翻译和设备模拟。4.2 KVM硬件辅助虚拟化当 QEMU 使用 KVM 时guest CPU 指令尽量直接在硬件上运行。QEMU 不再逐条翻译 guest 指令而是通过/dev/kvm请求 Linux kernel 创建 VM 和 vCPU然后反复调用KVM_RUN让 vCPU 进入 guest。qemu-system-riscv64\-machinevirt,accelkvm\-cpuhost\-nographic\-kernelImage\-appendroot/dev/vda ro consolettyS0\-drivefilerootfs.ext4,formatraw,ifvirtio这时分工变成CPU 执行主要由 RISC-V hardware Linux KVM 处理内存二阶段翻译由 KVM 配置硬件页表设备模型大多仍在 QEMU 用户态I/O 后端可能在 QEMU也可能走 vhost 等内核加速路径这也是理解 QEMU/KVM 的核心QEMU 不是“被 KVM 替代”了而是把 CPU 虚拟化的关键路径交给 KVM自己继续负责机器模型和大量设备模型。5. RISC-V 虚拟化里的执行模式没有 H-extension 时RISC-V 常见执行模式是M-mode: machine firmware / SBI implementation S-mode: supervisor OS kernel U-mode: user applications加入 H-extension 后会出现新的虚拟化语义。M-mode: machine firmware / SBI HS-mode: host supervisor, usually Linux host kernel VS-mode: guest supervisor, usually Linux guest kernel VU-mode: guest user applications这里最重要的是 HS-mode 和 VS-mode。Host Linux kernel 运行在 HS-mode。它拥有管理 guest 的能力可以配置虚拟化相关 CSR、stage-2 地址转换和 trap 委派策略。Guest Linux kernel 运行在 VS-mode。它看起来像一个普通 S-mode kernel但它访问的很多 supervisor 状态其实是 virtual supervisor 状态。这使得 guest kernel 可以尽量“像正常内核一样”运行同时在关键操作上仍然被 host/KVM 控制。6. 两阶段地址翻译内存虚拟化是 RISC-V KVM 的核心之一。Guest 里一次普通访存大致会经历两层地址转换。Guest virtual address | | stage 1: guest page table, managed by Guest Linux v Guest physical address | | stage 2: host-controlled page table, configured by KVM v Host physical address第一阶段由 Guest Linux 管理。Guest 认为自己在维护普通的页表。第二阶段由 Host Linux KVM 管理。它决定 guest physical address 最终映射到哪段 host memory。RISC-V 中stage-2 translation 的关键控制点之一是hgatp。它指向 guest physical 到 host physical 的转换结构。这背后的意义是Guest 可以管理自己的虚拟内存Host 可以限制 Guest 访问范围Guest 不能越过 stage-2 访问其他 VM 或 host 内存KVM 可以通过页表缺页、dirty log、write protect 等机制支持迁移和内存跟踪7. Trap、Exit 与 KVM_RUN虚拟化里经常说 VM exit。放到 QEMU/KVM 里可以从一个循环理解。QEMU thread | | ioctl(KVM_RUN) v Linux KVM enters guest | | guest runs on RISC-V CPU v trap / exit happens | | handled in KVM or returned to QEMU v QEMU handles userspace exit if needed | | ioctl(KVM_RUN) again v guest continues并不是所有 trap 都要回到 QEMU。有些事件 KVM 可以在内核里直接处理例如部分 CSR 访问、timer 处理、stage-2 fault 处理。有些事件需要返回用户态 QEMU例如 MMIO 设备访问、某些调试事件、需要用户态设备模型参与的 I/O。因此性能优化的一个方向就是能在 guest 里直接完成的不要 exit能在 KVM 内核态处理的不要返回 QEMU必须回 QEMU 的路径尽量批量化和异步化。8. Device treeGuest 看到的硬件契约RISC-V Linux 启动时通常依赖 device tree 描述硬件。在 QEMUvirtmachine 中QEMU 会为 guest 构造一份 device tree告诉 Guest LinuxRAM 从哪里开始有几个 CPU hart串口在哪里virtio-mmio 设备在哪里interrupt controller 是什么initrd 放在什么位置chosen bootargs 是什么这份 device tree 是 QEMU 和 Guest Linux 之间的硬件契约。Guest kernel 不需要知道这些设备背后是不是真实硬件。它只需要根据 device tree 发现设备然后加载对应 driver。例如 virtio block 设备在 guest 中看起来像一个块设备但它背后可能只是 host 上的一个镜像文件。Guest virtio-blk driver | | virtqueue v QEMU virtio device model | | pread/pwrite or async I/O v host disk image file9. Linux kernel 和 QEMU 里应该重点看哪些代码如果后续要读源码可以先盯住几个入口而不是一上来全仓库搜索。Linux kernel 侧arch/riscv/kvm/include/uapi/linux/kvm.hvirt/kvm/arch/riscv/include/asm/kvm_host.harch/riscv/include/asm/kvm_vcpu_timer.hQEMU 侧target/riscv/hw/riscv/virt.caccel/kvm/target/riscv/kvm/hw/virtio/hw/block/virtio-blk.chw/net/virtio-net.cRISC-V 规范侧RISC-V Privileged ArchitectureHypervisor extension chapterSBI specificationAIA specification如果讨论高级中断架构这些文件和规范会在后续文章中逐步串起来。10. 第一篇小结这一篇先建立三层关系。第一RISC-V H-extension 提供硬件虚拟化语义。它让 host kernel 可以管理 guest kernel同时让 guest kernel 尽量保持普通 supervisor kernel 的运行模型。第二Linux KVM 是硬件能力和用户态 VMM 之间的桥。QEMU 通过/dev/kvm创建 VM/vCPULinux KVM 负责进入 guest、处理 exit、维护 stage-2 page table并与 kernel 的调度、内存和中断子系统协作。第三QEMU 仍然是虚拟机形态的组织者。即使 CPU 执行交给 KVMQEMU 仍然负责 machine model、device tree、virtio 设备、镜像文件、网络后端和迁移等大量工作。可以把这篇的核心模型压缩成一句话RISC-V 定义虚拟化能力Linux KVM 管理这些能力QEMU 把它们组织成一台可启动、可交互、可管理的虚拟机器。11. 下一篇预告从 QEMU 启动一个 RISC-V Linux Guest下一篇可以从一个最小实验开始沿着启动路径看 QEMU 和 Linux guest 如何建立关系。重点包括qemu-system-riscv64 -machine virt创建了什么kernel Image、initrd、rootfs 如何被放进 guest memoryQEMU 如何生成 device treeGuest Linux 早期启动如何解析 device treeTCG 和 KVM 模式下启动路径有什么不同如何打开 QEMU trace、Linux earlycon 和 KVM debug 信息