背景
Rust-Shyper (RISC-V) 框架设计
对系统所有关键资源和状态进行统一管理和隔离 提供一套完整的核间通信机制 提供多种设备模拟策略,能够为 VM 提供虚拟磁盘、虚拟网卡等 Virt I/O 模拟设备 为 VM 模拟 SBI 接口,使之能够使用原有 M 模式下的底层接口
此外,除了 APLIC 可以往 Interrupt File 中直接注入中断外,未来的实现会允许 PCI 设备通过 MSI 信号直接注入中断,避免其陷入 Hypervisor。
[!NOTE] Rust-Shyper 对 Sstc 与 Svnapot 拓展的支持 RISC-V Sstc 拓展提供了 VS 模式下的计时器,使得 GVM 不需要陷入更高特权级或者进行 SBI 调用也可以设置计时器并产生中断;Svnapot 拓展使得页表可以支持高达 64 KiB 的页面,从而降低高内存负载下 TLB 的压力。Rust-Shyper 已经实现了对这两个拓展的支持,但尚未进行完备的测试。
Rust-Shyper 使用指南
从仓库 ^1 中下载源码后,安装下列软件依赖:
Rust Nightly
riscv64-linux-gnu-gcc 交叉编译工具链
cargo-binutils (Optional)
qemu-system-riscv64 >= 8.2.0
然后进入 cli 目录编译用户态运行的 Rust-Shyper CLI,并从 tools 目录中获取编译好的内核模块。它们均需要打包进 MVM 的镜像中。
MVM 的配置
MVM 是用于对其他虚拟机进行管理的管理 VM,运行 Linux,可以通过内核模块和用户态命令行工具(CLI)与 Rust-Shyper 通信,以此完成管理 VM 的任务。
仓库中提供了 5.15 版本内核的镜像:image/Image_5.15.100-riscv-starfive,具有较为完整的功能(开启了大部分常用的内核选项)和兼容性。
可以Ubuntu base image为基础,构建本项目使用的 Linux rootfs,或者可以使用已经配置好的镜像 [^2]。该镜像的用户名与密码均为 root.
[!NOTE] 关于 Ubuntu 对 RISC-V 的支持与镜像构建 要了解更多关于 Ubuntu 对 RISC-V 的支持,参见 https://ubuntu.com/download/risc-v;Ubuntu base image 是一个很小的 Linux rootfs,支持 apt 安装程序,并自带基本的 gnu 命令行工具,可供用户从零构建包含完整软件包 rootfs,可参见 Ubuntu Base官方。
对 MVM 的配置详见仓库 src/config/qemu_riscv64_def.rs,该文件配置了 MVM 的模拟设备、直通设备、中间物理内存(IPA)起始地址与大小、内核启动参数、内核加载地址、CPU 数目等等。这部分并非完全固定,可以根据自己的需求在一定范围内做动态的更改。
Rust-Shyper 的启动
使用下列命令编译并连接 RISC-V 版本的 Rust-Shyper:
ARCH=riscv64 make
ARCH=riscv64 make run
insmod shyper_riscv64.ko
Step.2 启动 Rust-Shyper 守护进程
chmod +x shyper
./shyper system daemon [mediated-cfg.json] &
{
"mediated": [
"/root/vm0_ubuntu.img", // 此处配置第1个GVM的中介磁盘
"/root/vm0_ubuntu_2.img", // 此处配置第2个VM的中介磁盘
"/root/vm0_busybox.img" // 此处配置第3个VM的中介磁盘
]
}
其中列表每一项既可以写分区名(如 /dev/sda1),又可以写磁盘镜像名。
Step.3 通过配置文件来配置GVM
./shyper vm config <vm-config.json>
本目录下(./vm1_config_riscv.json)提供了配置文件的示例(链接中的 Step 3)。
Step.4 启动客户虚拟机
./shyper vm boot <VMID>
按照启动的顺序排序。MVM 的 VMID 为 0,第一个启动的 GVM 的 VMID 为 1,可以通过./shyper vm list 查看当前配置的各个VM的信息。
Step.5 与客户虚拟机交互
首先从外部连接到 MVM:
ssh root@localhost -p 5555
然后通过 minicom 连接 hvc1,监控 GVM 的串口输出:
minicom -D /dev/hvc1 -b 115200
这样便可以与 GVM 交互了:
本项目镜像中带有的GVM镜像 vm0_ubuntu.img,其用户名为 root,密码为 vm1.
通过 AIA 启动 Rust-Shyper
需要将 qemu-system-riscv64 升级到 9.0.2,并使用支持 AIA 的内核版本(如 6.10)。并使用配置好的支持 AIA 启动的镜像 [^3]。
使用如下命令编译使用 AIA 的 RISC-V 版本的 Rust-Shyper:
ARCH=riscv64 IRQ=aia AIA_GUESTS=3 make
并使用如下命令运行:
ARCH=riscv64 IRQ=aia AIA_GUESTS=3 make run
IRQ=[plic|aia]:选择中断的方式,当没有输入该参数时,默认是plic AIA_GUESTS=nnn:需要为每个 HART 模拟的 interrupt file 的数量,也是将要运行 VM 的数量,当没有输入该参数时,默认是 3
启动GVM时的配置
镜像中提供的配置示例(./vm1_config_riscv_aia.json),相较于 PLIC,需要将模拟中断的设备替换为APLIC:
{
"name": "aplic@d000000",
"base_ipa": "0xd000000",
"length": "0x8000",
"cfg_num": 2,
"cfg_list": [
0,
0
],
"type": "EMU_DEVICE_T_APLIC"
},
VM 多核启动时,关于 IMSIC 的地址映射
由于在多核启动时,会向不同的 Hart 中写 IPI 中断号,但由于 GuestOS 不认为自己处于虚拟化模式下,会访问每个 Hart 的 S-mode 的 Interrupt File,所以需要建立地址映射,以便可以找到正确的 Guest Interrupt File.
若当前hart的分配情况为:
MVM:Hart0
GVM:Hart1、2、3
GVM:Hart2、3
由于 MVM 是单核启动,所以无需建立地址映射。GVM1 应建立以下映射:
由于使用的是 Hart1、2、3,其正确的 Guest Interrupt File 地址应为0x28006000、0x2800a000、0x2800e000,但 VM 会认为自己的所用的是 Hart0、1、2,他会访问的地址是 0x28000000、0x28004000、0x28008000,所以应建立以下映射关系,VM 才会正确访问到对应的 Interrupt File
"passthrough_device": {
"passthrough_device_list": [
{
"base_ipa": "0x28000000",
"base_pa": "0x28006000",
"length": "0x1000"
},
{
"base_ipa": "0x28004000",
"base_pa": "0x2800a000",
"length": "0x1000"
},
{
"base_ipa": "0x28008000",
"base_pa": "0x2800e000",
"length": "0x1000"
}
]
},
相应的,GVM2 应该映射如下:
"passthrough_device": {
"passthrough_device_list": [
{
"base_ipa": "0x28000000",
"base_pa": "0x28006000",
"length": "0x1000"
},
{
"base_ipa": "0x28004000",
"base_pa": "0x2800a000",
"length": "0x1000"
},
{
"base_ipa": "0x28008000",
"base_pa": "0x2800e000",
"length": "0x1000"
}
]
},
参考资料
[1]: 开源仓库:https://gitee.com/openeuler/rust_shyper
[2]: 基于 Ubuntu base image 构建好的镜像:https://bhpan.buaa.edu.cn/link/AAF36D01FF739449A19B7D28CC5639F555,提取码 `2Axz`
[3]: 通过 AIA 启动的以构建好的 rootfs 镜像:https://pan.baidu.com/s/1UXlWRf0WFmFndFUj2-bUpQ?pwd=skst,提取码 `skst`