发展背景
在 2021 年 KVM 的论坛会议,进行了一场以 “QEMU+Rust BoF” 为主题的讨论,会议摘要可以通过 QEMU blog 2022 Rust 板块找到: QEMU+Rust BoF, KVM Forum 2021。
在这个会议上,对 QEMU 引入 Rust 的相关话题,进行了充分讨论,可以总结为以下几个方面:
-
混合使用 C 和 Rust 的方法:使用 Rust FFI 绑定 QEMU 的 C 结构体/ API,mlureau 提到,bindgen 可以覆盖 FFI 部分,而为了安全性和符合 Rust 编程习惯的手动工作量较大(除非使用 gobject-introspection 等工具),对于设备模型,比如 vhost-user 和 vfio-user 这些进程外设备,可以考虑使用 Rust 来实现其用户空间部分;
-
合入Rust 代码的基本要求:社区中是否有足够多的人熟悉审查 Rust 代码(这在当时还不行,但 Rust 可能会吸引新的贡献者,并且由于严格的检查和工具支持,对审阅者来说也更简单);
-
QEMU 核心抽象的问题:pbonzini 指出,核心抽象是最大的挑战,这会影响到 QEMU 如何与 Rust 集成,编译器检查不能告诉你是否选择了正确的抽象,mlureau 补充说,QEMU 不是公共库,因此可以重构代码而不必担心,实际上比其他语言更容易重构(如果能编译通过,它就能工作),另外一个严峻的问题,就是技术债,毕竟 QEMU 已经发展了近二十年,支持的设备繁杂而庞大;
-
Rust 带来的潜在好处:dwg 认为,改变核心抽象虽然困难,但这也是可能获得最大收益的地方。Rust允许我们将细微但重要的约束编码到接口类型中,而这些约束目前必须通过文档和惯例来处理;
-
平台支持的问题:需要确定哪些已支持的主机(操作系统、发行版版本、CPU架构)无法构建和运行Rust代码,即我们排除了哪些用户(在当时,支持 Rust 的平台还不够丰富,无法覆盖 QEMU 的全部用户)。
-
下游消费者的态度: Rust 包在 Debian 和 Fedora 中正在被处理,无论 QEMU 是否使用 Rust ,它们都不会成为第一个使用 Rust 的软件包,不同发行版对 Rust 包的处理方式相当复杂或者差异化,一些发行版要求包首先发布在 crates.io 上,并单独打包每个包,而其他发行版则接受直接包含所有依赖的方式;
-
Rust 作为非可选依赖的时间线:mlureau 认为,在最近的发行版 / 架构支持 QEMU 需求后的至少两年内,Rust 应该保持为可选依赖,另外需要考虑,如果某个受支持的发行版版本本身没有打包 Rust ,但最终用户可以通过 rustup 自行获取 Rust 编译器,这种情况是否被视为支持的问题。
2024 年,QEMU 集成 Rust 的提案开始产出成熟的补丁,并在社区邮件列表引起广泛的讨论,详见:[RFC PATCH v1 0/6] Implement ARM PL011 in Rust。
进程外设备(Out-of-process devices)是指这些设备的驱动程序和逻辑不在QEMU的主进程中运行,而是运行在独立的用户空间进程中。这种方式的好处包括:
- 性能提升:减少内核和用户空间之间的切换开销。
- 灵活性:可以在独立的进程中实现复杂的设备逻辑,而不影响QEMU主进程的稳定性。
- 安全性:即使设备驱动程序出现错误,也不会直接影响QEMU主进程。
测试使用 Rust 建模的 PL011 设备
QEMU 9.2 版本正式支持了 Rust (>= 1.63.0,bindgen >= 0.60.0),不过默认禁用 Rust 特性,可以使用以下命令使能 Rust ,我们以构建 target = aarch64
为例:
$ cargo install bindgen-cli$ ./configure --enable-system --target-list=aarch64-softmmu --enable-rust
使能 Rust 以后,将默认替换原来使用 C 编写的 PL011 外设模型,改用 Rust 版本。
为了验证结果,我们修改源代码,增加打印:
// rust/hw/char/pl011/src/device.rs:234 pub fn write(&mut self, offset: hwaddr, value: u64) {- // eprintln!("write offset {offset} value {value}");+ eprintln!("\nwrite offset {offset} value {value}");
编译 QEMU ,命令如下:
$ ninja -j$(nproc) -C build/
准备一个 arm64 的裸机程序,命令如下:
$ git clone https://github.com/zevorn/arm64-baremetal-demo.git
配置交叉编译环境 aarch64-none-elf-gnu,以及 QEMU 可执行文件路径,修改 arm64-baremetal-demo 的 Makefile:
// Makefile:10QEMU_PATH=/home/zevorn/qemu/build # 填入 qemu 路径CROSS_PREFIX=aarch64-none-linux-gnu- # 填入 gcc 路径+前缀,如果配置环境变量了,只写前缀即可
编译运行裸机程序,看到以下输出:
$ cd arm64-baremetal-demo$ make runwrite offset 0 value 72Hwrite offset 0 value 101ewrite offset 0 value 108lwrite offset 0 value 108lwrite offset 0 value 111owrite offset 0 value 32write offset 0 value 87Wwrite offset 0 value 111owrite offset 0 value 114rwrite offset 0 value 108lwrite offset 0 value 100dwrite offset 0 value 10