Development Background

At the 2021 KVM Forum, there was a discussion themed “QEMU+Rust BoF”. The meeting notes can be found in the 2022 Rust section of the QEMU blog: QEMU+Rust BoF, KVM Forum 2021.

At that meeting, the topics related to introducing Rust into QEMU were discussed in depth and can be summarized as follows:

  1. How to mix C and Rust: use Rust FFI bindings for QEMU’s C structs/APIs. mlureau mentioned that bindgen can cover the FFI layer, but the manual work needed for safety and to fit Rust’s programming style is substantial (unless tools such as gobject-introspection are used). For device models such as out-of-process devices like vhost-user and vfio-user, Rust could be considered for the user-space portion;

  2. Basic requirements for merging Rust code: whether the community had enough people who could review Rust code (at the time, not really, though Rust might attract new contributors and, thanks to its strict checks and tooling, be easier for reviewers);

  3. The challenge of QEMU’s core abstractions: pbonzini pointed out that the core abstractions are the biggest challenge, because they affect how QEMU integrates with Rust. Compiler checks cannot tell you whether you picked the right abstraction. mlureau added that QEMU is not a public library, so the code can be refactored without worrying too much; in fact, it is easier to refactor than in other languages (if it compiles, it works). Another serious issue is technical debt, after all QEMU has evolved for nearly twenty years and supports a huge and diverse set of devices;

  4. Potential benefits brought by Rust: dwg believed that although changing the core abstractions is difficult, that is also where the biggest gains may be. Rust lets us encode subtle but important constraints into interface types, whereas these constraints currently have to be handled through documentation and convention;

  5. Platform support issues: it was necessary to determine which supported host systems (operating systems, distribution versions, CPU architectures) could not build and run Rust code, that is, which users would be excluded (at the time, Rust support was not broad enough to cover all of QEMU’s users);

  6. The attitude of downstream consumers: Rust packages were being handled in Debian and Fedora. Whether or not QEMU used Rust, it would not be the first package to do so. Different distributions handled Rust packaging quite differently: some required packages to be published on crates.io first and each package to be packaged separately, while others accepted bundling all dependencies directly;

  7. The timeline for Rust as a non-optional dependency: mlureau believed that Rust should remain optional for at least two years after the most recent distribution/architecture support requirements for QEMU. It was also necessary to consider whether a supported distribution version that did not package Rust, but whose end users could obtain Rust themselves through rustup, should still count as supported.

In 2024, proposals to integrate Rust into QEMU began to produce mature patches and triggered broad discussion on the community mailing list. See: [RFC PATCH v1 0/6] Implement ARM PL011 in Rust.


Out-of-process devices are devices whose driver and logic do not run in QEMU’s main process, but in a separate user-space process. The advantages include:

  • Performance improvement: reduced context-switch overhead between kernel and user space.
  • Flexibility: complex device logic can be implemented in an isolated process without affecting the stability of QEMU’s main process.
  • Security: even if the device driver fails, it does not directly affect QEMU’s main process.

Testing a PL011 Device Modeled in Rust

QEMU 9.2 officially supports Rust (>= 1.63.0, bindgen >= 0.60.0), but Rust support is disabled by default. You can enable Rust with the following commands; here we take target = aarch64 as an example:

1$ cargo install bindgen-cli
2$ ./configure --enable-system --target-list=aarch64-softmmu --enable-rust

After enabling Rust, the default C implementation of the PL011 device model is replaced with the Rust version.

To verify the result, modify the source code and add a print:

1// rust/hw/char/pl011/src/device.rs:234
2    pub fn write(&mut self, offset: hwaddr, value: u64) {
3-        // eprintln!("write offset {offset} value {value}");
4+      eprintln!("\nwrite offset {offset} value {value}");

Build QEMU with:

1$ ninja -j$(nproc) -C build/

Prepare a bare-metal ARM64 program:

1$ git clone https://github.com/zevorn/arm64-baremetal-demo.git

Configure the cross-compilation environment aarch64-none-elf-gnu and the QEMU binary path by editing the Makefile in arm64-baremetal-demo:

// Makefile:10
QEMU_PATH=/home/zevorn/qemu/build # Fill in the qemu path
CROSS_PREFIX=aarch64-none-linux-gnu- # Fill in the gcc path + prefix; if environment variables are set, only the prefix is needed

Build and run the bare-metal program. The output is:

 1$ cd arm64-baremetal-demo
 2$ make run
 3
 4write offset 0 value 72
 5H
 6write offset 0 value 101
 7e
 8write offset 0 value 108
 9l
10write offset 0 value 108
11l
12write offset 0 value 111
13o
14write offset 0 value 32
15
16write offset 0 value 87
17W
18write offset 0 value 111
19o
20write offset 0 value 114
21r
22write offset 0 value 108
23l
24write offset 0 value 100
25d
26write offset 0 value 10