Commit graph

26 commits

Author SHA1 Message Date
Paolo Bonzini
bc2a48d647 rust: make TryFrom macro more resilient
If the enum includes values such as "Ok", "Err", or "Error", the TryInto
macro can cause errors.  Be careful and qualify identifiers with the full
path, or in the case of TryFrom<>::Error do not use the associated type
at all.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-06-06 14:32:54 +02:00
Paolo Bonzini
214518614c rust: qemu-api-macros: add from_bits and into_bits to #[derive(TryInto)]
These const functions make it possible to use enums easily together
with the bitfield-struct crate.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-06-03 22:44:40 +02:00
Paolo Bonzini
648fe157d3 rust: add "bits", a custom bitflags implementation
One common thing that device emulation does is manipulate bitmasks, for example
to check whether two bitmaps have common bits.  One example in the pl011 crate
is the checks for pending interrupts, where an interrupt cause corresponds to
at least one interrupt source from a fixed set.

Unfortunately, this is one case where Rust *can* provide some kind of
abstraction but it does so with a rather Perl-ish There Is More Way To
Do It.  It is not something where a crate like "bilge" helps, because
it only covers the packing of bits in a structure; operations like "are
all bits of Y set in X" almost never make sense for bit-packed structs;
you need something else, there are several crates that do it and of course
we're going to roll our own.

In particular I examined three:

- bitmask (https://docs.rs/bitmask/0.5.0/bitmask/) does not support const
  at all.  This is a showstopper because one of the ugly things in the
  current pl011 code is the ugliness of code that defines interrupt masks
  at compile time:

    pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0);

  or even worse:

    const IRQMASK: [u32; 6] = [
      Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0,
      ...
    }

  You would have to use roughly the same code---"bitmask" only helps with
  defining the struct.

- bitmask_enum (https://docs.rs/bitmask-enum/2.2.5/bitmask_enum/) does not
  have a good separation of "valid" and "invalid" bits, so for example "!x"
  will invert all 16 bits if you choose u16 as the representation -- even if
  you only defined 10 bits.  This makes it easier to introduce subtle bugs
  in comparisons.

- bitflags (https://docs.rs/bitflags/2.6.0/bitflags/) is generally the most
  used such crate and is the one that I took most inspiration from with
  respect to the syntax.  It's a pretty sophisticated implementation,
  with a lot of bells and whistles such as an implementation of "Iter"
  that returns the bits one at a time.

The main thing that all of them lack, however, is a way to simplify the
ugly definitions like the above.  "bitflags" includes const methods that
perform AND/OR/XOR of masks (these are necessary because Rust operator
overloading does not support const yet, and therefore overloaded operators
cannot be used in the definition of a "static" variable), but they become
even more verbose and unmanageable, like

  Interrupt::E.union(Interrupt::MS).union(Interrupt::RT).union(Interrupt::TX).union(Interrupt::RX)

This was the main reason to create "bits", which allows something like

  bits!(Interrupt: E | MS | RT | TX | RX)

and expands it 1) add "Interrupt::" in front of all identifiers 2) convert
operators to the wordy const functions like "union".  It supports boolean
operators "&", "|", "^", "!" and parentheses, with a relatively simple
recursive descent parser that's implemented in qemu_api_macros.

Since I don't remember exactly how the macro was developed, I cannot exclude
that it contains code from "bitflags".  Therefore, I am conservatively leaving
in the MIT and Apache 2.0 licenses from bitflags.  In fact, I think there
would be a benefit in being able to push code back to "bitflags" anyway
whenever applicable, so that the two libraries do not diverge too much,
so that's another reason to use this.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-06-03 22:44:40 +02:00
Paolo Bonzini
1297b285cc rust: make declaration of dependent crates more consistent
Crates like "bilge" and "libc" can be shared by more than one directory,
so declare them directly in rust/meson.build.  While at it, make their
variable names end with "_rs" and always add a subproject() statement
(as that pinpoints the error better if the subproject is missing and
cannot be downloaded).

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-05-28 19:35:55 +02:00
Paolo Bonzini
c6b04613bd rust: clippy: enable uninlined_format_args lint
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-05-06 16:02:04 +02:00
Paolo Bonzini
b134a09ffa rust: remove offset_of replacement
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-05-06 16:02:04 +02:00
Paolo Bonzini
465a4b80e2 rust: qemu_api_macros: make pattern matching more readable and efficient
"let ... else" is useful when visiting syntax trees; it avoids multiple
levels of indentation and places the error close to the pattern.

While at it, use "ref" to avoid moving the syntax tree objects.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-05-06 16:02:04 +02:00
Stefan Zabka
ffd5a60e9b rust: centralize config in workspace root
This commit bundles common config option in the workspace
root and applies them through <config>.workspace = true

Signed-off-by: Stefan Zabka <git@zabka.it>
Link: https://lore.kernel.org/r/20250502212748.124953-1-git@zabka.it
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-05-02 23:37:55 +02:00
Paolo Bonzini
f07a5674cf rust: qemu_api_macros: add Wrapper derive macro
Add a derive macro that makes it easy to peel off all the layers of
specialness (UnsafeCell, MaybeUninit, etc.) and just get a pointer
to the wrapped type; and likewise add them back starting from a
*mut.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-03-06 12:44:46 +01:00
Paolo Bonzini
669fab6a1f rust: include rust_version in Cargo.toml
Tell clippy the minimum supported Rust version for QEMU.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-02-07 00:04:28 +01:00
Paolo Bonzini
f26137893b rust: remove unnecessary Cargo.toml metadata
Some items of Cargo.toml (readme, homepage, repository) are
only present because of clippy::cargo warnings being enabled in
rust/hw/char/pl011/src/lib.rs.  But these items are not
particularly useful and would be all the same for all Cargo.toml
files in the QEMU workspace.  Clean them up.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-02-07 00:04:28 +01:00
Paolo Bonzini
809c703a60 rust: qemu-api-macros: add automatic TryFrom/TryInto derivation
This is going to be fairly common. Using a custom procedural macro
provides better error messages and automatically finds the right
type.

Note that this is different from the same-named macro in the
derive_more crate.  That one provides conversion from e.g. tuples
to enums with tuple variants, not from integers to enums.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-01-10 23:34:44 +01:00
Paolo Bonzini
a3b620fff7 rust: qemu-api-macros: extend error reporting facility to parse errors
Generalize the CompileError tuple to an enum, that can be either an error
message or a parse error from syn.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-01-10 23:34:43 +01:00
Paolo Bonzini
e3ff5a17aa rust: macros: check that the first field of a #[derive(Object)] struct is a ParentField
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-01-10 23:34:43 +01:00
Paolo Bonzini
20f0b8e98b rust: macros: check that #[derive(Object)] requires #[repr(C)]
Convert derive_object to the same pattern of first making a
Result<proc_macro2::TokenStream, CompileError>, and then doing
.unwrap_or_else(Into::into) to support checking the validity of
the input.  Add is_c_repr to check that all QOM structs include
a #[repr(C)] attribute.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-01-10 23:34:43 +01:00
Paolo Bonzini
4aed0296b3 rust: rename qemu-api modules to follow C code a bit more
A full match would mean calling them qom::object and hw::core::qdev.  For now,
keep the names shorter but still a bit easier to find.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-12-19 19:36:37 +01:00
Paolo Bonzini
90868c3dce rust: cargo: store desired warning levels in workspace Cargo.toml
An extra benefit of workspaces is that they allow to place lint level
settings in a single Cargo.toml; the settings are then inherited by
packages in the workspace.

Correspondingly, teach rustc_args.py to get the unexpected_cfgs
configuration from the workspace Cargo.toml.

Note that it is still possible to allow or deny warnings per crate or
module, via the #![] attribute syntax.  The rust/qemu-api/src/bindings.rs
file is an example.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-12-10 18:44:06 +01:00
Paolo Bonzini
88dd060db9 rust: qemu-api-macros: always process subprojects before dependencies
Avoid looking for Rust dependencies via cmake.

Reviewed-by: Junjie Mao <junjie.mao@hotmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-11-09 08:34:07 +01:00
Junjie Mao
f351840088 rust: introduce alternative implementation of offset_of!
offset_of! was stabilized in Rust 1.77.0.  Use an alternative implemenation
that was found on the Rust forums, and whose author agreed to license as
MIT for use in QEMU.

The alternative allows only one level of field access, but apart
from this can be used just by replacing core::mem::offset_of! with
qemu_api::offset_of!.

The actual implementation of offset_of! is done in a declarative macro,
but for simplicity and to avoid introducing an extra level of indentation,
the trigger is a procedural macro #[derive(offsets)].

The procedural macro is perhaps a bit overengineered, but it helps
introducing some idioms that will be useful in the future as well.

Signed-off-by: Junjie Mao <junjie.mao@hotmail.com>
Co-developed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-11-05 14:18:16 +01:00
Paolo Bonzini
39c8faefb5 rust: create a cargo workspace
Workspaces allows tracking dependencies for multiple crates at once,
by having a single Cargo.lock file at the top of the rust/ tree.
Because QEMU's Cargo.lock files have to be synchronized with the versions
of crates in subprojects/, using a workspace avoids the need to copy
over the Cargo.lock file when adding a new device (and thus a new crate)
under rust/hw/.

In addition, workspaces let cargo download and build dependencies just
once.  While right now we have one leaf crate (hw/char/pl011), this
will not be the case once more devices are added.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-11-05 14:18:16 +01:00
Paolo Bonzini
907d2bbb80 rust: synchronize dependencies between subprojects and Cargo.lock
The next commit will introduce a new build.rs dependency for rust/qemu-api,
version_check.  Before adding it, ensure that all dependencies are
synchronized between the Meson- and cargo-based build systems.

Note that it's not clear whether in the long term we'll use Cargo for
anything; it seems that the three main uses (clippy, rustfmt, rustdoc)
can all be invoked manually---either via glue code in QEMU, or by
extending Meson to gain the relevant functionality.  However, for
the time being we're stuck with Cargo so it should at least look at
the same code as the rest of the build system.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-11-05 14:18:16 +01:00
Paolo Bonzini
e90d470733 rust: cleanup module_init!, use it from #[derive(Object)]
Remove the duplicate code by using the module_init! macro; at the same time,
simplify how module_init! is used, by taking inspiration from the implementation
of #[derive(Object)].

Reviewed-by: Junjie Mao <junjie.mao@hotmail.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-11-05 14:18:15 +01:00
Paolo Bonzini
4f7521916d rust: modernize link_section usage for ELF platforms
Some newer ABI implementations do not provide .ctors; and while
some linkers rewrite .ctors into .init_array, not all of them do.
Use the newer .init_array ABI, which works more reliably, and
apply it to all non-Apple, non-Windows platforms.

This is similar to how the ctor crate operates; without this change,
"#[derive(Object)]" does not work on Fedora 41.

Reviewed-by: Junjie Mao <junjie.mao@hotmail.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-11-05 14:18:15 +01:00
Paolo Bonzini
be3fc97a09 meson: import rust module into a global variable
Tested-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-11-05 14:18:15 +01:00
Paolo Bonzini
95e82f9b38 meson: check in main meson.build for native Rust compiler
A working native Rust compiler is always needed in order to compile Rust
code, even when cross compiling, in order to build the procedural macros
that QEMU uses.

Right now, the check is done in rust/qemu-api-macros/meson.build, but this
has two disadvantages.  First, it makes the build fail when the Meson "rust"
option is set to "auto" (instead, Rust support should be disabled).  Second,
add_languages() is one of the few functions that are executed even by
"meson introspect", except that "meson introspect" executes both branches
of "if" statements!  Therefore, "meson introspect" tries to look for a
Rust compiler even if the option is disabled---and then fails because
the compiler is required by rust/qemu-api-macros/meson.build.  This is
visible for example if the compilation host has a stale
scripts/meson-buildoptions.sh and no rustc installed.

Both issues can be fixed by moving the check to the main meson.build,
together with the check for the cross compiler.

Reported-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2024-10-14 15:48:05 +01:00
Manos Pitsidianakis
2b74dd9180 rust: add utility procedural macro crate
This commit adds a helper crate library, qemu-api-macros for derive (and
other procedural) macros to be used along qemu-api.

It needs to be a separate library because in Rust, procedural macros, or
macros that can generate arbitrary code, need to be special separate
compilation units.

Only one macro is introduced in this patch, #[derive(Object)]. It
generates a constructor to register a QOM TypeInfo on init and it must
be used on types that implement qemu_api::definitions::ObjectImpl trait.

Reviewed-by: Junjie Mao <junjie.mao@hotmail.com>
Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Link: https://lore.kernel.org/r/dd645642406a6dc2060c6f3f17db2bc77ed67b59.1727961605.git.manos.pitsidianakis@linaro.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2024-10-11 12:32:17 +02:00