qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Rust in QEMU update, January 2025


From: Paolo Bonzini
Subject: Rust in QEMU update, January 2025
Date: Tue, 28 Jan 2025 11:19:27 +0100
User-agent: Mozilla Thunderbird

It's been roughly two months since my previous posting of a roadmap
for Rust in QEMU, so it's time for an update.  While the project is
still at an experimental phase, the amount of functionality available
from safe Rust is enough that it could be considered for new devices.

As before, this mostly covers what I have looked at, which is making
it possible to write devices in safe Rust.  Topics such as QAPI and
async (block devices) are missing for this reason. An "⚠️" icon marks
the most important missing features.

Table of contents
'''''''''''''''''

* Status in QEMU 10.0
* Build system
* Feature parity for devices
* Avoiding undefined behavior
* Bindings to C code
* New utility code
* Rust version requirements
* Next steps


Status in QEMU 10.0
'''''''''''''''''''

QEMU when built with ``--enable-rust`` compiles on all supported
build platforms.  It passes CI and ``make check-unit`` runs tests for
rust/qemu-api.  ``make check-qtests`` covers the Rust pl011 device model,
including migration; a Rust conversion of the HPET device is available
on the mailing list, and will probably be merged in the next few weeks.

The amount of unsafe code needed to define classes, define callbacks,
and invoke core QEMU functionality has gone down a lot since 9.2,
especially if you count a few more pieces that are posted and pending
review.  QOM objects can be created and their lifetime managed using
entirely safe code; unlike C code, Rust is able to differentiate embedded
objects (which do not have an independent reference count) from those
that are allocated in their own heap block.  Available bindings include
sysbus devices, GPIO pins, clocks and MemoryRegionOps and timers;
Kevin Wolf is working on the block layer and on using Rust in tools.
There are also core building blocks that make it much easier to extend
the C<->Rust bindings.

Last December I posted a comparison between the pl011 C implementation
and the "intended" shape of the Rust code; at the time of the posting
(https://lists.nongnu.org/archive/html/qemu-rust/2024-12/msg00006.html)
the code compiled but was far from being usable.  The in-tree Rust
implementation of pl011 is now very similar to that intended shape,
the main exception being character devices and safe ``instance_init``.


Build system
''''''''''''

Developers can use ninja to easily access clippy, rustfmt and rustdoc.
Meson 1.7 supports clippy natively, with a rustdoc pull request
in progress.  This is important because long term it would be nice to
avoid having a proliferation of src/ directories since QEMU has hundreds
of devices.

A Meson pull request is also in progress that add support for doctests,
integrating them into ``make check`` and allowing them (unlike ``cargo test
--doc``) to link with C code[1]_.

⚠️ Modules do not work with Rust code yet due to a limitation of
rust/qemu-api/meson.build.  The fix for this is also in Meson and there
is a pull request open for this as well[2]_.

Rust is still not enabled and its presence is not checked for by
default.  If any devices are contributed that are written in Rust and
do not have a C counterpart, it may be worth splitting "enable Rust"
from "enable all devices written in Rust".

.. [1] https://github.com/mesonbuild/meson/pull/13933
.. [2] https://github.com/mesonbuild/meson/pull/14031


Feature parity for devices
''''''''''''''''''''''''''

⚠️ Some recent pl011 commits are missing in the Rust version.  Philippe
volunteered to port them to teach himself Rust.  Some issues in the
ported device are now fixed and the migration stream is compatible
between the Rust and C versions.

⚠️ Philippe also has a series to implement flow control and better use of
the PL011 FIFO (IIUC).  Porting this to Rust would be hard right now,
so its Rust implementation blocked on having chardev bindings.

⚠️ HPET is lacking support for live migration.

⚠️ As before, also missing is logging and tracing.


Avoiding undefined behavior
'''''''''''''''''''''''''''

Interior mutability types specific to QEMU---homegrown variants of Cell
and RefCell that support the "big QEMU lock", and therefore are usable
in a multi-threaded context---are included in tree, and used by the
pl011 and HPET device models.  This means that Rust code is now
respecting the invariant that mutable references must be either
unique, or obtained from an ``UnsafeCell<>``.

In some cases it is still creating references that point to invalid
data before they are initialized; this will be solved by using Linux's
pinned-init crate to implement the QOM ``instance_init`` method.
``pinned-init`` is available for use in userspace on crates.io and
eliminates the need to expose partially-initialized objects to safe
Rust code.

John Baublitz pointed out more cases of fields that should also be
included in a cell, because they are modified by C code.  These
include fields for qdev properties, and the C structs for QOM
classes.  The issues here are a lot more theoretical than the
rest, and Linux provides examples of how to deal with them too.


Bindings to C code
''''''''''''''''''

Even though QOM's object-oriented, inheritance heavy design is a challenge
for Rust, qdev bindings in QEMU 10.0 are going to be relatively complete,
covering both classes and interfaces.  Thanks to a new callback
functionality, devices have access to GPIO pins, timers, clocks and
MemoryRegionOps.  This should be enough to implement devices with very
limited use of unsafe code.

One area of development that was not mentioned in the previous roadmap
is VMState.  While a direct port of the C macros was part of 9.2, it
required a large amount of macros, and especially it was not type safe.
The latter actually made the Rust version worse than the C code and,
while fixable, rewriting it to take advantage of Rust's type system
and const evaluation was a better plan.  The VMState API is not yetfinal
(for example it still needs unsafe callbacks for pre_save/post_load)
but the new version is already a lot easier to use.

The remaining instances of `unsafe` blocks in the pl011 and HPET code
are as follows:

* pl011 needs character device bindings; the code for this is almost
  ready, though it may not necessarily be in 10.0

* HPET does some very simple memory accesses; a good safe solution
  for this may be the ``vm-memory`` crate.  I have not looked into
  using it, but ``vm-memory`` and ``vm-virtio`` were written with
  QEMU's use cases in mind

* as mentioned above, bindings for VMState definitions are also in
  progress and still partly unsafe

* the ``instance_init`` method is using unsafe code until the
  ``pinned-init`` crate is introduced.  ``pinned-init`` will also
  help with creating cyclical data structures

While this may seem a lot, the fact that we can enumerate the uses of
``unsafe`` and plan their removal is already a substantial achievement!

The amount of functionality available from safe Rust is enough that
including new devices should be possible, even if they need some unsafe
code for parts of QEMU that do not have bindings yet.

Most devices added to QEMU are simple and do not do any complex DMA.
In the previous roadmap I mentioned that such simple devices have very
little benefit from rewriting them in Rust.  While this is probably
true for devices that exist as C code, there is in my opinion already a
benefit to writing *new* devices in Rust.  The code is generally clearer
and so are some aspects of the semantics (e.g. which fields are mutable);
the main limitation is lack of support for tracing and logging.


New utility code
''''''''''''''''

The qemu-api and qemu-api-macros crates now include more utility code
for use in both bindings and devices, including:

* bit-twiddling extensions for integers

* a procedural macro to convert between enums and integers

* callback support

Callbacks are the best and most unexpected news since the last roadmap
post.  The solution implemented in the qemu-api crate makes it possible
to convert a Rust function and a reference into a function pointer and
an opaque value; generics are used to create separate trampolines (i.e.
separate function pointers) for each Rust function.  This mechanism
is used by clocks, timers, memory regions and character devices; so
this part can be considered solved.

The qemu-api-macros crates has also grown a small set of utility functions
that can be reused by more procedural macros in the future.

One utility that I'm missing is bit-flag enums.  The most
commonly used crate in the Rust ecosystem is bitflags
(https://docs.rs/bitflags/2.6.0/bitflags/); it does not have any
dependencies so it is easy to incorporate in QEMU if desired.


Rust version requirements
'''''''''''''''''''''''''

Compared to the previous roadmap, the main change is that three features
have become a bit more pressing:

* while pinned-init's code only needs small changes to support Rust 1.63.0,
  it relies heavily on ``impl Trait`` return types; trait functions however
  can only return ``impl Trait`` since Rust 1.75.0.  Because instance_init
  is itself in a trait, this is an important limitation which might delay the
  inclusion of pinned-init; the only supported distro with an older rustc
  is Debian bookworm.

* references to statics in constants (stable in 1.83.0); the main place
  where ugly workarounds are needed is VMState.

While none of these are blockers for enabling Rust, the first two add a
small amount of technical debt for each Rust device that is added to QEMU.
It may be appealing to use the ``rustc-web`` package on Debian bookworm,
which provides the 1.78.0 version of rustc as of this writing.


Next steps
''''''''''

Two areas that were mentioned in earlier discussions have seen no
activity yet:

* Tracepoints and logging are the highest-priority missing feature, perhaps
  together with DMA; while the current set of device only has a limited set
  of tracepoints, observability is obviously important as a debugging tool.
  This is the main blocker before implementing new devices in Rust can be
  encouraged.

* interoperability between QEMU's C data structure and Rust counterparts
  has no news either.  We'll figure it out as soon as we need a realize()
  implementation that can fail, or when tackling QAPI.

On top of this it may be worthwhile to investigate again using procedural
macros to simplify declaring QOM/qdev classes, such as for qdev properties.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]