RISC-V虚拟化实战基于RISC-V手撸Hypervisor
如何基于RISC-V H-extension,手撸一个Hypervisor,并成功运行Linux?
这位开发者在自研操作系统Starina实现虚拟化支持,并记录了全过程。
整个过程从零开始,一步步搭起一个能跑Linux的轻量虚拟机环境,架构类似WSL2,但运行在完全自研的系统内核上。
Step 1:进入Guest模式
RISC-V通过H-extension提供VS-mode(Virtual Supervisor),相当于Guest OS的内核态。设置好CSR(特别是 `hstatus.SPV`)并执行 `sret`,CPU就能跳入Guest态,成功触发了instruction guest-page fault,说明模式切换成功。
Step 2:执行第一条ecall
为了验证Guest能正常运行指令,加载了一个简单程序跑 `ecall`。这需要先构建Guest页表(使用Sv39x4等模式),并配置 `hgatp` 映射地址。Guest trap成功说明访问机制生效。
Step 3:Hello World from Guest
用RISC-V汇编写了一个最小程序,通过SBI(类似BIOS接口)输出字符“Hi!”。这个测试验证了系统调用处理、字符输出和中断处理链都通了。
Step 4:尝试跑Linux内核
构建了RISC-V Linux Image,复制进Guest内存,CPU跳转后成功执行,但马上遇到空指针异常,crash在 `__pi_fdt32_ld` —— 问题出在没有提供Device Tree(设备树)。
Step 5:补上Device Tree
用RustVMM的 `vm-fdt` crate 构建最小设备树,指定内存、CPU等信息,让Linux知道自己运行在什么样的“硬件”上。这个crate支持 `no_std`,直接集成到内核非常方便。
Step 6:支持rdtime读取
Linux读 `rdtime` 报错,原因是 `hcounteren` CSR没开启。补上之后,Guest内核能正常读取时间戳。这也体现了RISC-V对寄存器权限的严格控制。
Step 7:支持定时器与中断注入
Linux内核启动时需要确认系统时钟在“跳动”。通过实现 `sstc` 扩展 + 设置 `hideleg`,Guest能正确接收到timer中断。第一次成功向Guest注入中断,超关键。
Step 8:模拟MMIO设备
想要真正跑起来,还得让Linux能看到设备。用MMIO机制模拟了常见硬件(如PLIC中断控制器、virtio-blk磁盘、virtio-net网卡),Guest访问设备地址时触发trap,Hypervisor拦截并模拟读写。
Step 9:接入virtio-fs文件系统
为了更好地和Starina集成,选用了 `virtio-fs` 替代传统的 `virtio-blk`,通过FUSE协议实现Host与Guest共享文件系统,为后续文件交换打下基础。
九大步在RISC-V上写Hypervisor,Linux就能跑在你自己写的OS里。
全程都能在QEMU中完成,QEMU已经支持RISC-V H-extension模拟,再加上GDB调试支持,连Hypervisor和Guest的栈回溯都一目了然,开发体验非常丝滑。
原文: