qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/33] First Rust update for QEMU 10.2
@ 2025-09-08 10:49 Paolo Bonzini
  2025-09-08 10:49 ` [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu Paolo Bonzini
                   ` (33 more replies)
  0 siblings, 34 replies; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel

This includes:
- bumping MSRV to 1.83.0 to support const_refs_to_static
- Zhao's safe, builder-based implementation of migration callbacks
- Manos's qdev properties macro.  While bit-based properties are
  not yet supported, that's a small change overall.
- the Rust crate split from Marc-André
- adding proc macro aliases in individual crates, also from Marc-André

I'm still not convinced about having "bql" depend on "migration",
but I am convinced by the crate split between "util" and "bql",
so we can move the implementation of VMState from "bql" to
"migration" later if needed.

For the purpose of getting this in as an easy-to-use base for future
development, I'm disabling CI from Debian and Ubuntu.  The plan is:
- that Debian will require trixie to enable Rust usage
- that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions
  (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318)
- that Marc-André or someone else will add Rust to other CI jobs

Also planned for 10.2 from me is using rustc to link emulators
(already written but requires Meson 1.9.1) and possibly some work on
BQL-free HPET, especially with respect to migration.  I'll probably
resubmit https://lore.kernel.org/qemu-devel/20250717062727.305466-5-pbonzini@redhat.com/
too, since improvements to migration support will likely result in
more procedural macro attributes.

I'll send the next update some time in October, probably once
Ubuntu gets the newer version of the compiler.  I'm now going to
mostly disappear for a week. :)

Paolo

Manos Pitsidianakis (1):
  rust: add qdev Device derive macro

Marc-André Lureau (22):
  docs/rust: update msrv
  rust: remove unused global qemu "allocator"
  rust: add workspace authors
  rust: move vmstate_clock!() to qdev module
  rust: move VMState handling to QOM module
  rust: move Cell vmstate impl
  rust: split Rust-only "common" crate
  rust: make build.rs generic over various ./rust/projects
  rust: split "util" crate
  rust: split "migration" crate
  rust: split "bql" crate
  rust: split "qom" crate
  rust: split "chardev" crate
  rust: split "system" crate
  rust: split "hwcore" crate
  rust: rename qemu_api_macros -> qemu_macros
  rust/hpet: drop now unneeded qemu_api dep
  rust/pl011: drop dependency on qemu_api
  rust: repurpose qemu_api -> tests
  rust: re-export qemu_macros internal helper in "bits"
  rust: re-export qemu macros from common/qom/hwcore
  docs: update rust.rst

Paolo Bonzini (9):
  ci: temporarily remove rust from Debian and Ubuntu
  configure: bump Meson to 1.9.0 for use with Rust
  meson, cargo: require Rust 1.83.0
  rust: add missing const markers for MSRV==1.83.0
  rust: use inline const expressions
  rust: vmstate: use const_refs_to_static
  rust: qdev: const_refs_to_static
  rust: meson: remove unnecessary complication in device crates
  rust: do not inline do_init_io

Zhao Liu (1):
  rust: vmstate: convert to use builder pattern

 MAINTAINERS                                   |  12 +-
 docs/devel/rust.rst                           |  93 ++--
 configure                                     |   4 +-
 meson.build                                   |  10 +-
 rust/bql/wrapper.h                            |  27 +
 rust/chardev/wrapper.h                        |  28 +
 rust/hw/char/pl011/wrapper.h                  |  51 ++
 rust/hw/core/wrapper.h                        |  32 ++
 rust/{qemu-api => migration}/wrapper.h        |  20 -
 rust/qom/wrapper.h                            |  27 +
 rust/system/wrapper.h                         |  29 +
 rust/util/wrapper.h                           |  32 ++
 .gitlab-ci.d/buildtest.yml                    |   6 +-
 clippy.toml                                   |   2 +-
 python/scripts/vendor.py                      |   4 +-
 python/wheels/meson-1.8.1-py3-none-any.whl    | Bin 1013001 -> 0 bytes
 python/wheels/meson-1.9.0-py3-none-any.whl    | Bin 0 -> 1029634 bytes
 pythondeps.toml                               |   4 +-
 rust/Cargo.lock                               | 125 ++++-
 rust/Cargo.toml                               |  19 +-
 .../src/bindings.rs => bindings/src/lib.rs}   |  12 +-
 rust/bits/Cargo.toml                          |   2 +-
 rust/bits/meson.build                         |   2 +-
 rust/bits/src/lib.rs                          |  13 +-
 rust/bql/Cargo.toml                           |  23 +
 rust/bql/build.rs                             |   1 +
 rust/bql/meson.build                          |  52 ++
 rust/bql/src/bindings.rs                      |  25 +
 rust/{qemu-api => bql}/src/cell.rs            | 336 ++----------
 rust/bql/src/lib.rs                           |  29 +
 rust/chardev/Cargo.toml                       |  23 +
 rust/chardev/build.rs                         |   1 +
 rust/chardev/meson.build                      |  41 ++
 rust/chardev/src/bindings.rs                  |  36 ++
 rust/{qemu-api => chardev}/src/chardev.rs     |  25 +-
 rust/chardev/src/lib.rs                       |   6 +
 rust/common/Cargo.toml                        |  20 +
 rust/common/meson.build                       |  34 ++
 rust/{qemu-api => common}/src/assertions.rs   |  24 +-
 rust/{qemu-api => common}/src/bitops.rs       |   1 -
 rust/{qemu-api => common}/src/callbacks.rs    |  37 +-
 rust/{qemu-api => common}/src/errno.rs        |  19 +-
 rust/common/src/lib.rs                        |  22 +
 rust/common/src/opaque.rs                     | 236 ++++++++
 rust/{qemu-api => common}/src/uninit.rs       |   8 +-
 rust/common/src/zeroable.rs                   |  18 +
 rust/hw/char/pl011/Cargo.toml                 |  10 +-
 rust/hw/char/pl011/build.rs                   |   1 +
 rust/hw/char/pl011/meson.build                |  39 +-
 rust/hw/char/pl011/src/bindings.rs            |  27 +
 rust/hw/char/pl011/src/device.rs              | 195 +++----
 rust/hw/char/pl011/src/lib.rs                 |   1 +
 rust/hw/char/pl011/src/registers.rs           |   4 +-
 rust/hw/core/Cargo.toml                       |  26 +
 rust/hw/core/build.rs                         |   1 +
 rust/hw/core/meson.build                      |  80 +++
 rust/hw/core/src/bindings.rs                  |  41 ++
 rust/{qemu-api => hw/core}/src/irq.rs         |  16 +-
 rust/hw/core/src/lib.rs                       |  15 +
 rust/{qemu-api => hw/core}/src/qdev.rs        | 111 ++--
 rust/{qemu-api => hw/core}/src/sysbus.rs      |  24 +-
 rust/{qemu-api => hw/core}/tests/tests.rs     |  52 +-
 rust/hw/timer/hpet/Cargo.toml                 |   9 +-
 rust/hw/timer/hpet/meson.build                |  12 +-
 rust/hw/timer/hpet/src/device.rs              | 227 ++++----
 rust/hw/timer/hpet/src/fw_cfg.rs              |   6 +-
 rust/meson.build                              |  15 +-
 rust/migration/Cargo.toml                     |  20 +
 rust/migration/build.rs                       |   1 +
 rust/migration/meson.build                    |  53 ++
 rust/migration/src/bindings.rs                |  48 ++
 rust/migration/src/lib.rs                     |   6 +
 rust/{qemu-api => migration}/src/vmstate.rs   | 519 +++++++++++-------
 rust/qemu-api/.gitignore                      |   2 -
 rust/qemu-api/README.md                       |  19 -
 rust/qemu-api/meson.build                     | 114 ----
 rust/qemu-api/src/lib.rs                      | 174 ------
 rust/qemu-api/src/prelude.rs                  |  31 --
 rust/qemu-api/src/zeroable.rs                 |  37 --
 .../Cargo.toml                                |   2 +-
 .../meson.build                               |  10 +-
 .../src/bits.rs                               |   0
 .../src/lib.rs                                | 170 +++++-
 .../src/tests.rs                              | 115 +++-
 rust/qom/Cargo.toml                           |  23 +
 rust/qom/build.rs                             |   1 +
 rust/qom/meson.build                          |  43 ++
 rust/qom/src/bindings.rs                      |  25 +
 rust/qom/src/lib.rs                           |  13 +
 rust/qom/src/prelude.rs                       |  12 +
 rust/{qemu-api => qom}/src/qom.rs             |  31 +-
 rust/system/Cargo.toml                        |  21 +
 rust/system/build.rs                          |   1 +
 rust/system/meson.build                       |  42 ++
 rust/system/src/bindings.rs                   |  41 ++
 rust/system/src/lib.rs                        |   6 +
 rust/{qemu-api => system}/src/memory.rs       |  21 +-
 rust/tests/Cargo.toml                         |  26 +
 rust/tests/meson.build                        |  14 +
 .../tests/vmstate_tests.rs                    | 160 +++---
 rust/{qemu-api => util}/Cargo.toml            |  14 +-
 rust/{qemu-api => util}/build.rs              |  10 +-
 rust/util/meson.build                         |  55 ++
 rust/util/src/bindings.rs                     |  25 +
 rust/{qemu-api => util}/src/error.rs          |   6 +-
 rust/util/src/lib.rs                          |   9 +
 rust/{qemu-api => util}/src/log.rs            |  16 +-
 rust/{qemu-api => util}/src/module.rs         |   2 +-
 rust/{qemu-api => util}/src/timer.rs          |  16 +-
 109 files changed, 2868 insertions(+), 1569 deletions(-)
 create mode 100644 rust/bql/wrapper.h
 create mode 100644 rust/chardev/wrapper.h
 create mode 100644 rust/hw/char/pl011/wrapper.h
 create mode 100644 rust/hw/core/wrapper.h
 rename rust/{qemu-api => migration}/wrapper.h (77%)
 create mode 100644 rust/qom/wrapper.h
 create mode 100644 rust/system/wrapper.h
 create mode 100644 rust/util/wrapper.h
 delete mode 100644 python/wheels/meson-1.8.1-py3-none-any.whl
 create mode 100644 python/wheels/meson-1.9.0-py3-none-any.whl
 rename rust/{qemu-api/src/bindings.rs => bindings/src/lib.rs} (89%)
 create mode 100644 rust/bql/Cargo.toml
 create mode 120000 rust/bql/build.rs
 create mode 100644 rust/bql/meson.build
 create mode 100644 rust/bql/src/bindings.rs
 rename rust/{qemu-api => bql}/src/cell.rs (70%)
 create mode 100644 rust/bql/src/lib.rs
 create mode 100644 rust/chardev/Cargo.toml
 create mode 120000 rust/chardev/build.rs
 create mode 100644 rust/chardev/meson.build
 create mode 100644 rust/chardev/src/bindings.rs
 rename rust/{qemu-api => chardev}/src/chardev.rs (94%)
 create mode 100644 rust/chardev/src/lib.rs
 create mode 100644 rust/common/Cargo.toml
 create mode 100644 rust/common/meson.build
 rename rust/{qemu-api => common}/src/assertions.rs (86%)
 rename rust/{qemu-api => common}/src/bitops.rs (98%)
 rename rust/{qemu-api => common}/src/callbacks.rs (85%)
 rename rust/{qemu-api => common}/src/errno.rs (96%)
 create mode 100644 rust/common/src/lib.rs
 create mode 100644 rust/common/src/opaque.rs
 rename rust/{qemu-api => common}/src/uninit.rs (91%)
 create mode 100644 rust/common/src/zeroable.rs
 create mode 120000 rust/hw/char/pl011/build.rs
 create mode 100644 rust/hw/char/pl011/src/bindings.rs
 create mode 100644 rust/hw/core/Cargo.toml
 create mode 120000 rust/hw/core/build.rs
 create mode 100644 rust/hw/core/meson.build
 create mode 100644 rust/hw/core/src/bindings.rs
 rename rust/{qemu-api => hw/core}/src/irq.rs (93%)
 create mode 100644 rust/hw/core/src/lib.rs
 rename rust/{qemu-api => hw/core}/src/qdev.rs (82%)
 rename rust/{qemu-api => hw/core}/src/sysbus.rs (90%)
 rename rust/{qemu-api => hw/core}/tests/tests.rs (78%)
 create mode 100644 rust/migration/Cargo.toml
 create mode 120000 rust/migration/build.rs
 create mode 100644 rust/migration/meson.build
 create mode 100644 rust/migration/src/bindings.rs
 create mode 100644 rust/migration/src/lib.rs
 rename rust/{qemu-api => migration}/src/vmstate.rs (61%)
 delete mode 100644 rust/qemu-api/.gitignore
 delete mode 100644 rust/qemu-api/README.md
 delete mode 100644 rust/qemu-api/meson.build
 delete mode 100644 rust/qemu-api/src/lib.rs
 delete mode 100644 rust/qemu-api/src/prelude.rs
 delete mode 100644 rust/qemu-api/src/zeroable.rs
 rename rust/{qemu-api-macros => qemu-macros}/Cargo.toml (94%)
 rename rust/{qemu-api-macros => qemu-macros}/meson.build (63%)
 rename rust/{qemu-api-macros => qemu-macros}/src/bits.rs (100%)
 rename rust/{qemu-api-macros => qemu-macros}/src/lib.rs (54%)
 rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (50%)
 create mode 100644 rust/qom/Cargo.toml
 create mode 120000 rust/qom/build.rs
 create mode 100644 rust/qom/meson.build
 create mode 100644 rust/qom/src/bindings.rs
 create mode 100644 rust/qom/src/lib.rs
 create mode 100644 rust/qom/src/prelude.rs
 rename rust/{qemu-api => qom}/src/qom.rs (97%)
 create mode 100644 rust/system/Cargo.toml
 create mode 120000 rust/system/build.rs
 create mode 100644 rust/system/meson.build
 create mode 100644 rust/system/src/bindings.rs
 create mode 100644 rust/system/src/lib.rs
 rename rust/{qemu-api => system}/src/memory.rs (90%)
 create mode 100644 rust/tests/Cargo.toml
 create mode 100644 rust/tests/meson.build
 rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (78%)
 rename rust/{qemu-api => util}/Cargo.toml (55%)
 rename rust/{qemu-api => util}/build.rs (81%)
 create mode 100644 rust/util/meson.build
 create mode 100644 rust/util/src/bindings.rs
 rename rust/{qemu-api => util}/src/error.rs (98%)
 create mode 100644 rust/util/src/lib.rs
 rename rust/{qemu-api => util}/src/log.rs (93%)
 rename rust/{qemu-api => util}/src/module.rs (97%)
 rename rust/{qemu-api => util}/src/timer.rs (91%)

-- 
2.51.0



^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11  3:14   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 03/33] meson, cargo: require Rust 1.83.0 Paolo Bonzini
                   ` (32 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 .gitlab-ci.d/buildtest.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
index d888a600637..47dbba9d0db 100644
--- a/.gitlab-ci.d/buildtest.yml
+++ b/.gitlab-ci.d/buildtest.yml
@@ -39,9 +39,9 @@ build-system-ubuntu:
     job: amd64-ubuntu2204-container
   variables:
     IMAGE: ubuntu2204
-    CONFIGURE_ARGS: --enable-docs --enable-rust
+    CONFIGURE_ARGS: --enable-docs
     TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
-    MAKE_CHECK_ARGS: check-build check-doc
+    MAKE_CHECK_ARGS: check-build
 
 check-system-ubuntu:
   extends: .native_test_job_template
@@ -69,7 +69,7 @@ build-system-debian:
     job: amd64-debian-container
   variables:
     IMAGE: debian
-    CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust
+    CONFIGURE_ARGS: --with-coroutine=sigaltstack
     TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu
       sparc-softmmu xtensa-softmmu
     MAKE_CHECK_ARGS: check-build
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 03/33] meson, cargo: require Rust 1.83.0
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
  2025-09-08 10:49 ` [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11  3:19   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 04/33] rust: add missing const markers for MSRV==1.83.0 Paolo Bonzini
                   ` (31 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/devel/rust.rst | 14 +++++---------
 meson.build         |  6 +++---
 clippy.toml         |  2 +-
 rust/Cargo.toml     |  2 +-
 4 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index b6737536c69..e0ee4a9837d 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -75,21 +75,17 @@ Note that doctests require all ``.o`` files from the build to be available.
 Supported tools
 '''''''''''''''
 
-QEMU supports rustc version 1.77.0 and newer.  Notably, the following features
-are missing:
+QEMU supports rustc version 1.83.0 and newer.  The following features
+from relatively new versions of Rust are not used for historical reasons;
+patches are welcome:
 
 * inline const expression (stable in 1.79.0), currently worked around with
   associated constants in the ``FnCall`` trait.
 
-* associated constants have to be explicitly marked ``'static`` (`changed in
+* associated constants are still explicitly marked ``'static`` (`changed in
   1.81.0`__)
 
-* ``&raw`` (stable in 1.82.0).  Use ``addr_of!`` and ``addr_of_mut!`` instead,
-  though hopefully the need for raw pointers will go down over time.
-
-* ``new_uninit`` (stable in 1.82.0).  This is used internally by the ``pinned_init``
-  crate, which is planned for inclusion in QEMU, but it can be easily patched
-  out.
+* ``&raw`` (stable in 1.82.0).
 
 * referencing statics in constants (stable in 1.83.0).  For now use a const
   function; this is an important limitation for QEMU's migration stream
diff --git a/meson.build b/meson.build
index fa6186db334..ef3f52b951d 100644
--- a/meson.build
+++ b/meson.build
@@ -94,12 +94,12 @@ have_rust = have_rust and add_languages('rust', native: true,
     required: get_option('rust').disable_auto_if(not have_system))
 if have_rust
   rustc = meson.get_compiler('rust')
-  if rustc.version().version_compare('<1.77.0')
+  if rustc.version().version_compare('<1.83.0')
     if get_option('rust').enabled()
-      error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.77.0')
+      error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.83.0')
     else
       warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.')
-      message('Please upgrade to at least 1.77.0 to use Rust.')
+      message('Please upgrade to at least 1.83.0 to use Rust.')
       have_rust = false
     endif
   endif
diff --git a/clippy.toml b/clippy.toml
index 90161729838..204f5713c00 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -1,3 +1,3 @@
 doc-valid-idents = ["IrDA", "PrimeCell", ".."]
 allow-mixed-uninlined-format-args = false
-msrv = "1.77.0"
+msrv = "1.83.0"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 6f8884eb30b..99c275f2d9f 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -13,7 +13,7 @@ edition = "2021"
 homepage = "https://www.qemu.org"
 license = "GPL-2.0-or-later"
 repository = "https://gitlab.com/qemu-project/qemu/"
-rust-version = "1.77.0"
+rust-version = "1.83.0"
 
 [workspace.dependencies]
 anyhow = "~1.0"
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 04/33] rust: add missing const markers for MSRV==1.83.0
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
  2025-09-08 10:49 ` [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu Paolo Bonzini
  2025-09-08 10:49 ` [PATCH 03/33] meson, cargo: require Rust 1.83.0 Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11  3:20   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 05/33] rust: use inline const expressions Paolo Bonzini
                   ` (30 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel

Rust 1.83 allows more functions to be marked const.
Fix clippy with bumped minimum supported Rust version.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/bits/src/lib.rs            | 6 +++---
 rust/qemu-api/src/assertions.rs | 4 ++--
 rust/qemu-api/src/cell.rs       | 6 ++----
 rust/qemu-api/src/qom.rs        | 6 +++---
 rust/qemu-api/src/timer.rs      | 2 +-
 rust/qemu-api/src/uninit.rs     | 6 +++---
 6 files changed, 14 insertions(+), 16 deletions(-)

diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs
index d485d6bd110..e9d15ad0cb5 100644
--- a/rust/bits/src/lib.rs
+++ b/rust/bits/src/lib.rs
@@ -165,19 +165,19 @@ pub const fn into_bits(self) -> $type {
 
             #[allow(dead_code)]
             #[inline(always)]
-            pub fn set(&mut self, rhs: Self) {
+            pub const fn set(&mut self, rhs: Self) {
                 self.0 |= rhs.0;
             }
 
             #[allow(dead_code)]
             #[inline(always)]
-            pub fn clear(&mut self, rhs: Self) {
+            pub const fn clear(&mut self, rhs: Self) {
                 self.0 &= !rhs.0;
             }
 
             #[allow(dead_code)]
             #[inline(always)]
-            pub fn toggle(&mut self, rhs: Self) {
+            pub const fn toggle(&mut self, rhs: Self) {
                 self.0 ^= rhs.0;
             }
 
diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs
index a2d38c877df..e74fa3ef826 100644
--- a/rust/qemu-api/src/assertions.rs
+++ b/rust/qemu-api/src/assertions.rs
@@ -81,8 +81,8 @@ macro_rules! assert_field_type {
     (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => {
         const _: () = {
             #[allow(unused)]
-            fn assert_field_type($param_name: &$t) {
-                fn types_must_be_equal<T, U>(_: &T)
+            const fn assert_field_type($param_name: &$t) {
+                const fn types_must_be_equal<T, U>(_: &T)
                 where
                     T: $crate::assertions::EqType<Itself = U>,
                 {
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index 27063b049d5..148a13e3f56 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -980,8 +980,7 @@ pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
     /// but the functions containing the dereference are usually safe.  The
     /// value returned from `uninit()` must be initialized and pinned before
     /// calling them.
-    #[allow(clippy::missing_const_for_fn)]
-    pub unsafe fn uninit() -> Self {
+    pub const unsafe fn uninit() -> Self {
         Self {
             value: UnsafeCell::new(MaybeUninit::uninit()),
             _pin: PhantomPinned,
@@ -997,8 +996,7 @@ pub unsafe fn uninit() -> Self {
     /// but the functions containing the dereference are usually safe.  The
     /// value returned from `uninit()` must be pinned (and possibly initialized)
     /// before calling them.
-    #[allow(clippy::missing_const_for_fn)]
-    pub unsafe fn zeroed() -> Self {
+    pub const unsafe fn zeroed() -> Self {
         Self {
             value: UnsafeCell::new(MaybeUninit::zeroed()),
             _pin: PhantomPinned,
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index e20ee014cb1..014ffb1fd88 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -307,7 +307,7 @@ impl<T: ObjectType> ParentInit<'_, T> {
     /// Fields beyond `Object` could be uninitialized and it's your
     /// responsibility to avoid that they're used when the pointer is
     /// dereferenced, either directly or through a cast.
-    pub fn as_object_mut_ptr(&self) -> *mut bindings::Object {
+    pub const fn as_object_mut_ptr(&self) -> *mut bindings::Object {
         self.as_object_ptr().cast_mut()
     }
 
@@ -318,7 +318,7 @@ pub fn as_object_mut_ptr(&self) -> *mut bindings::Object {
     /// Fields beyond `Object` could be uninitialized and it's your
     /// responsibility to avoid that they're used when the pointer is
     /// dereferenced, either directly or through a cast.
-    pub fn as_object_ptr(&self) -> *const bindings::Object {
+    pub const fn as_object_ptr(&self) -> *const bindings::Object {
         self.0.as_ptr().cast()
     }
 }
@@ -336,7 +336,7 @@ impl<'a, T: ObjectImpl> ParentInit<'a, T> {
     /// However, while the fields of the resulting reference are initialized,
     /// calls might use uninitialized fields of the subclass.  It is your
     /// responsibility to avoid this.
-    pub unsafe fn upcast<U: ObjectType>(&self) -> &'a U
+    pub const unsafe fn upcast<U: ObjectType>(&self) -> &'a U
     where
         T::ParentType: IsA<U>,
     {
diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs
index 0a2d111d490..0daec62f926 100644
--- a/rust/qemu-api/src/timer.rs
+++ b/rust/qemu-api/src/timer.rs
@@ -39,7 +39,7 @@ impl Timer {
     ///
     /// The timer must be initialized before it is armed with
     /// [`modify`](Self::modify).
-    pub unsafe fn new() -> Self {
+    pub const unsafe fn new() -> Self {
         // SAFETY: requirements relayed to callers of Timer::new
         Self(unsafe { Opaque::zeroed() })
     }
diff --git a/rust/qemu-api/src/uninit.rs b/rust/qemu-api/src/uninit.rs
index 04123b4ae99..b0a829729dd 100644
--- a/rust/qemu-api/src/uninit.rs
+++ b/rust/qemu-api/src/uninit.rs
@@ -12,7 +12,7 @@ pub struct MaybeUninitField<'a, T, U> {
 
 impl<'a, T, U> MaybeUninitField<'a, T, U> {
     #[doc(hidden)]
-    pub fn new(parent: &'a mut MaybeUninit<T>, child: *mut U) -> Self {
+    pub const fn new(parent: &'a mut MaybeUninit<T>, child: *mut U) -> Self {
         MaybeUninitField { parent, child }
     }
 
@@ -21,7 +21,7 @@ pub fn new(parent: &'a mut MaybeUninit<T>, child: *mut U) -> Self {
     /// Because the `MaybeUninitField` remembers the containing object,
     /// it is possible to use it in foreign APIs that initialize the
     /// child.
-    pub fn parent(f: &Self) -> *const T {
+    pub const fn parent(f: &Self) -> *const T {
         f.parent.as_ptr()
     }
 
@@ -30,7 +30,7 @@ pub fn parent(f: &Self) -> *const T {
     /// Because the `MaybeUninitField` remembers the containing object,
     /// it is possible to use it in foreign APIs that initialize the
     /// child.
-    pub fn parent_mut(f: &mut Self) -> *mut T {
+    pub const fn parent_mut(f: &mut Self) -> *mut T {
         f.parent.as_mut_ptr()
     }
 }
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 05/33] rust: use inline const expressions
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (2 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 04/33] rust: add missing const markers for MSRV==1.83.0 Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11  6:15   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 06/33] rust: add qdev Device derive macro Paolo Bonzini
                   ` (29 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel

They were stabilized in Rust 1.79.0.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/devel/rust.rst            |  9 +++------
 rust/qemu-api/src/callbacks.rs | 27 +--------------------------
 rust/qemu-api/src/chardev.rs   |  2 +-
 rust/qemu-api/src/qdev.rs      |  2 +-
 rust/qemu-api/src/timer.rs     |  2 +-
 rust/qemu-api/src/vmstate.rs   |  2 +-
 6 files changed, 8 insertions(+), 36 deletions(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index e0ee4a9837d..98e3a33a3ce 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -79,9 +79,6 @@ QEMU supports rustc version 1.83.0 and newer.  The following features
 from relatively new versions of Rust are not used for historical reasons;
 patches are welcome:
 
-* inline const expression (stable in 1.79.0), currently worked around with
-  associated constants in the ``FnCall`` trait.
-
 * associated constants are still explicitly marked ``'static`` (`changed in
   1.81.0`__)
 
@@ -97,9 +94,9 @@ patches are welcome:
   before QEMU can use them.  For now, there is special code in
   ``util/error.c`` to support non-NUL-terminated file names.
 
-* associated const equality would be nice to have for some users of
-  ``callbacks::FnCall``, but is still experimental.  ``ASSERT_IS_SOME``
-  replaces it.
+Associated const equality would be nice to have for some users of
+``callbacks::FnCall``, but is still experimental.  Const assertions
+are used instead.
 
 __ https://github.com/rust-lang/rust/pull/125258
 
diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs
index 9642a16eb89..dbe2305f509 100644
--- a/rust/qemu-api/src/callbacks.rs
+++ b/rust/qemu-api/src/callbacks.rs
@@ -113,31 +113,6 @@
 /// This is always true for zero-capture closures and function pointers, as long
 /// as the code is able to name the function in the first place.
 pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
-    /// Referring to this internal constant asserts that the `Self` type is
-    /// zero-sized.  Can be replaced by an inline const expression in
-    /// Rust 1.79.0+.
-    const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
-
-    /// Referring to this constant asserts that the `Self` type is an actual
-    /// function type, which can be used to catch incorrect use of `()`
-    /// at compile time.
-    ///
-    /// # Examples
-    ///
-    /// ```compile_fail
-    /// # use qemu_api::callbacks::FnCall;
-    /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
-    ///     let _: () = F::ASSERT_IS_SOME;
-    ///     F::call((s,))
-    /// }
-    ///
-    /// let s: String = call_it((), "hello world"); // does not compile
-    /// ```
-    ///
-    /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in
-    /// Rust 1.79.0 or newer.
-    const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) };
-
     /// `true` if `Self` is an actual function type and not `()`.
     ///
     /// # Examples
@@ -195,7 +170,7 @@ unsafe impl<F, $($args,)* R> FnCall<($($args,)*), R> for F
 
             #[inline(always)]
             fn call(a: ($($args,)*)) -> R {
-                let _: () = Self::ASSERT_ZERO_SIZED;
+                const { assert!(mem::size_of::<Self>() == 0) };
 
                 // SAFETY: the safety of this method is the condition for implementing
                 // `FnCall`.  As to the `NonNull` idiom to create a zero-sized type,
diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs
index 6e0590d758e..cb27be52569 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -138,7 +138,7 @@ pub fn enable_handlers<
             F::call((owner, event))
         }
 
-        let _: () = CanReceiveFn::ASSERT_IS_SOME;
+        const { assert!(CanReceiveFn::IS_SOME) };
         let receive_cb: Option<unsafe extern "C" fn(*mut c_void, *const u8, c_int)> =
             if ReceiveFn::is_some() {
                 Some(rust_receive_cb::<T, ReceiveFn>)
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 36f02fb57db..52d54a4494e 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -373,7 +373,7 @@ fn do_init_gpio_in(
             }
         }
 
-        let _: () = F::ASSERT_IS_SOME;
+        const { assert!(F::IS_SOME) };
         unsafe extern "C" fn rust_irq_handler<T, F: for<'a> FnCall<(&'a T, u32, u32)>>(
             opaque: *mut c_void,
             line: c_int,
diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs
index 0daec62f926..1e639eaf221 100644
--- a/rust/qemu-api/src/timer.rs
+++ b/rust/qemu-api/src/timer.rs
@@ -56,7 +56,7 @@ pub fn init_full<'timer, 'opaque: 'timer, T, F>(
     ) where
         F: for<'a> FnCall<(&'a T,)>,
     {
-        let _: () = F::ASSERT_IS_SOME;
+        const { assert!(F::IS_SOME) };
 
         /// timer expiration callback
         unsafe extern "C" fn rust_timer_handler<T, F: for<'a> FnCall<(&'a T,)>>(
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 812f390d780..8515e382135 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -457,7 +457,7 @@ macro_rules! vmstate_exist_fn {
         const fn test_cb_builder__<T, F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>>(
             _phantom: ::core::marker::PhantomData<F>,
         ) -> $crate::vmstate::VMSFieldExistCb {
-            let _: () = F::ASSERT_IS_SOME;
+            const { assert!(F::IS_SOME) };
             $crate::vmstate::rust_vms_test_field_exists::<T, F>
         }
 
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 06/33] rust: add qdev Device derive macro
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (3 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 05/33] rust: use inline const expressions Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11  6:58   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 07/33] rust: vmstate: convert to use builder pattern Paolo Bonzini
                   ` (28 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Manos Pitsidianakis

From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>

Add derive macro for declaring qdev properties directly above the field
definitions. To do this, we split DeviceImpl::properties method on a
separate trait so we can implement only that part in the derive macro
expansion (we cannot partially implement the DeviceImpl trait).

Adding a `property` attribute above the field declaration will generate
a `qemu_api::bindings::Property` array member in the device's property
list.

Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Link: https://lore.kernel.org/r/20250711-rust-qdev-properties-v3-1-e198624416fb@linaro.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/hw/char/pl011/src/device.rs  |  31 +-----
 rust/hw/timer/hpet/src/device.rs  |   6 +-
 rust/qemu-api-macros/src/lib.rs   | 152 +++++++++++++++++++++++++++++-
 rust/qemu-api-macros/src/tests.rs | 111 ++++++++++++++++++++++
 rust/qemu-api/src/qdev.rs         |  70 ++++++++++++--
 rust/qemu-api/tests/tests.rs      |  24 +----
 6 files changed, 336 insertions(+), 58 deletions(-)

diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index ceb71dd99b6..37944635202 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -9,7 +9,6 @@
 };
 
 use qemu_api::{
-    bindings::{qdev_prop_bool, qdev_prop_chr},
     chardev::{CharBackend, Chardev, Event},
     impl_vmstate_forward,
     irq::{IRQState, InterruptSource},
@@ -17,7 +16,7 @@
     log_mask_ln,
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
-    qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
+    qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
     qom::{ObjectImpl, Owned, ParentField, ParentInit},
     static_assert,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
@@ -105,12 +104,13 @@ pub struct PL011Registers {
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
 /// PL011 Device Model in QEMU
 pub struct PL011State {
     pub parent_obj: ParentField<SysBusDevice>,
     pub iomem: MemoryRegion,
     #[doc(alias = "chr")]
+    #[property(rename = "chardev")]
     pub char_backend: CharBackend,
     pub regs: BqlRefCell<PL011Registers>,
     /// QEMU interrupts
@@ -129,6 +129,7 @@ pub struct PL011State {
     #[doc(alias = "clk")]
     pub clock: Owned<Clock>,
     #[doc(alias = "migrate_clk")]
+    #[property(rename = "migrate-clk", default = true)]
     pub migrate_clock: bool,
 }
 
@@ -176,9 +177,6 @@ impl ObjectImpl for PL011State {
 }
 
 impl DeviceImpl for PL011State {
-    fn properties() -> &'static [Property] {
-        &PL011_PROPERTIES
-    }
     fn vmsd() -> Option<&'static VMStateDescription> {
         Some(&VMSTATE_PL011)
     }
@@ -690,7 +688,7 @@ pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
 /// PL011 Luminary device model.
 pub struct PL011Luminary {
     parent_obj: ParentField<PL011State>,
@@ -782,22 +780,3 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
     },
     ..Zeroable::ZERO
 };
-
-qemu_api::declare_properties! {
-    PL011_PROPERTIES,
-    qemu_api::define_property!(
-        c"chardev",
-        PL011State,
-        char_backend,
-        unsafe { &qdev_prop_chr },
-        CharBackend
-    ),
-    qemu_api::define_property!(
-        c"migrate-clk",
-        PL011State,
-        migrate_clock,
-        unsafe { &qdev_prop_bool },
-        bool,
-        default = true
-    ),
-}
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index acf7251029e..01d5a0dd70c 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -1031,11 +1031,15 @@ impl ObjectImpl for HPETState {
     ..Zeroable::ZERO
 };
 
-impl DeviceImpl for HPETState {
+// SAFETY: HPET_PROPERTIES is a valid Property array constructed with the
+// qemu_api::declare_properties macro.
+unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState {
     fn properties() -> &'static [Property] {
         &HPET_PROPERTIES
     }
+}
 
+impl DeviceImpl for HPETState {
     fn vmsd() -> Option<&'static VMStateDescription> {
         Some(&VMSTATE_HPET)
     }
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index 959726efe6d..97b2c214b62 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -3,10 +3,11 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 use proc_macro::TokenStream;
-use quote::quote;
+use quote::{quote, quote_spanned, ToTokens};
 use syn::{
-    parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
-    DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant,
+    parse::Parse, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned,
+    token::Comma, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token,
+    Variant,
 };
 mod bits;
 use bits::BitsConstInternal;
@@ -152,6 +153,151 @@ pub const fn raw_get(slot: *mut Self) -> *mut <Self as ::qemu_api::cell::Wrapper
     })
 }
 
+#[derive(Debug)]
+enum DevicePropertyName {
+    CStr(syn::LitCStr),
+    Str(syn::LitStr),
+}
+
+#[derive(Debug)]
+struct DeviceProperty {
+    rename: Option<DevicePropertyName>,
+    defval: Option<syn::Expr>,
+}
+
+impl Parse for DeviceProperty {
+    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+        let _: syn::Token![#] = input.parse()?;
+        let bracketed;
+        _ = syn::bracketed!(bracketed in input);
+        let attribute = bracketed.parse::<syn::Ident>()?;
+        debug_assert_eq!(&attribute.to_string(), "property");
+        let mut retval = Self {
+            rename: None,
+            defval: None,
+        };
+        let content;
+        _ = syn::parenthesized!(content in bracketed);
+        while !content.is_empty() {
+            let value: syn::Ident = content.parse()?;
+            if value == "rename" {
+                let _: syn::Token![=] = content.parse()?;
+                if retval.rename.is_some() {
+                    return Err(syn::Error::new(
+                        value.span(),
+                        "`rename` can only be used at most once",
+                    ));
+                }
+                if content.peek(syn::LitStr) {
+                    retval.rename = Some(DevicePropertyName::Str(content.parse::<syn::LitStr>()?));
+                } else {
+                    retval.rename =
+                        Some(DevicePropertyName::CStr(content.parse::<syn::LitCStr>()?));
+                }
+            } else if value == "default" {
+                let _: syn::Token![=] = content.parse()?;
+                if retval.defval.is_some() {
+                    return Err(syn::Error::new(
+                        value.span(),
+                        "`default` can only be used at most once",
+                    ));
+                }
+                retval.defval = Some(content.parse()?);
+            } else {
+                return Err(syn::Error::new(
+                    value.span(),
+                    format!("unrecognized field `{value}`"),
+                ));
+            }
+
+            if !content.is_empty() {
+                let _: syn::Token![,] = content.parse()?;
+            }
+        }
+        Ok(retval)
+    }
+}
+
+#[proc_macro_derive(Device, attributes(property))]
+pub fn derive_device(input: TokenStream) -> TokenStream {
+    let input = parse_macro_input!(input as DeriveInput);
+
+    derive_device_or_error(input)
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
+}
+
+fn derive_device_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
+    is_c_repr(&input, "#[derive(Device)]")?;
+    let properties: Vec<(syn::Field, DeviceProperty)> = get_fields(&input, "#[derive(Device)]")?
+        .iter()
+        .flat_map(|f| {
+            f.attrs
+                .iter()
+                .filter(|a| a.path().is_ident("property"))
+                .map(|a| Ok((f.clone(), syn::parse2(a.to_token_stream())?)))
+        })
+        .collect::<Result<Vec<_>, Error>>()?;
+    let name = &input.ident;
+    let mut properties_expanded = vec![];
+
+    for (field, prop) in properties {
+        let DeviceProperty { rename, defval } = prop;
+        let field_name = field.ident.unwrap();
+        macro_rules! str_to_c_str {
+            ($value:expr, $span:expr) => {{
+                let (value, span) = ($value, $span);
+                let cstr = std::ffi::CString::new(value.as_str()).map_err(|err| {
+                    Error::new(
+                        span,
+                        format!(
+                            "Property name `{value}` cannot be represented as a C string: {err}"
+                        ),
+                    )
+                })?;
+                let cstr_lit = syn::LitCStr::new(&cstr, span);
+                Ok(quote! { #cstr_lit })
+            }};
+        }
+
+        let prop_name = rename.map_or_else(
+            || str_to_c_str!(field_name.to_string(), field_name.span()),
+            |rename| -> Result<proc_macro2::TokenStream, Error> {
+                match rename {
+                    DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }),
+                    DevicePropertyName::Str(str_lit) => {
+                        str_to_c_str!(str_lit.value(), str_lit.span())
+                    }
+                }
+            },
+        )?;
+        let field_ty = field.ty.clone();
+        let qdev_prop = quote! { <#field_ty as ::qemu_api::qdev::QDevProp>::VALUE };
+        let set_default = defval.is_some();
+        let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 }));
+        properties_expanded.push(quote! {
+            ::qemu_api::bindings::Property {
+                name: ::std::ffi::CStr::as_ptr(#prop_name),
+                info: #qdev_prop ,
+                offset: ::core::mem::offset_of!(#name, #field_name) as isize,
+                set_default: #set_default,
+                defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
+                ..::qemu_api::zeroable::Zeroable::ZERO
+            }
+        });
+    }
+
+    Ok(quote_spanned! {input.span() =>
+        unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name {
+            fn properties() -> &'static [::qemu_api::bindings::Property] {
+                static PROPERTIES: &[::qemu_api::bindings::Property] = &[#(#properties_expanded),*];
+
+                PROPERTIES
+            }
+        }
+    })
+}
+
 #[proc_macro_derive(Wrapper)]
 pub fn derive_opaque(input: TokenStream) -> TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs
index 6028cdbc4c3..aafffcdce91 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -36,6 +36,117 @@ macro_rules! derive_compile {
     }};
 }
 
+#[test]
+fn test_derive_device() {
+    // Check that repr(C) is used
+    derive_compile_fail!(
+        derive_device_or_error,
+        quote! {
+            #[derive(Device)]
+            struct Foo {
+                _unused: [u8; 0],
+            }
+        },
+        "#[repr(C)] required for #[derive(Device)]"
+    );
+    // Check that invalid/misspelled attributes raise an error
+    derive_compile_fail!(
+        derive_device_or_error,
+        quote! {
+            #[repr(C)]
+            #[derive(Device)]
+            struct DummyState {
+                #[property(defalt = true)]
+                migrate_clock: bool,
+            }
+        },
+        "unrecognized field `defalt`"
+    );
+    // Check that repeated attributes are not allowed:
+    derive_compile_fail!(
+        derive_device_or_error,
+        quote! {
+            #[repr(C)]
+            #[derive(Device)]
+            struct DummyState {
+                #[property(rename = "migrate-clk", rename = "migrate-clk", default = true)]
+                migrate_clock: bool,
+            }
+        },
+        "`rename` can only be used at most once"
+    );
+    derive_compile_fail!(
+        derive_device_or_error,
+        quote! {
+            #[repr(C)]
+            #[derive(Device)]
+            struct DummyState {
+                #[property(default = true, default = true)]
+                migrate_clock: bool,
+            }
+        },
+        "`default` can only be used at most once"
+    );
+    // Check that the field name is preserved when `rename` isn't used:
+    derive_compile!(
+        derive_device_or_error,
+        quote! {
+            #[repr(C)]
+            #[derive(Device)]
+            pub struct DummyState {
+                parent: ParentField<DeviceState>,
+                #[property(default = true)]
+                migrate_clock: bool,
+            }
+        },
+        quote! {
+            unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
+                fn properties() -> &'static [::qemu_api::bindings::Property] {
+                    static PROPERTIES: &[::qemu_api::bindings::Property] =
+                        &[::qemu_api::bindings::Property {
+                            name: ::std::ffi::CStr::as_ptr(c"migrate_clock"),
+                            info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
+                            offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
+                            set_default: true,
+                            defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
+                            ..::qemu_api::zeroable::Zeroable::ZERO
+                        }];
+                    PROPERTIES
+                }
+            }
+        }
+    );
+    // Check that `rename` value is used for the property name when used:
+    derive_compile!(
+        derive_device_or_error,
+        quote! {
+            #[repr(C)]
+            #[derive(Device)]
+            pub struct DummyState {
+                parent: ParentField<DeviceState>,
+                #[property(rename = "migrate-clk", default = true)]
+                migrate_clock: bool,
+            }
+        },
+        quote! {
+            unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
+                fn properties() -> &'static [::qemu_api::bindings::Property] {
+                    static PROPERTIES: &[::qemu_api::bindings::Property] =
+                        &[::qemu_api::bindings::Property {
+                            name: ::std::ffi::CStr::as_ptr(c"migrate-clk"),
+                            info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
+                            offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
+                            set_default: true,
+                            defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
+                            ..::qemu_api::zeroable::Zeroable::ZERO
+                        }];
+                    PROPERTIES
+                }
+            }
+        }
+    );
+}
+
 #[test]
 fn test_derive_object() {
     derive_compile_fail!(
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 52d54a4494e..6a58a00e3fc 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -101,8 +101,67 @@ pub trait ResettablePhasesImpl {
     T::EXIT.unwrap()(unsafe { state.as_ref() }, typ);
 }
 
+/// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type.
+///
+/// This trait is used by [`qemu_api_macros::Device`] derive macro.
+///
+/// Base types that already have `qdev_prop_*` globals in the QEMU API should
+/// use those values as exported by the [`bindings`] module, instead of
+/// redefining them.
+///
+/// # Safety
+///
+/// This trait is marked as `unsafe` because currently having a `const` refer to
+/// an `extern static` as a reference instead of a raw pointer results in this
+/// compiler error:
+///
+/// ```text
+/// constructing invalid value: encountered reference to `extern` static in `const`
+/// ```
+///
+/// This is because the compiler generally might dereference a normal reference
+/// during const evaluation, but not in this case (if it did, it'd need to
+/// dereference the raw pointer so this would fail to compile).
+///
+/// It is the implementer's responsibility to provide a valid
+/// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe.
+pub unsafe trait QDevProp {
+    const VALUE: *const bindings::PropertyInfo;
+}
+
+/// Use [`bindings::qdev_prop_bool`] for `bool`.
+unsafe impl QDevProp for bool {
+    const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_bool };
+}
+
+/// Use [`bindings::qdev_prop_uint64`] for `u64`.
+unsafe impl QDevProp for u64 {
+    const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 };
+}
+
+/// Use [`bindings::qdev_prop_chr`] for [`crate::chardev::CharBackend`].
+unsafe impl QDevProp for crate::chardev::CharBackend {
+    const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr };
+}
+
+/// Trait to define device properties.
+///
+/// # Safety
+///
+/// Caller is responsible for the validity of properties array.
+pub unsafe trait DevicePropertiesImpl {
+    /// An array providing the properties that the user can set on the
+    /// device.  Not a `const` because referencing statics in constants
+    /// is unstable until Rust 1.83.0.
+    fn properties() -> &'static [Property] {
+        &[]
+    }
+}
+
 /// Trait providing the contents of [`DeviceClass`].
-pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
+pub trait DeviceImpl:
+    ObjectImpl + ResettablePhasesImpl + DevicePropertiesImpl + IsA<DeviceState>
+{
     /// _Realization_ is the second stage of device creation. It contains
     /// all operations that depend on device properties and can fail (note:
     /// this is not yet supported for Rust devices).
@@ -111,13 +170,6 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
     /// with the function pointed to by `REALIZE`.
     const REALIZE: Option<fn(&Self) -> Result<()>> = None;
 
-    /// An array providing the properties that the user can set on the
-    /// device.  Not a `const` because referencing statics in constants
-    /// is unstable until Rust 1.83.0.
-    fn properties() -> &'static [Property] {
-        &[]
-    }
-
     /// A `VMStateDescription` providing the migration format for the device
     /// Not a `const` because referencing statics in constants is unstable
     /// until Rust 1.83.0.
@@ -175,7 +227,7 @@ pub fn class_init<T: DeviceImpl>(&mut self) {
         if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
             self.vmsd = vmsd;
         }
-        let prop = <T as DeviceImpl>::properties();
+        let prop = <T as DevicePropertiesImpl>::properties();
         if !prop.is_empty() {
             unsafe {
                 bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len());
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index a658a49fcfd..aff3eecd654 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -5,11 +5,10 @@
 use std::{ffi::CStr, ptr::addr_of};
 
 use qemu_api::{
-    bindings::{module_call_init, module_init_type, qdev_prop_bool},
+    bindings::{module_call_init, module_init_type},
     cell::{self, BqlCell},
-    declare_properties, define_property,
     prelude::*,
-    qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
+    qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
     qom::{ObjectImpl, ParentField},
     sysbus::SysBusDevice,
     vmstate::VMStateDescription,
@@ -26,9 +25,10 @@
 };
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
 pub struct DummyState {
     parent: ParentField<DeviceState>,
+    #[property(rename = "migrate-clk", default = true)]
     migrate_clock: bool,
 }
 
@@ -44,17 +44,6 @@ pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) {
     }
 }
 
-declare_properties! {
-    DUMMY_PROPERTIES,
-        define_property!(
-            c"migrate-clk",
-            DummyState,
-            migrate_clock,
-            unsafe { &qdev_prop_bool },
-            bool
-        ),
-}
-
 unsafe impl ObjectType for DummyState {
     type Class = DummyClass;
     const TYPE_NAME: &'static CStr = c"dummy";
@@ -69,16 +58,13 @@ impl ObjectImpl for DummyState {
 impl ResettablePhasesImpl for DummyState {}
 
 impl DeviceImpl for DummyState {
-    fn properties() -> &'static [Property] {
-        &DUMMY_PROPERTIES
-    }
     fn vmsd() -> Option<&'static VMStateDescription> {
         Some(&VMSTATE)
     }
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
 pub struct DummyChildState {
     parent: ParentField<DummyState>,
 }
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 07/33] rust: vmstate: convert to use builder pattern
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (4 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 06/33] rust: add qdev Device derive macro Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-08 10:49 ` [PATCH 08/33] rust: vmstate: use const_refs_to_static Paolo Bonzini
                   ` (27 subsequent siblings)
  33 siblings, 0 replies; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Zhao Liu

From: Zhao Liu <zhao1.liu@intel.com>

Similar to MemoryRegionOps, the builder pattern has two advantages:
1) it makes it possible to build a VMStateDescription that knows which
types it will be invoked on; 2) it provides a way to wrap the callbacks
and let devices avoid "unsafe".

Unfortunately, building a static VMStateDescription requires the
builder methods to be "const", and because the VMStateFields are
*also* static, this requires const_refs_static.  So this requires
Rust 1.83.0.

Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/devel/rust.rst                  |   2 +-
 rust/hw/char/pl011/src/device.rs     | 131 +++++++--------
 rust/hw/timer/hpet/src/device.rs     | 162 ++++++++----------
 rust/qemu-api/src/errno.rs           |  11 +-
 rust/qemu-api/src/qdev.rs            |   6 +-
 rust/qemu-api/src/vmstate.rs         | 242 ++++++++++++++++++++++++++-
 rust/qemu-api/tests/tests.rs         |  16 +-
 rust/qemu-api/tests/vmstate_tests.rs | 124 +++++++-------
 8 files changed, 450 insertions(+), 244 deletions(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 98e3a33a3ce..aca29e55c05 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -162,7 +162,7 @@ module           status
 ``qom``          stable
 ``sysbus``       stable
 ``timer``        stable
-``vmstate``      proof of concept
+``vmstate``      stable
 ``zeroable``     stable
 ================ ======================
 
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 37944635202..21611d9c099 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -3,9 +3,8 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 use std::{
-    ffi::{c_int, c_void, CStr},
-    mem::size_of,
-    ptr::NonNull,
+    ffi::CStr,
+    mem::size_of
 };
 
 use qemu_api::{
@@ -21,9 +20,8 @@
     static_assert,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
     uninit_field_mut,
-    vmstate::VMStateDescription,
+    vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
     vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
-    zeroable::Zeroable,
 };
 
 use crate::registers::{self, Interrupt, RegisterOffset};
@@ -177,8 +175,8 @@ impl ObjectImpl for PL011State {
 }
 
 impl DeviceImpl for PL011State {
-    fn vmsd() -> Option<&'static VMStateDescription> {
-        Some(&VMSTATE_PL011)
+    fn vmsd() -> Option<VMStateDescription<Self>> {
+        Some(VMSTATE_PL011)
     }
     const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
 }
@@ -467,10 +465,10 @@ pub fn fifo_rx_put(&mut self, value: registers::Data) -> bool {
         false
     }
 
-    pub fn post_load(&mut self) -> Result<(), ()> {
+    pub fn post_load(&mut self) -> Result<(), vmstate::InvalidError> {
         /* Sanity-check input state */
         if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
-            return Err(());
+            return Err(vmstate::InvalidError);
         }
 
         if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
@@ -529,6 +527,10 @@ const fn clock_update(&self, _event: ClockEvent) {
         /* pl011_trace_baudrate_change(s); */
     }
 
+    pub fn clock_needed(&self) -> bool {
+        self.migrate_clock
+    }
+
     fn post_init(&self) {
         self.init_mmio(&self.iomem);
         for irq in self.interrupts.iter() {
@@ -645,7 +647,7 @@ fn update(&self) {
         }
     }
 
-    pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
+    pub fn post_load(&self, _version_id: u8) -> Result<(), vmstate::InvalidError> {
         self.regs.borrow_mut().post_load()
     }
 }
@@ -715,68 +717,53 @@ impl DeviceImpl for PL011Luminary {}
 impl ResettablePhasesImpl for PL011Luminary {}
 impl SysBusDeviceImpl for PL011Luminary {}
 
-extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
-    let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
-    unsafe { state.as_ref().migrate_clock }
-}
-
 /// Migration subsection for [`PL011State`] clock.
-static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
-    name: c"pl011/clock".as_ptr(),
-    version_id: 1,
-    minimum_version_id: 1,
-    needed: Some(pl011_clock_needed),
-    fields: vmstate_fields! {
-        vmstate_clock!(PL011State, clock),
-    },
-    ..Zeroable::ZERO
-};
+static VMSTATE_PL011_CLOCK: VMStateDescription<PL011State> =
+    VMStateDescriptionBuilder::<PL011State>::new()
+        .name(c"pl011/clock")
+        .version_id(1)
+        .minimum_version_id(1)
+        .needed(&PL011State::clock_needed)
+        .fields(vmstate_fields! {
+             vmstate_clock!(PL011State, clock),
+        })
+        .build();
 
-extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
-    let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
-    let result = unsafe { state.as_ref().post_load(version_id as u32) };
-    if result.is_err() {
-        -1
-    } else {
-        0
-    }
-}
+static VMSTATE_PL011_REGS: VMStateDescription<PL011Registers> =
+    VMStateDescriptionBuilder::<PL011Registers>::new()
+        .name(c"pl011/regs")
+        .version_id(2)
+        .minimum_version_id(2)
+        .fields(vmstate_fields! {
+            vmstate_of!(PL011Registers, flags),
+            vmstate_of!(PL011Registers, line_control),
+            vmstate_of!(PL011Registers, receive_status_error_clear),
+            vmstate_of!(PL011Registers, control),
+            vmstate_of!(PL011Registers, dmacr),
+            vmstate_of!(PL011Registers, int_enabled),
+            vmstate_of!(PL011Registers, int_level),
+            vmstate_of!(PL011Registers, read_fifo),
+            vmstate_of!(PL011Registers, ilpr),
+            vmstate_of!(PL011Registers, ibrd),
+            vmstate_of!(PL011Registers, fbrd),
+            vmstate_of!(PL011Registers, ifl),
+            vmstate_of!(PL011Registers, read_pos),
+            vmstate_of!(PL011Registers, read_count),
+            vmstate_of!(PL011Registers, read_trigger),
+        })
+        .build();
 
-static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
-    name: c"pl011/regs".as_ptr(),
-    version_id: 2,
-    minimum_version_id: 2,
-    fields: vmstate_fields! {
-        vmstate_of!(PL011Registers, flags),
-        vmstate_of!(PL011Registers, line_control),
-        vmstate_of!(PL011Registers, receive_status_error_clear),
-        vmstate_of!(PL011Registers, control),
-        vmstate_of!(PL011Registers, dmacr),
-        vmstate_of!(PL011Registers, int_enabled),
-        vmstate_of!(PL011Registers, int_level),
-        vmstate_of!(PL011Registers, read_fifo),
-        vmstate_of!(PL011Registers, ilpr),
-        vmstate_of!(PL011Registers, ibrd),
-        vmstate_of!(PL011Registers, fbrd),
-        vmstate_of!(PL011Registers, ifl),
-        vmstate_of!(PL011Registers, read_pos),
-        vmstate_of!(PL011Registers, read_count),
-        vmstate_of!(PL011Registers, read_trigger),
-    },
-    ..Zeroable::ZERO
-};
-
-pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
-    name: c"pl011".as_ptr(),
-    version_id: 2,
-    minimum_version_id: 2,
-    post_load: Some(pl011_post_load),
-    fields: vmstate_fields! {
-        vmstate_unused!(core::mem::size_of::<u32>()),
-        vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
-    },
-    subsections: vmstate_subsections! {
-        VMSTATE_PL011_CLOCK
-    },
-    ..Zeroable::ZERO
-};
+pub const VMSTATE_PL011: VMStateDescription<PL011State> =
+    VMStateDescriptionBuilder::<PL011State>::new()
+        .name(c"pl011")
+        .version_id(2)
+        .minimum_version_id(2)
+        .post_load(&PL011State::post_load)
+        .fields(vmstate_fields! {
+            vmstate_unused!(core::mem::size_of::<u32>()),
+            vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
+        })
+        .subsections(vmstate_subsections! {
+             VMSTATE_PL011_CLOCK
+        })
+        .build();
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 01d5a0dd70c..955cf869ff6 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 use std::{
-    ffi::{c_int, c_void, CStr},
+    ffi::CStr,
     mem::MaybeUninit,
     pin::Pin,
     ptr::{addr_of_mut, null_mut, NonNull},
@@ -27,9 +27,8 @@
     sysbus::{SysBusDevice, SysBusDeviceImpl},
     timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
     uninit_field_mut,
-    vmstate::VMStateDescription,
+    vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
-    zeroable::Zeroable,
 };
 
 use crate::fw_cfg::HPETFwConfig;
@@ -213,6 +212,10 @@ pub struct HPETTimer {
     last: u64,
 }
 
+// SAFETY: Sync is not automatically derived due to the `state` field,
+// which is always dereferenced to a shared reference.
+unsafe impl Sync for HPETTimer {}
+
 impl HPETTimer {
     fn new(index: u8, state: *const HPETState) -> HPETTimer {
         HPETTimer {
@@ -841,7 +844,7 @@ fn write(&self, addr: hwaddr, value: u64, size: u32) {
         }
     }
 
-    fn pre_save(&self) -> i32 {
+    fn pre_save(&self) -> Result<(), vmstate::Infallible> {
         if self.is_hpet_enabled() {
             self.counter.set(self.get_ticks());
         }
@@ -852,10 +855,10 @@ fn pre_save(&self) -> i32 {
          * that was configured.
          */
         self.num_timers_save.set(self.num_timers as u8);
-        0
+        Ok(())
     }
 
-    fn post_load(&self, _version_id: u8) -> i32 {
+    fn post_load(&self, _version_id: u8) -> Result<(), vmstate::Infallible> {
         for timer in self.timers.iter().take(self.num_timers) {
             let mut t = timer.borrow_mut();
 
@@ -869,7 +872,7 @@ fn post_load(&self, _version_id: u8) -> i32 {
                 .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns());
         }
 
-        0
+        Ok(())
     }
 
     fn is_rtc_irq_level_needed(&self) -> bool {
@@ -939,97 +942,66 @@ impl ObjectImpl for HPETState {
     ),
 }
 
-unsafe extern "C" fn hpet_rtc_irq_level_needed(opaque: *mut c_void) -> bool {
-    // SAFETY:
-    // the pointer is convertible to a reference
-    let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() };
-    state.is_rtc_irq_level_needed()
-}
+static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription<HPETState> =
+    VMStateDescriptionBuilder::<HPETState>::new()
+        .name(c"hpet/rtc_irq_level")
+        .version_id(1)
+        .minimum_version_id(1)
+        .needed(&HPETState::is_rtc_irq_level_needed)
+        .fields(vmstate_fields! {
+            vmstate_of!(HPETState, rtc_irq_level),
+        })
+        .build();
 
-unsafe extern "C" fn hpet_offset_needed(opaque: *mut c_void) -> bool {
-    // SAFETY:
-    // the pointer is convertible to a reference
-    let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() };
-    state.is_offset_needed()
-}
+static VMSTATE_HPET_OFFSET: VMStateDescription<HPETState> =
+    VMStateDescriptionBuilder::<HPETState>::new()
+        .name(c"hpet/offset")
+        .version_id(1)
+        .minimum_version_id(1)
+        .needed(&HPETState::is_offset_needed)
+        .fields(vmstate_fields! {
+            vmstate_of!(HPETState, hpet_offset),
+        })
+        .build();
 
-unsafe extern "C" fn hpet_pre_save(opaque: *mut c_void) -> c_int {
-    // SAFETY:
-    // the pointer is convertible to a reference
-    let state: &mut HPETState =
-        unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() };
-    state.pre_save() as c_int
-}
-
-unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
-    // SAFETY:
-    // the pointer is convertible to a reference
-    let state: &mut HPETState =
-        unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() };
-    let version: u8 = version_id.try_into().unwrap();
-    state.post_load(version) as c_int
-}
-
-static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription {
-    name: c"hpet/rtc_irq_level".as_ptr(),
-    version_id: 1,
-    minimum_version_id: 1,
-    needed: Some(hpet_rtc_irq_level_needed),
-    fields: vmstate_fields! {
-        vmstate_of!(HPETState, rtc_irq_level),
-    },
-    ..Zeroable::ZERO
-};
-
-static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription {
-    name: c"hpet/offset".as_ptr(),
-    version_id: 1,
-    minimum_version_id: 1,
-    needed: Some(hpet_offset_needed),
-    fields: vmstate_fields! {
-        vmstate_of!(HPETState, hpet_offset),
-    },
-    ..Zeroable::ZERO
-};
-
-static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription {
-    name: c"hpet_timer".as_ptr(),
-    version_id: 1,
-    minimum_version_id: 1,
-    fields: vmstate_fields! {
-        vmstate_of!(HPETTimer, index),
-        vmstate_of!(HPETTimer, config),
-        vmstate_of!(HPETTimer, cmp),
-        vmstate_of!(HPETTimer, fsb),
-        vmstate_of!(HPETTimer, period),
-        vmstate_of!(HPETTimer, wrap_flag),
-        vmstate_of!(HPETTimer, qemu_timer),
-    },
-    ..Zeroable::ZERO
-};
+static VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> =
+    VMStateDescriptionBuilder::<HPETTimer>::new()
+        .name(c"hpet_timer")
+        .version_id(1)
+        .minimum_version_id(1)
+        .fields(vmstate_fields! {
+            vmstate_of!(HPETTimer, index),
+            vmstate_of!(HPETTimer, config),
+            vmstate_of!(HPETTimer, cmp),
+            vmstate_of!(HPETTimer, fsb),
+            vmstate_of!(HPETTimer, period),
+            vmstate_of!(HPETTimer, wrap_flag),
+            vmstate_of!(HPETTimer, qemu_timer),
+        })
+        .build();
 
 const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match";
 
-static VMSTATE_HPET: VMStateDescription = VMStateDescription {
-    name: c"hpet".as_ptr(),
-    version_id: 2,
-    minimum_version_id: 2,
-    pre_save: Some(hpet_pre_save),
-    post_load: Some(hpet_post_load),
-    fields: vmstate_fields! {
-        vmstate_of!(HPETState, config),
-        vmstate_of!(HPETState, int_status),
-        vmstate_of!(HPETState, counter),
-        vmstate_of!(HPETState, num_timers_save),
-        vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
-        vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0),
-    },
-    subsections: vmstate_subsections! {
-        VMSTATE_HPET_RTC_IRQ_LEVEL,
-        VMSTATE_HPET_OFFSET,
-    },
-    ..Zeroable::ZERO
-};
+const VMSTATE_HPET: VMStateDescription<HPETState> =
+    VMStateDescriptionBuilder::<HPETState>::new()
+        .name(c"hpet")
+        .version_id(2)
+        .minimum_version_id(2)
+        .pre_save(&HPETState::pre_save)
+        .post_load(&HPETState::post_load)
+        .fields(vmstate_fields! {
+            vmstate_of!(HPETState, config),
+            vmstate_of!(HPETState, int_status),
+            vmstate_of!(HPETState, counter),
+            vmstate_of!(HPETState, num_timers_save),
+            vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
+            vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0),
+        })
+        .subsections(vmstate_subsections!(
+            VMSTATE_HPET_RTC_IRQ_LEVEL,
+            VMSTATE_HPET_OFFSET,
+        ))
+        .build();
 
 // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the
 // qemu_api::declare_properties macro.
@@ -1040,8 +1012,8 @@ fn properties() -> &'static [Property] {
 }
 
 impl DeviceImpl for HPETState {
-    fn vmsd() -> Option<&'static VMStateDescription> {
-        Some(&VMSTATE_HPET)
+    fn vmsd() -> Option<VMStateDescription<Self>> {
+        Some(VMSTATE_HPET)
     }
 
     const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs
index 18d101448b9..507850fe33c 100644
--- a/rust/qemu-api/src/errno.rs
+++ b/rust/qemu-api/src/errno.rs
@@ -7,7 +7,10 @@
 //! convention.  This module provides functions to portably convert an integer
 //! into an [`io::Result`] and back.
 
-use std::{convert::TryFrom, io, io::ErrorKind};
+use std::{
+    convert::{self, TryFrom},
+    io::{self, ErrorKind},
+};
 
 /// An `errno` value that can be converted into an [`io::Error`]
 pub struct Errno(pub u16);
@@ -99,6 +102,12 @@ fn from(value: io::Error) -> Errno {
     }
 }
 
+impl From<convert::Infallible> for Errno {
+    fn from(_value: convert::Infallible) -> Errno {
+        panic!("unreachable")
+    }
+}
+
 /// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`]
 /// for the "right" set of types.
 mod traits {
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 6a58a00e3fc..4dda8c81131 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -173,7 +173,7 @@ pub trait DeviceImpl:
     /// A `VMStateDescription` providing the migration format for the device
     /// Not a `const` because referencing statics in constants is unstable
     /// until Rust 1.83.0.
-    fn vmsd() -> Option<&'static VMStateDescription> {
+    fn vmsd() -> Option<VMStateDescription<Self>> {
         None
     }
 }
@@ -225,7 +225,9 @@ pub fn class_init<T: DeviceImpl>(&mut self) {
             self.realize = Some(rust_realize_fn::<T>);
         }
         if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
-            self.vmsd = vmsd;
+            // Give a 'static lifetime to the return value of vmsd().
+            // Temporary until vmsd() can be changed into a const.
+            self.vmsd = Box::leak(Box::new(vmsd.get()));
         }
         let prop = <T as DevicePropertiesImpl>::properties();
         if !prop.is_empty() {
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 8515e382135..f5f1ea5590f 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -24,12 +24,24 @@
 //!   `include/migration/vmstate.h`. These are not type-safe and only provide
 //!   functionality that is missing from `vmstate_of!`.
 
-use core::{marker::PhantomData, mem, ptr::NonNull};
-use std::ffi::{c_int, c_void};
+pub use std::convert::Infallible;
+use std::{
+    error::Error,
+    ffi::{c_int, c_void, CStr},
+    fmt, io,
+    marker::PhantomData,
+    mem,
+    ptr::NonNull,
+};
 
-pub use crate::bindings::{VMStateDescription, VMStateField};
+pub use crate::bindings::{MigrationPriority, VMStateField};
 use crate::{
-    bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable,
+    bindings::{self, VMStateFlags},
+    callbacks::FnCall,
+    errno::{into_neg_errno, Errno},
+    prelude::*,
+    qom::Owned,
+    zeroable::Zeroable,
 };
 
 /// This macro is used to call a function with a generic argument bound
@@ -440,7 +452,7 @@ pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), b
     opaque: *mut c_void,
     version_id: c_int,
 ) -> bool {
-    // SAFETY: the opaque was passed as a reference to `T`.
+    // SAFETY: assumes vmstate_struct! is used correctly
     let owner: &T = unsafe { &*(opaque.cast::<T>()) };
     let version: u8 = version_id.try_into().unwrap();
     F::call((owner, version))
@@ -490,7 +502,7 @@ macro_rules! vmstate_struct {
             },
             size: ::core::mem::size_of::<$type>(),
             flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
-            vmsd: $vmsd,
+            vmsd: $vmsd.as_ref(),
             $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
             ..$crate::zeroable::Zeroable::ZERO
          } $(.with_varray_flag_unchecked(
@@ -594,11 +606,225 @@ macro_rules! vmstate_subsections {
     ($($subsection:expr),*$(,)*) => {{
         static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[
             $({
-                static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection;
+                static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get();
                 ::core::ptr::addr_of!(_SUBSECTION)
             }),*,
             ::core::ptr::null()
         ]);
-        _SUBSECTIONS.0.as_ptr()
+        &_SUBSECTIONS
     }}
 }
+
+pub struct VMStateDescription<T>(bindings::VMStateDescription, PhantomData<fn(&T)>);
+
+// SAFETY: When a *const T is passed to the callbacks, the call itself
+// is done in a thread-safe manner.  The invocation is okay as long as
+// T itself is `Sync`.
+unsafe impl<T: Sync> Sync for VMStateDescription<T> {}
+
+#[derive(Clone)]
+pub struct VMStateDescriptionBuilder<T>(bindings::VMStateDescription, PhantomData<fn(&T)>);
+
+#[derive(Debug)]
+pub struct InvalidError;
+
+impl Error for InvalidError {}
+
+impl std::fmt::Display for InvalidError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "invalid migration data")
+    }
+}
+
+impl From<InvalidError> for Errno {
+    fn from(_value: InvalidError) -> Errno {
+        io::ErrorKind::InvalidInput.into()
+    }
+}
+
+unsafe extern "C" fn vmstate_no_version_cb<
+    T,
+    F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>,
+>(
+    opaque: *mut c_void,
+) -> c_int {
+    // SAFETY: assumes vmstate_struct! is used correctly
+    let result = F::call((unsafe { &*(opaque.cast::<T>()) },));
+    into_neg_errno(result)
+}
+
+unsafe extern "C" fn vmstate_post_load_cb<
+    T,
+    F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>,
+>(
+    opaque: *mut c_void,
+    version_id: c_int,
+) -> c_int {
+    // SAFETY: assumes vmstate_struct! is used correctly
+    let owner: &T = unsafe { &*(opaque.cast::<T>()) };
+    let version: u8 = version_id.try_into().unwrap();
+    let result = F::call((owner, version));
+    into_neg_errno(result)
+}
+
+unsafe extern "C" fn vmstate_needed_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
+    opaque: *mut c_void,
+) -> bool {
+    // SAFETY: assumes vmstate_struct! is used correctly
+    F::call((unsafe { &*(opaque.cast::<T>()) },))
+}
+
+unsafe extern "C" fn vmstate_dev_unplug_pending_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
+    opaque: *mut c_void,
+) -> bool {
+    // SAFETY: assumes vmstate_struct! is used correctly
+    F::call((unsafe { &*(opaque.cast::<T>()) },))
+}
+
+impl<T> VMStateDescriptionBuilder<T> {
+    #[must_use]
+    pub const fn name(mut self, name_str: &CStr) -> Self {
+        self.0.name = ::std::ffi::CStr::as_ptr(name_str);
+        self
+    }
+
+    #[must_use]
+    pub const fn unmigratable(mut self) -> Self {
+        self.0.unmigratable = true;
+        self
+    }
+
+    #[must_use]
+    pub const fn early_setup(mut self) -> Self {
+        self.0.early_setup = true;
+        self
+    }
+
+    #[must_use]
+    pub const fn version_id(mut self, version: u8) -> Self {
+        self.0.version_id = version as c_int;
+        self
+    }
+
+    #[must_use]
+    pub const fn minimum_version_id(mut self, min_version: u8) -> Self {
+        self.0.minimum_version_id = min_version as c_int;
+        self
+    }
+
+    #[must_use]
+    pub const fn priority(mut self, priority: MigrationPriority) -> Self {
+        self.0.priority = priority;
+        self
+    }
+
+    #[must_use]
+    pub const fn pre_load<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
+        mut self,
+        _f: &F,
+    ) -> Self {
+        self.0.pre_load = if F::IS_SOME {
+            Some(vmstate_no_version_cb::<T, F>)
+        } else {
+            None
+        };
+        self
+    }
+
+    #[must_use]
+    pub const fn post_load<F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>>(
+        mut self,
+        _f: &F,
+    ) -> Self {
+        self.0.post_load = if F::IS_SOME {
+            Some(vmstate_post_load_cb::<T, F>)
+        } else {
+            None
+        };
+        self
+    }
+
+    #[must_use]
+    pub const fn pre_save<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
+        mut self,
+        _f: &F,
+    ) -> Self {
+        self.0.pre_save = if F::IS_SOME {
+            Some(vmstate_no_version_cb::<T, F>)
+        } else {
+            None
+        };
+        self
+    }
+
+    #[must_use]
+    pub const fn post_save<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
+        mut self,
+        _f: &F,
+    ) -> Self {
+        self.0.post_save = if F::IS_SOME {
+            Some(vmstate_no_version_cb::<T, F>)
+        } else {
+            None
+        };
+        self
+    }
+
+    #[must_use]
+    pub const fn needed<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
+        self.0.needed = if F::IS_SOME {
+            Some(vmstate_needed_cb::<T, F>)
+        } else {
+            None
+        };
+        self
+    }
+
+    #[must_use]
+    pub const fn unplug_pending<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
+        self.0.dev_unplug_pending = if F::IS_SOME {
+            Some(vmstate_dev_unplug_pending_cb::<T, F>)
+        } else {
+            None
+        };
+        self
+    }
+
+    #[must_use]
+    pub const fn fields(mut self, fields: *const VMStateField) -> Self {
+        self.0.fields = fields;
+        self
+    }
+
+    #[must_use]
+    pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self {
+        self.0.subsections = subs.0.as_ptr();
+        self
+    }
+
+    #[must_use]
+    pub const fn build(self) -> VMStateDescription<T> {
+        VMStateDescription::<T>(self.0, PhantomData)
+    }
+
+    #[must_use]
+    pub const fn new() -> Self {
+        Self(bindings::VMStateDescription::ZERO, PhantomData)
+    }
+}
+
+impl<T> Default for VMStateDescriptionBuilder<T> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<T> VMStateDescription<T> {
+    pub const fn get(&self) -> bindings::VMStateDescription {
+        self.0
+    }
+
+    pub const fn as_ref(&self) -> &bindings::VMStateDescription {
+        &self.0
+    }
+}
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index aff3eecd654..4d4e4653f38 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -11,18 +11,16 @@
     qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
     qom::{ObjectImpl, ParentField},
     sysbus::SysBusDevice,
-    vmstate::VMStateDescription,
-    zeroable::Zeroable,
+    vmstate::{VMStateDescription, VMStateDescriptionBuilder},
 };
 
 mod vmstate_tests;
 
 // Test that macros can compile.
-pub static VMSTATE: VMStateDescription = VMStateDescription {
-    name: c"name".as_ptr(),
-    unmigratable: true,
-    ..Zeroable::ZERO
-};
+pub const VMSTATE: VMStateDescription<DummyState> = VMStateDescriptionBuilder::<DummyState>::new()
+    .name(c"name")
+    .unmigratable()
+    .build();
 
 #[repr(C)]
 #[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
@@ -58,8 +56,8 @@ impl ObjectImpl for DummyState {
 impl ResettablePhasesImpl for DummyState {}
 
 impl DeviceImpl for DummyState {
-    fn vmsd() -> Option<&'static VMStateDescription> {
-        Some(&VMSTATE)
+    fn vmsd() -> Option<VMStateDescription<Self>> {
+        Some(VMSTATE)
     }
 }
 
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs
index bded836eb60..8ffc2779d46 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -16,9 +16,8 @@
     },
     cell::{BqlCell, Opaque},
     impl_vmstate_forward,
-    vmstate::{VMStateDescription, VMStateField},
+    vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField},
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
-    zeroable::Zeroable,
 };
 
 const FOO_ARRAY_MAX: usize = 3;
@@ -41,22 +40,22 @@ struct FooA {
     elem: i8,
 }
 
-static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
-    name: c"foo_a".as_ptr(),
-    version_id: 1,
-    minimum_version_id: 1,
-    fields: vmstate_fields! {
+static VMSTATE_FOOA: VMStateDescription<FooA> = VMStateDescriptionBuilder::<FooA>::new()
+    .name(c"foo_a")
+    .version_id(1)
+    .minimum_version_id(1)
+    .fields(vmstate_fields! {
         vmstate_of!(FooA, elem),
         vmstate_unused!(size_of::<i64>()),
         vmstate_of!(FooA, arr[0 .. num]).with_version_id(0),
         vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]),
-    },
-    ..Zeroable::ZERO
-};
+    })
+    .build();
 
 #[test]
 fn test_vmstate_uint16() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
 
     // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16)
     assert_eq!(
@@ -76,7 +75,8 @@ fn test_vmstate_uint16() {
 
 #[test]
 fn test_vmstate_unused() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
 
     // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED)
     assert_eq!(
@@ -96,7 +96,8 @@ fn test_vmstate_unused() {
 
 #[test]
 fn test_vmstate_varray_uint16_unsafe() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
 
     // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to
     // VMSTATE_VARRAY_UINT16_UNSAFE)
@@ -117,7 +118,8 @@ fn test_vmstate_varray_uint16_unsafe() {
 
 #[test]
 fn test_vmstate_varray_multiply() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
 
     // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to
     // VMSTATE_VARRAY_MULTIPLY)
@@ -171,24 +173,25 @@ fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
     true
 }
 
-static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
-    name: c"foo_b".as_ptr(),
-    version_id: 2,
-    minimum_version_id: 1,
-    fields: vmstate_fields! {
-        vmstate_of!(FooB, val).with_version_id(2),
-        vmstate_of!(FooB, wrap),
-        vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
-        vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
-        vmstate_of!(FooB, arr_i64),
-        vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
-    },
-    ..Zeroable::ZERO
-};
+static VMSTATE_FOOB: VMStateDescription<FooB> =
+    VMStateDescriptionBuilder::<FooB>::new()
+        .name(c"foo_b")
+        .version_id(2)
+        .minimum_version_id(1)
+        .fields(vmstate_fields! {
+            vmstate_of!(FooB, val).with_version_id(2),
+            vmstate_of!(FooB, wrap),
+            vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
+            vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
+            vmstate_of!(FooB, arr_i64),
+            vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
+        })
+        .build();
 
 #[test]
 fn test_vmstate_bool_v() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
 
     // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V)
     assert_eq!(
@@ -208,7 +211,8 @@ fn test_vmstate_bool_v() {
 
 #[test]
 fn test_vmstate_uint64() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
 
     // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64)
     assert_eq!(
@@ -228,7 +232,8 @@ fn test_vmstate_uint64() {
 
 #[test]
 fn test_vmstate_struct_varray_uint8() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
 
     // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to
     // VMSTATE_STRUCT_VARRAY_UINT8)
@@ -246,13 +251,14 @@ fn test_vmstate_struct_varray_uint8() {
         foo_fields[2].flags.0,
         VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0
     );
-    assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA);
+    assert_eq!(foo_fields[2].vmsd, VMSTATE_FOOA.as_ref());
     assert!(foo_fields[2].field_exists.is_none());
 }
 
 #[test]
 fn test_vmstate_struct_varray_uint32_multiply() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
 
     // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to
     // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32)
@@ -272,13 +278,14 @@ fn test_vmstate_struct_varray_uint32_multiply() {
             | VMStateFlags::VMS_VARRAY_UINT32.0
             | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
     );
-    assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA);
+    assert_eq!(foo_fields[3].vmsd, VMSTATE_FOOA.as_ref());
     assert!(foo_fields[3].field_exists.is_none());
 }
 
 #[test]
 fn test_vmstate_macro_array() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
 
     // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to
     // VMSTATE_ARRAY)
@@ -299,7 +306,8 @@ fn test_vmstate_macro_array() {
 
 #[test]
 fn test_vmstate_struct_varray_uint8_wrapper() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
     let mut foo_b: FooB = Default::default();
     let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::<c_void>();
 
@@ -335,26 +343,28 @@ struct FooC {
     arr_ptr_wrap: FooCWrapper,
 }
 
-static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
-    name: c"foo_c".as_ptr(),
-    version_id: 3,
-    minimum_version_id: 1,
-    fields: vmstate_fields! {
+unsafe impl Sync for FooC {}
+
+static VMSTATE_FOOC: VMStateDescription<FooC> = VMStateDescriptionBuilder::<FooC>::new()
+    .name(c"foo_c")
+    .version_id(3)
+    .minimum_version_id(1)
+    .fields(vmstate_fields! {
         vmstate_of!(FooC, ptr).with_version_id(2),
         // FIXME: Currently vmstate_struct doesn't support the pointer to structure.
         // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
         vmstate_unused!(size_of::<NonNull<FooA>>()),
         vmstate_of!(FooC, arr_ptr),
         vmstate_of!(FooC, arr_ptr_wrap),
-    },
-    ..Zeroable::ZERO
-};
+    })
+    .build();
 
 const PTR_SIZE: usize = size_of::<*mut ()>();
 
 #[test]
 fn test_vmstate_pointer() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
 
     // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER)
     assert_eq!(
@@ -377,7 +387,8 @@ fn test_vmstate_pointer() {
 
 #[test]
 fn test_vmstate_macro_array_of_pointer() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
 
     // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to
     // VMSTATE_ARRAY_OF_POINTER)
@@ -401,7 +412,8 @@ fn test_vmstate_macro_array_of_pointer() {
 
 #[test]
 fn test_vmstate_macro_array_of_pointer_wrapped() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
 
     // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to
     // VMSTATE_ARRAY_OF_POINTER)
@@ -450,21 +462,21 @@ fn validate_food_2(_state: &FooD, _version_id: u8) -> bool {
     true
 }
 
-static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
-    name: c"foo_d".as_ptr(),
-    version_id: 3,
-    minimum_version_id: 1,
-    fields: vmstate_fields! {
+static VMSTATE_FOOD: VMStateDescription<FooD> = VMStateDescriptionBuilder::<FooD>::new()
+    .name(c"foo_d")
+    .version_id(3)
+    .minimum_version_id(1)
+    .fields(vmstate_fields! {
         vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0),
         vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1),
         vmstate_validate!(FooD, c"foo_d_2", validate_food_2),
-    },
-    ..Zeroable::ZERO
-};
+    })
+    .build();
 
 #[test]
 fn test_vmstate_validate() {
-    let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) };
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOD.as_ref().fields, 4) };
     let mut foo_d = FooD;
     let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>();
 
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 08/33] rust: vmstate: use const_refs_to_static
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (5 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 07/33] rust: vmstate: convert to use builder pattern Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:07   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 09/33] rust: qdev: const_refs_to_static Paolo Bonzini
                   ` (26 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel

The VMStateDescriptionBuilder already needs const_refs_static, so
use it to remove the need for vmstate_clock! and vmstate_struct!,
as well as to simplify the implementation for scalars.

If the consts in the VMState trait can reference to static
VMStateDescription, scalars do not need the info_enum_to_ref!
indirection and structs can implement the VMState trait themselves.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/devel/rust.rst                  |   5 -
 rust/hw/char/pl011/src/device.rs     |  19 ++-
 rust/hw/timer/hpet/src/device.rs     |   8 +-
 rust/qemu-api/src/assertions.rs      |   4 -
 rust/qemu-api/src/vmstate.rs         | 229 +++++++--------------------
 rust/qemu-api/tests/vmstate_tests.rs |  65 +++++---
 6 files changed, 113 insertions(+), 217 deletions(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index aca29e55c05..10b0590b56c 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -84,11 +84,6 @@ patches are welcome:
 
 * ``&raw`` (stable in 1.82.0).
 
-* referencing statics in constants (stable in 1.83.0).  For now use a const
-  function; this is an important limitation for QEMU's migration stream
-  architecture (VMState).  Right now, VMState lacks type safety because
-  it is hard to place the ``VMStateField`` definitions in traits.
-
 * NUL-terminated file names with ``#[track_caller]`` are scheduled for
   inclusion as ``#![feature(location_file_nul)]``, but it will be a while
   before QEMU can use them.  For now, there is special code in
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 21611d9c099..87a17716fed 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -2,14 +2,11 @@
 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-use std::{
-    ffi::CStr,
-    mem::size_of
-};
+use std::{ffi::CStr, mem::size_of};
 
 use qemu_api::{
     chardev::{CharBackend, Chardev, Event},
-    impl_vmstate_forward,
+    impl_vmstate_forward, impl_vmstate_struct,
     irq::{IRQState, InterruptSource},
     log::Log,
     log_mask_ln,
@@ -21,7 +18,7 @@
     sysbus::{SysBusDevice, SysBusDeviceImpl},
     uninit_field_mut,
     vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
-    vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
+    vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused,
 };
 
 use crate::registers::{self, Interrupt, RegisterOffset};
@@ -725,11 +722,12 @@ impl SysBusDeviceImpl for PL011Luminary {}
         .minimum_version_id(1)
         .needed(&PL011State::clock_needed)
         .fields(vmstate_fields! {
-             vmstate_clock!(PL011State, clock),
+             vmstate_of!(PL011State, clock),
         })
         .build();
 
-static VMSTATE_PL011_REGS: VMStateDescription<PL011Registers> =
+impl_vmstate_struct!(
+    PL011Registers,
     VMStateDescriptionBuilder::<PL011Registers>::new()
         .name(c"pl011/regs")
         .version_id(2)
@@ -751,7 +749,8 @@ impl SysBusDeviceImpl for PL011Luminary {}
             vmstate_of!(PL011Registers, read_count),
             vmstate_of!(PL011Registers, read_trigger),
         })
-        .build();
+        .build()
+);
 
 pub const VMSTATE_PL011: VMStateDescription<PL011State> =
     VMStateDescriptionBuilder::<PL011State>::new()
@@ -761,7 +760,7 @@ impl SysBusDeviceImpl for PL011Luminary {}
         .post_load(&PL011State::post_load)
         .fields(vmstate_fields! {
             vmstate_unused!(core::mem::size_of::<u32>()),
-            vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
+            vmstate_of!(PL011State, regs),
         })
         .subsections(vmstate_subsections! {
              VMSTATE_PL011_CLOCK
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 955cf869ff6..eb5bd042b1c 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -16,6 +16,7 @@
         qdev_prop_uint32, qdev_prop_usize,
     },
     cell::{BqlCell, BqlRefCell},
+    impl_vmstate_struct,
     irq::InterruptSource,
     memory::{
         hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
@@ -28,7 +29,7 @@
     timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
     uninit_field_mut,
     vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
-    vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
+    vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
 };
 
 use crate::fw_cfg::HPETFwConfig;
@@ -964,7 +965,7 @@ impl ObjectImpl for HPETState {
         })
         .build();
 
-static VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> =
+const VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> =
     VMStateDescriptionBuilder::<HPETTimer>::new()
         .name(c"hpet_timer")
         .version_id(1)
@@ -979,6 +980,7 @@ impl ObjectImpl for HPETState {
             vmstate_of!(HPETTimer, qemu_timer),
         })
         .build();
+impl_vmstate_struct!(HPETTimer, VMSTATE_HPET_TIMER);
 
 const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match";
 
@@ -995,7 +997,7 @@ impl ObjectImpl for HPETState {
             vmstate_of!(HPETState, counter),
             vmstate_of!(HPETState, num_timers_save),
             vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
-            vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0),
+            vmstate_of!(HPETState, timers[0 .. num_timers_save], HPETState::validate_num_timers).with_version_id(0),
         })
         .subsections(vmstate_subsections!(
             VMSTATE_HPET_RTC_IRQ_LEVEL,
diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs
index e74fa3ef826..e4fe23b674e 100644
--- a/rust/qemu-api/src/assertions.rs
+++ b/rust/qemu-api/src/assertions.rs
@@ -95,10 +95,6 @@ const fn types_must_be_equal<T, U>(_: &T)
     ($t:ty, $i:tt, $ti:ty) => {
         $crate::assert_field_type!(@internal v, $ti, $t, v.$i);
     };
-
-    ($t:ty, $i:tt, $ti:ty, num = $num:ident) => {
-        $crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]);
-    };
 }
 
 /// Assert that an expression matches a pattern.  This can also be
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index f5f1ea5590f..b5c6b764fbb 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -11,10 +11,11 @@
 //!   migration format for a struct.  This is based on the [`VMState`] trait,
 //!   which is defined by all migratable types.
 //!
-//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and
-//!   [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with
-//!   the definition of the [`VMState`] trait (respectively for transparent
-//!   structs and for `bilge`-defined types)
+//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward),
+//!   [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
+//!   [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the
+//!   definition of the [`VMState`] trait (respectively for transparent structs,
+//!   nested structs and `bilge`-defined types)
 //!
 //! * helper macros to declare a device model state struct, in particular
 //!   [`vmstate_subsections`](crate::vmstate_subsections) and
@@ -31,7 +32,7 @@
     fmt, io,
     marker::PhantomData,
     mem,
-    ptr::NonNull,
+    ptr::{addr_of, NonNull},
 };
 
 pub use crate::bindings::{MigrationPriority, VMStateField};
@@ -40,6 +41,7 @@
     callbacks::FnCall,
     errno::{into_neg_errno, Errno},
     prelude::*,
+    qdev,
     qom::Owned,
     zeroable::Zeroable,
 };
@@ -81,70 +83,6 @@ const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker:
     };
 }
 
-/// Workaround for lack of `const_refs_static`: references to global variables
-/// can be included in a `static`, but not in a `const`; unfortunately, this
-/// is exactly what would go in the `VMStateField`'s `info` member.
-///
-/// This enum contains the contents of the `VMStateField`'s `info` member,
-/// but as an `enum` instead of a pointer.
-#[allow(non_camel_case_types)]
-pub enum VMStateFieldType {
-    null,
-    vmstate_info_bool,
-    vmstate_info_int8,
-    vmstate_info_int16,
-    vmstate_info_int32,
-    vmstate_info_int64,
-    vmstate_info_uint8,
-    vmstate_info_uint16,
-    vmstate_info_uint32,
-    vmstate_info_uint64,
-    vmstate_info_timer,
-}
-
-/// Workaround for lack of `const_refs_static`.  Converts a `VMStateFieldType`
-/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`.
-#[macro_export]
-macro_rules! info_enum_to_ref {
-    ($e:expr) => {
-        unsafe {
-            match $e {
-                $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(),
-                $crate::vmstate::VMStateFieldType::vmstate_info_bool => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_int8 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_int16 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_int32 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_int64 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64)
-                }
-                $crate::vmstate::VMStateFieldType::vmstate_info_timer => {
-                    ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer)
-                }
-            }
-        }
-    };
-}
-
 /// A trait for types that can be included in a device's migration stream.  It
 /// provides the base contents of a `VMStateField` (minus the name and offset).
 ///
@@ -155,12 +93,6 @@ macro_rules! info_enum_to_ref {
 /// to implement it except via macros that do it for you, such as
 /// `impl_vmstate_bitsized!`.
 pub unsafe trait VMState {
-    /// The `info` member of a `VMStateField` is a pointer and as such cannot
-    /// yet be included in the [`BASE`](VMState::BASE) associated constant;
-    /// this is only allowed by Rust 1.83.0 and newer.  For now, include the
-    /// member as an enum which is stored in a separate constant.
-    const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null;
-
     /// The base contents of a `VMStateField` (minus the name and offset) for
     /// the type that is implementing the trait.
     const BASE: VMStateField;
@@ -175,12 +107,6 @@ pub unsafe trait VMState {
     };
 }
 
-/// Internal utility function to retrieve a type's `VMStateFieldType`;
-/// used by [`vmstate_of!`](crate::vmstate_of).
-pub const fn vmstate_scalar_type<T: VMState>(_: PhantomData<T>) -> VMStateFieldType {
-    T::SCALAR_TYPE
-}
-
 /// Internal utility function to retrieve a type's `VMStateField`;
 /// used by [`vmstate_of!`](crate::vmstate_of).
 pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField {
@@ -207,9 +133,9 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 /// * an array of any of the above
 ///
 /// In order to support other types, the trait `VMState` must be implemented
-/// for them.  The macros
-/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized)
-/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this.
+/// for them.  The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward),
+/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
+/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this.
 #[macro_export]
 macro_rules! vmstate_of {
     ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
@@ -222,11 +148,6 @@ macro_rules! vmstate_of {
             $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
             // The calls to `call_func_with_field!` are the magic that
             // computes most of the VMStateField from the type of the field.
-            info: $crate::info_enum_to_ref!($crate::call_func_with_field!(
-                $crate::vmstate::vmstate_scalar_type,
-                $struct_name,
-                $field_name
-            )),
             ..$crate::call_func_with_field!(
                 $crate::vmstate::vmstate_base,
                 $struct_name,
@@ -327,8 +248,6 @@ macro_rules! impl_vmstate_forward {
     // the first field of the tuple
     ($tuple:ty) => {
         unsafe impl $crate::vmstate::VMState for $tuple {
-            const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
-                $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0);
             const BASE: $crate::bindings::VMStateField =
                 $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0);
         }
@@ -340,7 +259,6 @@ unsafe impl $crate::vmstate::VMState for $tuple {
 macro_rules! impl_vmstate_transparent {
     ($type:ty where $base:tt: VMState $($where:tt)*) => {
         unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
-            const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE;
             const BASE: VMStateField = VMStateField {
                 size: mem::size_of::<$type>(),
                 ..<$base as VMState>::BASE
@@ -361,10 +279,6 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
 macro_rules! impl_vmstate_bitsized {
     ($type:ty) => {
         unsafe impl $crate::vmstate::VMState for $type {
-            const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
-                                        <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
-                                          as ::bilge::prelude::Number>::UnderlyingType
-                                         as $crate::vmstate::VMState>::SCALAR_TYPE;
             const BASE: $crate::bindings::VMStateField =
                                         <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
                                           as ::bilge::prelude::Number>::UnderlyingType
@@ -382,8 +296,8 @@ unsafe impl $crate::vmstate::VMState for $type {
 macro_rules! impl_vmstate_scalar {
     ($info:ident, $type:ty$(, $varray_flag:ident)?) => {
         unsafe impl VMState for $type {
-            const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info;
             const BASE: VMStateField = VMStateField {
+                info: addr_of!(bindings::$info),
                 size: mem::size_of::<$type>(),
                 flags: VMStateFlags::VMS_SINGLE,
                 ..Zeroable::ZERO
@@ -404,6 +318,21 @@ unsafe impl VMState for $type {
 impl_vmstate_scalar!(vmstate_info_uint64, u64);
 impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer);
 
+macro_rules! impl_vmstate_c_struct {
+    ($type:ty, $vmsd:expr) => {
+        unsafe impl VMState for $type {
+            const BASE: VMStateField = $crate::bindings::VMStateField {
+                vmsd: addr_of!($vmsd),
+                size: mem::size_of::<$type>(),
+                flags: VMStateFlags::VMS_STRUCT,
+                ..Zeroable::ZERO
+            };
+        }
+    };
+}
+
+impl_vmstate_c_struct!(qdev::Clock, bindings::vmstate_clock);
+
 // Pointer types using the underlying type's VMState plus VMS_POINTER
 // Note that references are not supported, though references to cells
 // could be allowed.
@@ -411,7 +340,6 @@ unsafe impl VMState for $type {
 macro_rules! impl_vmstate_pointer {
     ($type:ty where $base:tt: VMState $($where:tt)*) => {
         unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
-            const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
             const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag();
         }
     };
@@ -430,7 +358,6 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
 // VMS_ARRAY/VMS_ARRAY_OF_POINTER
 
 unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
-    const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
     const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N);
 }
 
@@ -452,7 +379,7 @@ pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), b
     opaque: *mut c_void,
     version_id: c_int,
 ) -> bool {
-    // SAFETY: assumes vmstate_struct! is used correctly
+    // SAFETY: the function is used in T's implementation of VMState
     let owner: &T = unsafe { &*(opaque.cast::<T>()) };
     let version: u8 = version_id.try_into().unwrap();
     F::call((owner, version))
@@ -480,76 +407,6 @@ const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
     }};
 }
 
-// FIXME: including the `vmsd` field in a `const` is not possible without
-// the const_refs_static feature (stabilized in Rust 1.83.0).  Without it,
-// it is not possible to use VMS_STRUCT in a transparent manner using
-// `vmstate_of!`.  While VMSTATE_CLOCK can at least try to be type-safe,
-// VMSTATE_STRUCT includes $type only for documentation purposes; it
-// is checked against $field_name and $struct_name, but not against $vmsd
-// which is what really would matter.
-#[doc(alias = "VMSTATE_STRUCT")]
-#[macro_export]
-macro_rules! vmstate_struct {
-    ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(, $test_fn:expr)? $(,)?) => {
-        $crate::bindings::VMStateField {
-            name: ::core::concat!(::core::stringify!($field_name), "\0")
-                .as_bytes()
-                .as_ptr() as *const ::std::os::raw::c_char,
-            $(num_offset: ::std::mem::offset_of!($struct_name, $num),)?
-            offset: {
-                $crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?);
-                ::std::mem::offset_of!($struct_name, $field_name)
-            },
-            size: ::core::mem::size_of::<$type>(),
-            flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
-            vmsd: $vmsd.as_ref(),
-            $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
-            ..$crate::zeroable::Zeroable::ZERO
-         } $(.with_varray_flag_unchecked(
-                  $crate::call_func_with_field!(
-                      $crate::vmstate::vmstate_varray_flag,
-                      $struct_name,
-                      $num
-                  )
-              )
-           $(.with_varray_multiply($factor))?)?
-    };
-}
-
-#[doc(alias = "VMSTATE_CLOCK")]
-#[macro_export]
-macro_rules! vmstate_clock {
-    ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{
-        $crate::bindings::VMStateField {
-            name: ::core::concat!(::core::stringify!($field_name), "\0")
-                .as_bytes()
-                .as_ptr() as *const ::std::os::raw::c_char,
-            offset: {
-                $crate::assert_field_type!(
-                    $struct_name,
-                    $field_name,
-                    $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
-                );
-                ::std::mem::offset_of!($struct_name, $field_name)
-            },
-            size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
-            flags: $crate::bindings::VMStateFlags(
-                $crate::bindings::VMStateFlags::VMS_STRUCT.0
-                    | $crate::bindings::VMStateFlags::VMS_POINTER.0,
-            ),
-            vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
-            ..$crate::zeroable::Zeroable::ZERO
-         } $(.with_varray_flag_unchecked(
-                  $crate::call_func_with_field!(
-                      $crate::vmstate::vmstate_varray_flag,
-                      $struct_name,
-                      $num
-                  )
-              )
-           $(.with_varray_multiply($factor))?)?
-    }};
-}
-
 /// Helper macro to declare a list of
 /// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return
 /// a pointer to the array of values it created.
@@ -584,6 +441,30 @@ macro_rules! vmstate_validate {
     };
 }
 
+/// Helper macro to allow using a struct in [`vmstate_of!`]
+///
+/// # Safety
+///
+/// The [`VMStateDescription`] constant `$vmsd` must be an accurate
+/// description of the struct.
+#[macro_export]
+macro_rules! impl_vmstate_struct {
+    ($type:ty, $vmsd:expr) => {
+        unsafe impl $crate::vmstate::VMState for $type {
+            const BASE: $crate::bindings::VMStateField = {
+                static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref();
+
+                $crate::bindings::VMStateField {
+                    vmsd: ::core::ptr::addr_of!(*VMSD),
+                    size: ::core::mem::size_of::<$type>(),
+                    flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
+                    ..$crate::zeroable::Zeroable::ZERO
+                }
+            };
+        }
+    };
+}
+
 /// A transparent wrapper type for the `subsections` field of
 /// [`VMStateDescription`].
 ///
@@ -648,7 +529,7 @@ fn from(_value: InvalidError) -> Errno {
 >(
     opaque: *mut c_void,
 ) -> c_int {
-    // SAFETY: assumes vmstate_struct! is used correctly
+    // SAFETY: the function is used in T's implementation of VMState
     let result = F::call((unsafe { &*(opaque.cast::<T>()) },));
     into_neg_errno(result)
 }
@@ -660,7 +541,7 @@ fn from(_value: InvalidError) -> Errno {
     opaque: *mut c_void,
     version_id: c_int,
 ) -> c_int {
-    // SAFETY: assumes vmstate_struct! is used correctly
+    // SAFETY: the function is used in T's implementation of VMState
     let owner: &T = unsafe { &*(opaque.cast::<T>()) };
     let version: u8 = version_id.try_into().unwrap();
     let result = F::call((owner, version));
@@ -670,14 +551,14 @@ fn from(_value: InvalidError) -> Errno {
 unsafe extern "C" fn vmstate_needed_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
     opaque: *mut c_void,
 ) -> bool {
-    // SAFETY: assumes vmstate_struct! is used correctly
+    // SAFETY: the function is used in T's implementation of VMState
     F::call((unsafe { &*(opaque.cast::<T>()) },))
 }
 
 unsafe extern "C" fn vmstate_dev_unplug_pending_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
     opaque: *mut c_void,
 ) -> bool {
-    // SAFETY: assumes vmstate_struct! is used correctly
+    // SAFETY: the function is used in T's implementation of VMState
     F::call((unsafe { &*(opaque.cast::<T>()) },))
 }
 
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs
index 8ffc2779d46..2c0670ba0ee 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -15,9 +15,9 @@
         vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
     },
     cell::{BqlCell, Opaque},
-    impl_vmstate_forward,
+    impl_vmstate_forward, impl_vmstate_struct,
     vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField},
-    vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
+    vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate,
 };
 
 const FOO_ARRAY_MAX: usize = 3;
@@ -52,6 +52,8 @@ struct FooA {
     })
     .build();
 
+impl_vmstate_struct!(FooA, VMSTATE_FOOA);
+
 #[test]
 fn test_vmstate_uint16() {
     let foo_fields: &[VMStateField] =
@@ -173,20 +175,19 @@ fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
     true
 }
 
-static VMSTATE_FOOB: VMStateDescription<FooB> =
-    VMStateDescriptionBuilder::<FooB>::new()
-        .name(c"foo_b")
-        .version_id(2)
-        .minimum_version_id(1)
-        .fields(vmstate_fields! {
-            vmstate_of!(FooB, val).with_version_id(2),
-            vmstate_of!(FooB, wrap),
-            vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
-            vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
-            vmstate_of!(FooB, arr_i64),
-            vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
-        })
-        .build();
+static VMSTATE_FOOB: VMStateDescription<FooB> = VMStateDescriptionBuilder::<FooB>::new()
+    .name(c"foo_b")
+    .version_id(2)
+    .minimum_version_id(1)
+    .fields(vmstate_fields! {
+        vmstate_of!(FooB, val).with_version_id(2),
+        vmstate_of!(FooB, wrap),
+        vmstate_of!(FooB, arr_a[0 .. num_a]).with_version_id(1),
+        vmstate_of!(FooB, arr_a_mul[0 .. num_a_mul * 32]).with_version_id(2),
+        vmstate_of!(FooB, arr_i64),
+        vmstate_of!(FooB, arr_a_wrap[0 .. num_a_wrap], validate_foob),
+    })
+    .build();
 
 #[test]
 fn test_vmstate_bool_v() {
@@ -351,9 +352,7 @@ unsafe impl Sync for FooC {}
     .minimum_version_id(1)
     .fields(vmstate_fields! {
         vmstate_of!(FooC, ptr).with_version_id(2),
-        // FIXME: Currently vmstate_struct doesn't support the pointer to structure.
-        // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
-        vmstate_unused!(size_of::<NonNull<FooA>>()),
+        vmstate_of!(FooC, ptr_a),
         vmstate_of!(FooC, arr_ptr),
         vmstate_of!(FooC, arr_ptr_wrap),
     })
@@ -385,6 +384,31 @@ fn test_vmstate_pointer() {
     assert!(foo_fields[0].field_exists.is_none());
 }
 
+#[test]
+fn test_vmstate_struct_pointer() {
+    let foo_fields: &[VMStateField] =
+        unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
+
+    // 2st VMStateField ("ptr_a") in VMSTATE_FOOC (corresponding to
+    // VMSTATE_STRUCT_POINTER)
+    assert_eq!(
+        unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
+        b"ptr_a\0"
+    );
+    assert_eq!(foo_fields[1].offset, PTR_SIZE);
+    assert_eq!(foo_fields[1].num_offset, 0);
+    assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref());
+    assert_eq!(foo_fields[1].version_id, 0);
+    assert_eq!(foo_fields[1].size, size_of::<FooA>());
+    assert_eq!(foo_fields[1].num, 0);
+    assert_eq!(
+        foo_fields[1].flags.0,
+        VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0
+    );
+    assert!(foo_fields[1].info.is_null());
+    assert!(foo_fields[1].field_exists.is_none());
+}
+
 #[test]
 fn test_vmstate_macro_array_of_pointer() {
     let foo_fields: &[VMStateField] =
@@ -444,8 +468,7 @@ fn test_vmstate_macro_array_of_pointer_wrapped() {
 //   * VMSTATE_FOOD:
 //     - VMSTATE_VALIDATE
 
-// Add more member fields when vmstate_of/vmstate_struct support "test"
-// parameter.
+// Add more member fields when vmstate_of support "test" parameter.
 struct FooD;
 
 impl FooD {
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 09/33] rust: qdev: const_refs_to_static
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (6 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 08/33] rust: vmstate: use const_refs_to_static Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:20   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 10/33] docs/rust: update msrv Paolo Bonzini
                   ` (25 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel

Now that const_refs_static can be assumed, convert the members of
the DeviceImpl trait from functions to constants.  This lets the
compiler know that they have a 'static lifetime, and removes the
need for the weird "Box::leak()".

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/hw/char/pl011/src/device.rs  |  4 +--
 rust/hw/timer/hpet/src/device.rs  |  9 ++-----
 rust/qemu-api-macros/src/lib.rs   |  8 +++---
 rust/qemu-api-macros/src/tests.rs | 44 ++++++++++++++-----------------
 rust/qemu-api/src/qdev.rs         | 19 +++++--------
 rust/qemu-api/tests/tests.rs      |  4 +--
 6 files changed, 33 insertions(+), 55 deletions(-)

diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 87a17716fed..8411db8d00c 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -172,9 +172,7 @@ impl ObjectImpl for PL011State {
 }
 
 impl DeviceImpl for PL011State {
-    fn vmsd() -> Option<VMStateDescription<Self>> {
-        Some(VMSTATE_PL011)
-    }
+    const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_PL011);
     const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
 }
 
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index eb5bd042b1c..dd5326a40d4 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -1008,16 +1008,11 @@ impl ObjectImpl for HPETState {
 // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the
 // qemu_api::declare_properties macro.
 unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState {
-    fn properties() -> &'static [Property] {
-        &HPET_PROPERTIES
-    }
+    const PROPERTIES: &'static [Property] = &HPET_PROPERTIES;
 }
 
 impl DeviceImpl for HPETState {
-    fn vmsd() -> Option<VMStateDescription<Self>> {
-        Some(VMSTATE_HPET)
-    }
-
+    const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_HPET);
     const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
 }
 
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index 97b2c214b62..a65a7ce2fe9 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -289,11 +289,9 @@ macro_rules! str_to_c_str {
 
     Ok(quote_spanned! {input.span() =>
         unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name {
-            fn properties() -> &'static [::qemu_api::bindings::Property] {
-                static PROPERTIES: &[::qemu_api::bindings::Property] = &[#(#properties_expanded),*];
-
-                PROPERTIES
-            }
+            const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
+                #(#properties_expanded),*
+            ];
         }
     })
 }
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs
index aafffcdce91..0e5a5728908 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -101,18 +101,16 @@ pub struct DummyState {
         },
         quote! {
             unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
-                fn properties() -> &'static [::qemu_api::bindings::Property] {
-                    static PROPERTIES: &[::qemu_api::bindings::Property] =
-                        &[::qemu_api::bindings::Property {
-                            name: ::std::ffi::CStr::as_ptr(c"migrate_clock"),
-                            info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
-                            offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
-                            set_default: true,
-                            defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
-                            ..::qemu_api::zeroable::Zeroable::ZERO
-                        }];
-                    PROPERTIES
-                }
+                const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
+                    ::qemu_api::bindings::Property {
+                        name: ::std::ffi::CStr::as_ptr(c"migrate_clock"),
+                        info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
+                        offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
+                        set_default: true,
+                        defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
+                        ..::qemu_api::zeroable::Zeroable::ZERO
+                    }
+                ];
             }
         }
     );
@@ -130,18 +128,16 @@ pub struct DummyState {
         },
         quote! {
             unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
-                fn properties() -> &'static [::qemu_api::bindings::Property] {
-                    static PROPERTIES: &[::qemu_api::bindings::Property] =
-                        &[::qemu_api::bindings::Property {
-                            name: ::std::ffi::CStr::as_ptr(c"migrate-clk"),
-                            info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
-                            offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
-                            set_default: true,
-                            defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
-                            ..::qemu_api::zeroable::Zeroable::ZERO
-                        }];
-                    PROPERTIES
-                }
+                const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
+                    ::qemu_api::bindings::Property {
+                        name: ::std::ffi::CStr::as_ptr(c"migrate-clk"),
+                        info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
+                        offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
+                        set_default: true,
+                        defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
+                        ..::qemu_api::zeroable::Zeroable::ZERO
+                    }
+                ];
             }
         }
     );
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 4dda8c81131..436142d8aef 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -151,11 +151,8 @@ unsafe impl QDevProp for crate::chardev::CharBackend {
 /// Caller is responsible for the validity of properties array.
 pub unsafe trait DevicePropertiesImpl {
     /// An array providing the properties that the user can set on the
-    /// device.  Not a `const` because referencing statics in constants
-    /// is unstable until Rust 1.83.0.
-    fn properties() -> &'static [Property] {
-        &[]
-    }
+    /// device.
+    const PROPERTIES: &'static [Property] = &[];
 }
 
 /// Trait providing the contents of [`DeviceClass`].
@@ -173,9 +170,7 @@ pub trait DeviceImpl:
     /// A `VMStateDescription` providing the migration format for the device
     /// Not a `const` because referencing statics in constants is unstable
     /// until Rust 1.83.0.
-    fn vmsd() -> Option<VMStateDescription<Self>> {
-        None
-    }
+    const VMSTATE: Option<VMStateDescription<Self>> = None;
 }
 
 /// # Safety
@@ -224,12 +219,10 @@ pub fn class_init<T: DeviceImpl>(&mut self) {
         if <T as DeviceImpl>::REALIZE.is_some() {
             self.realize = Some(rust_realize_fn::<T>);
         }
-        if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
-            // Give a 'static lifetime to the return value of vmsd().
-            // Temporary until vmsd() can be changed into a const.
-            self.vmsd = Box::leak(Box::new(vmsd.get()));
+        if let Some(ref vmsd) = <T as DeviceImpl>::VMSTATE {
+            self.vmsd = vmsd.as_ref();
         }
-        let prop = <T as DevicePropertiesImpl>::properties();
+        let prop = <T as DevicePropertiesImpl>::PROPERTIES;
         if !prop.is_empty() {
             unsafe {
                 bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len());
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 4d4e4653f38..13495687419 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -56,9 +56,7 @@ impl ObjectImpl for DummyState {
 impl ResettablePhasesImpl for DummyState {}
 
 impl DeviceImpl for DummyState {
-    fn vmsd() -> Option<VMStateDescription<Self>> {
-        Some(VMSTATE)
-    }
+    const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE);
 }
 
 #[repr(C)]
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 10/33] docs/rust: update msrv
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (7 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 09/33] rust: qdev: const_refs_to_static Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:21   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 11/33] rust: remove unused global qemu "allocator" Paolo Bonzini
                   ` (24 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-2-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/devel/rust.rst | 2 +-
 rust/Cargo.toml     | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 10b0590b56c..20d15347def 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -1,4 +1,4 @@
-.. |msrv| replace:: 1.63.0
+.. |msrv| replace:: 1.83.0
 
 Rust in QEMU
 ============
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 99c275f2d9f..9b26aa07cec 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -13,6 +13,7 @@ edition = "2021"
 homepage = "https://www.qemu.org"
 license = "GPL-2.0-or-later"
 repository = "https://gitlab.com/qemu-project/qemu/"
+# don't forget to update docs/devel/rust.rst msrv
 rust-version = "1.83.0"
 
 [workspace.dependencies]
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 11/33] rust: remove unused global qemu "allocator"
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (8 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 10/33] docs/rust: update msrv Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:21   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 12/33] rust: add workspace authors Paolo Bonzini
                   ` (23 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

The global allocator has always been disabled. There is no clear reason
Rust and C should use the same allocator. Allocations made from Rust
must be freed by Rust, and same for C, otherwise we head into troubles.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-3-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 meson.build               |   4 --
 rust/Cargo.toml           |   2 +-
 rust/qemu-api/Cargo.toml  |   1 -
 rust/qemu-api/meson.build |   1 -
 rust/qemu-api/src/lib.rs  | 135 --------------------------------------
 5 files changed, 1 insertion(+), 142 deletions(-)

diff --git a/meson.build b/meson.build
index ef3f52b951d..49395197f44 100644
--- a/meson.build
+++ b/meson.build
@@ -1090,9 +1090,6 @@ glib = declare_dependency(dependencies: [glib_pc, gmodule],
 # TODO: remove this check and the corresponding workaround (qtree) when
 # the minimum supported glib is >= 2.75.3
 glib_has_gslice = glib.version().version_compare('<2.75.3')
-# Check whether glib has the aligned_alloc family of functions.
-# <https://docs.gtk.org/glib/func.aligned_alloc.html>
-glib_has_aligned_alloc = glib.version().version_compare('>=2.72.0')
 
 # override glib dep to include the above refinements
 meson.override_dependency('glib-2.0', glib)
@@ -2706,7 +2703,6 @@ config_host_data.set('CONFIG_GETLOADAVG', cc.has_function('getloadavg'))
 config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range'))
 config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs'))
 config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', glib_has_gslice)
-config_host_data.set('HAVE_GLIB_WITH_ALIGNED_ALLOC', glib_has_aligned_alloc)
 config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util))
 config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul', prefix: osdep_prefix))
 config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 9b26aa07cec..d98d2b77026 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -23,7 +23,7 @@ libc = "0.2.162"
 
 [workspace.lints.rust]
 unexpected_cfgs = { level = "deny", check-cfg = [
-    'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
+    'cfg(MESON)',
 ] }
 
 # Occasionally, we may need to silence warnings and clippy lints that
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index c07a17a28b0..c5ed78035b6 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -21,7 +21,6 @@ libc = { workspace = true }
 
 [features]
 default = ["debug_cell"]
-allocator = []
 debug_cell = []
 
 [lints]
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index a090297c458..062009f1618 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -2,7 +2,6 @@ _qemu_api_cfg = run_command(rustc_args,
   '--config-headers', config_host_h, '--features', files('Cargo.toml'),
   capture: true, check: true).stdout().strip().splitlines()
 
-# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
 if get_option('debug_mutex')
   _qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
 endif
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index bcb51c7986a..daa2493bb69 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -36,139 +36,4 @@
 // crate).
 extern crate self as qemu_api;
 
-use std::{
-    alloc::{GlobalAlloc, Layout},
-    ffi::c_void,
-};
-
 pub use error::{Error, Result};
-
-#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
-extern "C" {
-    fn g_aligned_alloc0(
-        n_blocks: bindings::gsize,
-        n_block_bytes: bindings::gsize,
-        alignment: bindings::gsize,
-    ) -> bindings::gpointer;
-    fn g_aligned_free(mem: bindings::gpointer);
-}
-
-#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
-extern "C" {
-    fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void;
-    fn qemu_vfree(ptr: *mut c_void);
-}
-
-extern "C" {
-    fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer;
-    fn g_free(mem: bindings::gpointer);
-}
-
-/// An allocator that uses the same allocator as QEMU in C.
-///
-/// It is enabled by default with the `allocator` feature.
-///
-/// To set it up manually as a global allocator in your crate:
-///
-/// ```ignore
-/// use qemu_api::QemuAllocator;
-///
-/// #[global_allocator]
-/// static GLOBAL: QemuAllocator = QemuAllocator::new();
-/// ```
-#[derive(Clone, Copy, Debug)]
-#[repr(C)]
-pub struct QemuAllocator {
-    _unused: [u8; 0],
-}
-
-#[cfg_attr(all(feature = "allocator", not(test)), global_allocator)]
-pub static GLOBAL: QemuAllocator = QemuAllocator::new();
-
-impl QemuAllocator {
-    // From the glibc documentation, on GNU systems, malloc guarantees 16-byte
-    // alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See
-    // https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html.
-    // This alignment guarantee also applies to Windows and Android. On Darwin
-    // and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems.
-    #[cfg(all(
-        target_pointer_width = "32",
-        not(any(target_os = "macos", target_os = "openbsd"))
-    ))]
-    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(8);
-    #[cfg(all(
-        target_pointer_width = "64",
-        not(any(target_os = "macos", target_os = "openbsd"))
-    ))]
-    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
-    #[cfg(all(
-        any(target_pointer_width = "32", target_pointer_width = "64"),
-        any(target_os = "macos", target_os = "openbsd")
-    ))]
-    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
-    #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
-    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = None;
-
-    pub const fn new() -> Self {
-        Self { _unused: [] }
-    }
-}
-
-impl Default for QemuAllocator {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-// Sanity check.
-const _: [(); 8] = [(); ::core::mem::size_of::<*mut c_void>()];
-
-unsafe impl GlobalAlloc for QemuAllocator {
-    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
-        if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
-        {
-            // SAFETY: g_malloc0() is safe to call.
-            unsafe { g_malloc0(layout.size().try_into().unwrap()).cast::<u8>() }
-        } else {
-            #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
-            {
-                // SAFETY: g_aligned_alloc0() is safe to call.
-                unsafe {
-                    g_aligned_alloc0(
-                        layout.size().try_into().unwrap(),
-                        1,
-                        layout.align().try_into().unwrap(),
-                    )
-                    .cast::<u8>()
-                }
-            }
-            #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
-            {
-                // SAFETY: qemu_memalign() is safe to call.
-                unsafe { qemu_memalign(layout.align(), layout.size()).cast::<u8>() }
-            }
-        }
-    }
-
-    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
-        if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
-        {
-            // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid
-            // glib-allocated pointer, so `g_free`ing is safe.
-            unsafe { g_free(ptr.cast::<_>()) }
-        } else {
-            #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
-            {
-                // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned
-                // glib-allocated pointer, so `g_aligned_free`ing is safe.
-                unsafe { g_aligned_free(ptr.cast::<_>()) }
-            }
-            #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
-            {
-                // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned
-                // glib-allocated pointer, so `qemu_vfree`ing is safe.
-                unsafe { qemu_vfree(ptr.cast::<_>()) }
-            }
-        }
-    }
-}
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 12/33] rust: add workspace authors
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (9 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 11/33] rust: remove unused global qemu "allocator" Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:23   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 13/33] rust: move vmstate_clock!() to qdev module Paolo Bonzini
                   ` (22 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-4-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/Cargo.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index d98d2b77026..cd4bf8ef8e1 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -15,6 +15,7 @@ license = "GPL-2.0-or-later"
 repository = "https://gitlab.com/qemu-project/qemu/"
 # don't forget to update docs/devel/rust.rst msrv
 rust-version = "1.83.0"
+authors = ["The QEMU Project Developers <qemu-devel@nongnu.org>"]
 
 [workspace.dependencies]
 anyhow = "~1.0"
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 13/33] rust: move vmstate_clock!() to qdev module
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (10 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 12/33] rust: add workspace authors Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:25   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 14/33] rust: move VMState handling to QOM module Paolo Bonzini
                   ` (21 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This will allow to split vmstate to a standalone crate next.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-8-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/src/qdev.rs    |  3 +++
 rust/qemu-api/src/vmstate.rs | 14 ++++++--------
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 436142d8aef..e2b4121cac5 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -17,6 +17,7 @@
     cell::{bql_locked, Opaque},
     chardev::Chardev,
     error::{Error, Result},
+    impl_vmstate_c_struct,
     irq::InterruptSource,
     prelude::*,
     qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
@@ -455,3 +456,5 @@ unsafe impl ObjectType for Clock {
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
 }
 qom_isa!(Clock: Object);
+
+impl_vmstate_c_struct!(Clock, bindings::vmstate_clock);
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index b5c6b764fbb..59c7950a5aa 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -41,7 +41,6 @@
     callbacks::FnCall,
     errno::{into_neg_errno, Errno},
     prelude::*,
-    qdev,
     qom::Owned,
     zeroable::Zeroable,
 };
@@ -318,21 +317,20 @@ unsafe impl VMState for $type {
 impl_vmstate_scalar!(vmstate_info_uint64, u64);
 impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer);
 
+#[macro_export]
 macro_rules! impl_vmstate_c_struct {
     ($type:ty, $vmsd:expr) => {
         unsafe impl VMState for $type {
-            const BASE: VMStateField = $crate::bindings::VMStateField {
-                vmsd: addr_of!($vmsd),
-                size: mem::size_of::<$type>(),
-                flags: VMStateFlags::VMS_STRUCT,
-                ..Zeroable::ZERO
+            const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField {
+                vmsd: ::std::ptr::addr_of!($vmsd),
+                size: ::std::mem::size_of::<$type>(),
+                flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
+                ..$crate::zeroable::Zeroable::ZERO
             };
         }
     };
 }
 
-impl_vmstate_c_struct!(qdev::Clock, bindings::vmstate_clock);
-
 // Pointer types using the underlying type's VMState plus VMS_POINTER
 // Note that references are not supported, though references to cells
 // could be allowed.
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 14/33] rust: move VMState handling to QOM module
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (11 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 13/33] rust: move vmstate_clock!() to qdev module Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:26   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 15/33] rust: move Cell vmstate impl Paolo Bonzini
                   ` (20 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This will allow to split vmstate to a standalone crate next.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-9-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/src/qom.rs     |  3 +++
 rust/qemu-api/src/vmstate.rs | 11 +++++------
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 014ffb1fd88..12c6fc6752c 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -109,6 +109,7 @@
         object_get_typename, object_new, object_ref, object_unref, TypeInfo,
     },
     cell::{bql_locked, Opaque},
+    impl_vmstate_pointer,
 };
 
 /// A safe wrapper around [`bindings::Object`].
@@ -948,3 +949,5 @@ fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 
 impl<T> ObjectClassMethods for T where T: IsA<Object> {}
 impl<R: ObjectDeref> ObjectMethods for R where R::Target: IsA<Object> {}
+
+impl_vmstate_pointer!(Owned<T> where T: VMState + ObjectType);
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 59c7950a5aa..4e1086779ac 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -40,8 +40,6 @@
     bindings::{self, VMStateFlags},
     callbacks::FnCall,
     errno::{into_neg_errno, Errno},
-    prelude::*,
-    qom::Owned,
     zeroable::Zeroable,
 };
 
@@ -128,7 +126,8 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`,
 ///   [`BqlCell`], [`BqlRefCell`]
 /// * a raw pointer to any of the above
-/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above
+/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of
+///   the above
 /// * an array of any of the above
 ///
 /// In order to support other types, the trait `VMState` must be implemented
@@ -335,10 +334,11 @@ unsafe impl VMState for $type {
 // Note that references are not supported, though references to cells
 // could be allowed.
 
+#[macro_export]
 macro_rules! impl_vmstate_pointer {
     ($type:ty where $base:tt: VMState $($where:tt)*) => {
-        unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
-            const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag();
+        unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
+            const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag();
         }
     };
 }
@@ -350,7 +350,6 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
 // Unlike C pointers, Box is always non-null therefore there is no need
 // to specify VMS_ALLOC.
 impl_vmstate_pointer!(Box<T> where T: VMState);
-impl_vmstate_pointer!(Owned<T> where T: VMState + ObjectType);
 
 // Arrays using the underlying type's VMState plus
 // VMS_ARRAY/VMS_ARRAY_OF_POINTER
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 15/33] rust: move Cell vmstate impl
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (12 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 14/33] rust: move VMState handling to QOM module Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:31   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 16/33] rust: split Rust-only "common" crate Paolo Bonzini
                   ` (19 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This will allow to split vmstate to a standalone crate next.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-10-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/src/cell.rs    |  6 +++++-
 rust/qemu-api/src/vmstate.rs | 14 +++++++-------
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index 148a13e3f56..9943d7286b6 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -221,7 +221,7 @@
     ptr::NonNull,
 };
 
-use crate::bindings;
+use crate::{bindings, impl_vmstate_transparent};
 
 /// An internal function that is used by doctests.
 pub fn bql_start_test() {
@@ -456,6 +456,8 @@ pub fn take(&self) -> T {
     }
 }
 
+impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
+
 /// A mutable memory location with dynamically checked borrow rules,
 /// protected by the Big QEMU Lock.
 ///
@@ -764,6 +766,8 @@ fn from(t: T) -> BqlRefCell<T> {
     }
 }
 
+impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
+
 struct BorrowRef<'b> {
     borrow: &'b Cell<BorrowFlag>,
 }
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 4e1086779ac..ce42b031bc1 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -124,7 +124,8 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 /// * scalar types (integer and `bool`)
 /// * the C struct `QEMUTimer`
 /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`,
-///   [`BqlCell`], [`BqlRefCell`]
+///   [`BqlCell`](crate::cell::BqlCell),
+///   [`BqlRefCell`](crate::cell::BqlRefCell)),
 /// * a raw pointer to any of the above
 /// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of
 ///   the above
@@ -254,14 +255,15 @@ unsafe impl $crate::vmstate::VMState for $tuple {
 
 // Transparent wrappers: just use the internal type
 
+#[macro_export]
 macro_rules! impl_vmstate_transparent {
     ($type:ty where $base:tt: VMState $($where:tt)*) => {
-        unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
-            const BASE: VMStateField = VMStateField {
+        unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
+            const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField {
                 size: mem::size_of::<$type>(),
-                ..<$base as VMState>::BASE
+                ..<$base as $crate::vmstate::VMState>::BASE
             };
-            const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG;
+            const VARRAY_FLAG: $crate::bindings::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG;
         }
     };
 }
@@ -269,8 +271,6 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
 impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
 impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
 impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
-impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
-impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
 impl_vmstate_transparent!(crate::cell::Opaque<T> where T: VMState);
 
 #[macro_export]
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 16/33] rust: split Rust-only "common" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (13 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 15/33] rust: move Cell vmstate impl Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-11 10:44   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 17/33] rust: make build.rs generic over various ./rust/projects Paolo Bonzini
                   ` (18 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-6-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                                 |   1 +
 rust/Cargo.lock                             |  10 +
 rust/Cargo.toml                             |   1 +
 rust/common/Cargo.toml                      |  19 ++
 rust/common/meson.build                     |  34 +++
 rust/{qemu-api => common}/src/assertions.rs |  16 +-
 rust/{qemu-api => common}/src/bitops.rs     |   1 -
 rust/{qemu-api => common}/src/callbacks.rs  |  10 +-
 rust/{qemu-api => common}/src/errno.rs      |   8 +-
 rust/common/src/lib.rs                      |  20 ++
 rust/common/src/opaque.rs                   | 238 ++++++++++++++++++++
 rust/{qemu-api => common}/src/uninit.rs     |   2 +-
 rust/common/src/zeroable.rs                 |  18 ++
 rust/hw/char/pl011/Cargo.toml               |   1 +
 rust/hw/char/pl011/meson.build              |   3 +-
 rust/hw/char/pl011/src/device.rs            |   3 +-
 rust/hw/timer/hpet/Cargo.toml               |   1 +
 rust/hw/timer/hpet/meson.build              |   3 +-
 rust/hw/timer/hpet/src/device.rs            |   2 +-
 rust/hw/timer/hpet/src/fw_cfg.rs            |   3 +-
 rust/meson.build                            |   1 +
 rust/qemu-api-macros/src/lib.rs             |  16 +-
 rust/qemu-api-macros/src/tests.rs           |   6 +-
 rust/qemu-api/Cargo.toml                    |   1 +
 rust/qemu-api/meson.build                   |  14 +-
 rust/qemu-api/src/bindings.rs               |  21 ++
 rust/qemu-api/src/cell.rs                   | 235 +------------------
 rust/qemu-api/src/chardev.rs                |   5 +-
 rust/qemu-api/src/error.rs                  |   3 +-
 rust/qemu-api/src/irq.rs                    |   3 +-
 rust/qemu-api/src/lib.rs                    |   6 -
 rust/qemu-api/src/log.rs                    |   4 +-
 rust/qemu-api/src/memory.rs                 |   5 +-
 rust/qemu-api/src/prelude.rs                |   6 +-
 rust/qemu-api/src/qdev.rs                   |  10 +-
 rust/qemu-api/src/qom.rs                    |   3 +-
 rust/qemu-api/src/sysbus.rs                 |   3 +-
 rust/qemu-api/src/timer.rs                  |   8 +-
 rust/qemu-api/src/vmstate.rs                |  23 +-
 rust/qemu-api/src/zeroable.rs               |  37 ---
 rust/qemu-api/tests/vmstate_tests.rs        |   3 +-
 41 files changed, 448 insertions(+), 359 deletions(-)
 create mode 100644 rust/common/Cargo.toml
 create mode 100644 rust/common/meson.build
 rename rust/{qemu-api => common}/src/assertions.rs (92%)
 rename rust/{qemu-api => common}/src/bitops.rs (98%)
 rename rust/{qemu-api => common}/src/callbacks.rs (97%)
 rename rust/{qemu-api => common}/src/errno.rs (98%)
 create mode 100644 rust/common/src/lib.rs
 create mode 100644 rust/common/src/opaque.rs
 rename rust/{qemu-api => common}/src/uninit.rs (98%)
 create mode 100644 rust/common/src/zeroable.rs
 delete mode 100644 rust/qemu-api/src/zeroable.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 1ae28e88042..2feb743c502 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3513,6 +3513,7 @@ F: include/hw/registerfields.h
 Rust
 M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 S: Maintained
+F: rust/common/
 F: rust/qemu-api
 F: rust/qemu-api-macros
 F: rust/rustfmt.toml
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 4baf6ba663c..71e8c7ed62a 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -44,6 +44,13 @@ dependencies = [
  "qemu_api_macros",
 ]
 
+[[package]]
+name = "common"
+version = "0.1.0"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "either"
 version = "1.12.0"
@@ -63,6 +70,7 @@ dependencies = [
 name = "hpet"
 version = "0.1.0"
 dependencies = [
+ "common",
  "qemu_api",
  "qemu_api_macros",
 ]
@@ -89,6 +97,7 @@ dependencies = [
  "bilge",
  "bilge-impl",
  "bits",
+ "common",
  "qemu_api",
  "qemu_api_macros",
 ]
@@ -130,6 +139,7 @@ name = "qemu_api"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "common",
  "foreign",
  "libc",
  "qemu_api_macros",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index cd4bf8ef8e1..c0426d42430 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -2,6 +2,7 @@
 resolver = "2"
 members = [
     "bits",
+    "common",
     "qemu-api-macros",
     "qemu-api",
     "hw/char/pl011",
diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml
new file mode 100644
index 00000000000..5e106427e80
--- /dev/null
+++ b/rust/common/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "common"
+version = "0.1.0"
+description = "Rust common code for QEMU"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+libc.workspace = true
+
+[lints]
+workspace = true
diff --git a/rust/common/meson.build b/rust/common/meson.build
new file mode 100644
index 00000000000..230a967760d
--- /dev/null
+++ b/rust/common/meson.build
@@ -0,0 +1,34 @@
+_common_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+_common_rs = static_library(
+  'common',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/assertions.rs',
+      'src/bitops.rs',
+      'src/callbacks.rs',
+      'src/errno.rs',
+      'src/opaque.rs',
+      'src/uninit.rs',
+      'src/zeroable.rs',
+    ],
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  rust_args: _common_cfg,
+  dependencies: [libc_rs],
+)
+
+common_rs = declare_dependency(link_with: [_common_rs])
+
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-common-doctests',
+     _common_rs,
+     protocol: 'rust',
+     dependencies: common_rs,
+     suite: ['doc', 'rust'])
diff --git a/rust/qemu-api/src/assertions.rs b/rust/common/src/assertions.rs
similarity index 92%
rename from rust/qemu-api/src/assertions.rs
rename to rust/common/src/assertions.rs
index e4fe23b674e..91f83a5d3d4 100644
--- a/rust/qemu-api/src/assertions.rs
+++ b/rust/common/src/assertions.rs
@@ -8,7 +8,7 @@
 //! types match the expectations of C code.
 //!
 //! Documentation is hidden because it only exposes macros, which
-//! are exported directly from `qemu_api`.
+//! are exported directly from `common`.
 
 // Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292
 // (stackoverflow answers are released under MIT license).
@@ -27,7 +27,7 @@ impl<T> EqType for T {
 /// # Examples
 ///
 /// ```
-/// # use qemu_api::assert_same_type;
+/// # use common::assert_same_type;
 /// # use std::ops::Deref;
 /// assert_same_type!(u32, u32);
 /// assert_same_type!(<Box<u32> as Deref>::Target, u32);
@@ -36,7 +36,7 @@ impl<T> EqType for T {
 /// Different types will cause a compile failure
 ///
 /// ```compile_fail
-/// # use qemu_api::assert_same_type;
+/// # use common::assert_same_type;
 /// assert_same_type!(&Box<u32>, &u32);
 /// ```
 #[macro_export]
@@ -61,7 +61,7 @@ fn types_must_be_equal<T, U>(_: T)
 /// # Examples
 ///
 /// ```
-/// # use qemu_api::assert_field_type;
+/// # use common::assert_field_type;
 /// pub struct A {
 ///     field1: u32,
 /// }
@@ -72,7 +72,7 @@ fn types_must_be_equal<T, U>(_: T)
 /// Different types will cause a compile failure
 ///
 /// ```compile_fail
-/// # use qemu_api::assert_field_type;
+/// # use common::assert_field_type;
 /// # pub struct A { field1: u32 }
 /// assert_field_type!(A, field1, i32);
 /// ```
@@ -103,7 +103,7 @@ const fn types_must_be_equal<T, U>(_: &T)
 /// # Examples
 ///
 /// ```
-/// # use qemu_api::assert_match;
+/// # use common::assert_match;
 /// // JoinHandle does not implement `Eq`, therefore the result
 /// // does not either.
 /// let result: Result<std::thread::JoinHandle<()>, u32> = Err(42);
@@ -132,12 +132,12 @@ macro_rules! assert_match {
 /// # Examples
 ///
 /// ```
-/// # use qemu_api::static_assert;
+/// # use common::static_assert;
 /// static_assert!("abc".len() == 3);
 /// ```
 ///
 /// ```compile_fail
-/// # use qemu_api::static_assert;
+/// # use common::static_assert;
 /// static_assert!("abc".len() == 2); // does not compile
 /// ```
 #[macro_export]
diff --git a/rust/qemu-api/src/bitops.rs b/rust/common/src/bitops.rs
similarity index 98%
rename from rust/qemu-api/src/bitops.rs
rename to rust/common/src/bitops.rs
index b1e3a530ab5..06c78c3b8a7 100644
--- a/rust/qemu-api/src/bitops.rs
+++ b/rust/common/src/bitops.rs
@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 //! This module provides bit operation extensions to integer types.
-//! It is usually included via the `qemu_api` prelude.
 
 use std::ops::{
     Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
diff --git a/rust/qemu-api/src/callbacks.rs b/rust/common/src/callbacks.rs
similarity index 97%
rename from rust/qemu-api/src/callbacks.rs
rename to rust/common/src/callbacks.rs
index dbe2305f509..b8898fe96f7 100644
--- a/rust/qemu-api/src/callbacks.rs
+++ b/rust/common/src/callbacks.rs
@@ -55,7 +55,7 @@
 /// # Examples
 ///
 /// ```
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
 /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
 ///     F::call((s,))
 /// }
@@ -71,7 +71,7 @@
 /// Attempting to pass a non-zero-sized closure causes a compile-time failure:
 ///
 /// ```compile_fail
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
 /// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String {
 /// #     F::call((s,))
 /// # }
@@ -82,7 +82,7 @@
 /// `()` can be used to indicate "no function":
 ///
 /// ```
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
 /// fn optional<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option<String> {
 ///     if F::IS_SOME {
 ///         Some(F::call((s,)))
@@ -97,7 +97,7 @@
 /// Invoking `F::call` will then be a run-time error.
 ///
 /// ```should_panic
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
 /// # fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
 /// #     F::call((s,))
 /// # }
@@ -120,7 +120,7 @@ pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
     /// You can use `IS_SOME` to catch this at compile time:
     ///
     /// ```compile_fail
-    /// # use qemu_api::callbacks::FnCall;
+    /// # use common::callbacks::FnCall;
     /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
     ///     const { assert!(F::IS_SOME) }
     ///     F::call((s,))
diff --git a/rust/qemu-api/src/errno.rs b/rust/common/src/errno.rs
similarity index 98%
rename from rust/qemu-api/src/errno.rs
rename to rust/common/src/errno.rs
index 507850fe33c..64b2933b078 100644
--- a/rust/qemu-api/src/errno.rs
+++ b/rust/common/src/errno.rs
@@ -185,7 +185,7 @@ fn map_ok(self) -> i32 {
 /// are interpreted as negated `errno` and turned into an `Err`.
 ///
 /// ```
-/// # use qemu_api::errno::into_io_result;
+/// # use common::errno::into_io_result;
 /// # use std::io::ErrorKind;
 /// let ok = into_io_result(1i32).unwrap();
 /// assert_eq!(ok, 1u32);
@@ -201,7 +201,7 @@ fn map_ok(self) -> i32 {
 /// likely overflows and will panic:
 ///
 /// ```should_panic
-/// # use qemu_api::errno::into_io_result;
+/// # use common::errno::into_io_result;
 /// # #[allow(dead_code)]
 /// let err = into_io_result(-0x1234_5678i32); // panic
 /// ```
@@ -213,7 +213,7 @@ pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
 /// values to report errors.
 ///
 /// ```
-/// # use qemu_api::errno::into_neg_errno;
+/// # use common::errno::into_neg_errno;
 /// # use std::io::{self, ErrorKind};
 /// let ok: io::Result<()> = Ok(());
 /// assert_eq!(into_neg_errno(ok), 0);
@@ -232,7 +232,7 @@ pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
 /// positive:
 ///
 /// ```should_panic
-/// # use qemu_api::errno::into_neg_errno;
+/// # use common::errno::into_neg_errno;
 /// # use std::io;
 /// let err: io::Result<u32> = Ok(0x8899_AABB);
 /// into_neg_errno(err) // panic
diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs
new file mode 100644
index 00000000000..25216503aab
--- /dev/null
+++ b/rust/common/src/lib.rs
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod assertions;
+
+pub mod bitops;
+
+pub mod callbacks;
+pub use callbacks::FnCall;
+
+pub mod errno;
+pub use errno::Errno;
+
+pub mod opaque;
+pub use opaque::{Opaque, Wrapper};
+
+pub mod uninit;
+pub use uninit::MaybeUninitField;
+
+pub mod zeroable;
+pub use zeroable::Zeroable;
diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs
new file mode 100644
index 00000000000..d25a5f3ae1a
--- /dev/null
+++ b/rust/common/src/opaque.rs
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: MIT
+
+//! ## Opaque wrappers
+//!
+//! The cell types from the previous section are useful at the boundaries
+//! of code that requires interior mutability.  When writing glue code that
+//! interacts directly with C structs, however, it is useful to operate
+//! at a lower level.
+//!
+//! C functions often violate Rust's fundamental assumptions about memory
+//! safety by modifying memory even if it is shared.  Furthermore, C structs
+//! often start their life uninitialized and may be populated lazily.
+//!
+//! For this reason, this module provides the [`Opaque<T>`] type to opt out
+//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
+//! value is always through raw pointers, obtained via methods like
+//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
+//! pointers can then be passed to C functions or dereferenced; both actions
+//! require `unsafe` blocks, making it clear where safety guarantees must be
+//! manually verified. For example
+//!
+//! ```ignore
+//! unsafe {
+//!     let state = Opaque::<MyStruct>::uninit();
+//!     qemu_struct_init(state.as_mut_ptr());
+//! }
+//! ```
+//!
+//! [`Opaque<T>`] will usually be wrapped one level further, so that
+//! bridge methods can be added to the wrapper:
+//!
+//! ```ignore
+//! pub struct MyStruct(Opaque<bindings::MyStruct>);
+//!
+//! impl MyStruct {
+//!     fn new() -> Pin<Box<MyStruct>> {
+//!         let result = Box::pin(unsafe { Opaque::uninit() });
+//!         unsafe { qemu_struct_init(result.as_mut_ptr()) };
+//!         result
+//!     }
+//! }
+//! ```
+//!
+//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
+//! several advantages:
+//!
+//! * The choice of traits to be implemented is not limited by the
+//!   bindgen-generated code.  For example, [`Drop`] can be added without
+//!   disabling [`Copy`] on the underlying bindgen type
+//!
+//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
+//!   type rather than being automatically derived from the C struct's layout
+//!
+//! * Methods can be implemented in a separate crate from the bindgen-generated
+//!   bindings
+//!
+//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
+//!   implementations can be customized to be more readable than the raw C
+//!   struct representation
+//!
+//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
+//! assert in the code that the right lock is taken, to use it together
+//! with a custom lock guard type, or to let C code take the lock, as
+//! appropriate.  It is also possible to use it with non-thread-safe
+//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
+//! it is neither `Sync` nor `Send`.
+//!
+//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
+//! and only at FFI boundaries. For QEMU-specific types that need interior
+//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
+//!
+//! [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html
+//! [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html
+use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull};
+
+/// Stores an opaque value that is shared with C code.
+///
+/// Often, C structs can changed when calling a C function even if they are
+/// behind a shared Rust reference, or they can be initialized lazily and have
+/// invalid bit patterns (e.g. `3` for a [`bool`]).  This goes against Rust's
+/// strict aliasing rules, which normally prevent mutation through shared
+/// references.
+///
+/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
+/// assume the usual constraints that Rust structs require, and allows using
+/// shared references on the Rust side.
+///
+/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
+/// of `T`.
+#[repr(transparent)]
+pub struct Opaque<T> {
+    value: UnsafeCell<MaybeUninit<T>>,
+    // PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
+    // one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
+    // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
+    _pin: PhantomPinned,
+}
+
+impl<T> Opaque<T> {
+    /// Creates a new shared reference from a C pointer
+    ///
+    /// # Safety
+    ///
+    /// The pointer must be valid, though it need not point to a valid value.
+    pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
+        let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
+        // SAFETY: Self is a transparent wrapper over T
+        unsafe { ptr.as_ref() }
+    }
+
+    /// Creates a new opaque object with uninitialized contents.
+    ///
+    /// # Safety
+    ///
+    /// Ultimately the pointer to the returned value will be dereferenced
+    /// in another `unsafe` block, for example when passing it to a C function,
+    /// but the functions containing the dereference are usually safe.  The
+    /// value returned from `uninit()` must be initialized and pinned before
+    /// calling them.
+    pub const unsafe fn uninit() -> Self {
+        Self {
+            value: UnsafeCell::new(MaybeUninit::uninit()),
+            _pin: PhantomPinned,
+        }
+    }
+
+    /// Creates a new opaque object with zeroed contents.
+    ///
+    /// # Safety
+    ///
+    /// Ultimately the pointer to the returned value will be dereferenced
+    /// in another `unsafe` block, for example when passing it to a C function,
+    /// but the functions containing the dereference are usually safe.  The
+    /// value returned from `uninit()` must be pinned (and possibly initialized)
+    /// before calling them.
+    pub const unsafe fn zeroed() -> Self {
+        Self {
+            value: UnsafeCell::new(MaybeUninit::zeroed()),
+            _pin: PhantomPinned,
+        }
+    }
+
+    /// Returns a raw mutable pointer to the opaque data.
+    pub const fn as_mut_ptr(&self) -> *mut T {
+        UnsafeCell::get(&self.value).cast()
+    }
+
+    /// Returns a raw pointer to the opaque data.
+    pub const fn as_ptr(&self) -> *const T {
+        self.as_mut_ptr().cast_const()
+    }
+
+    /// Returns a raw pointer to the opaque data that can be passed to a
+    /// C function as `void *`.
+    pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
+        UnsafeCell::get(&self.value).cast()
+    }
+
+    /// Converts a raw pointer to the wrapped type.
+    pub const fn raw_get(slot: *mut Self) -> *mut T {
+        // Compare with Linux's raw_get method, which goes through an UnsafeCell
+        // because it takes a *const Self instead.
+        slot.cast()
+    }
+}
+
+impl<T> fmt::Debug for Opaque<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let mut name: String = "Opaque<".to_string();
+        name += std::any::type_name::<T>();
+        name += ">";
+        f.debug_tuple(&name).field(&self.as_ptr()).finish()
+    }
+}
+
+impl<T: Default> Opaque<T> {
+    /// Creates a new opaque object with default contents.
+    ///
+    /// # Safety
+    ///
+    /// Ultimately the pointer to the returned value will be dereferenced
+    /// in another `unsafe` block, for example when passing it to a C function,
+    /// but the functions containing the dereference are usually safe.  The
+    /// value returned from `uninit()` must be pinned before calling them.
+    pub unsafe fn new() -> Self {
+        Self {
+            value: UnsafeCell::new(MaybeUninit::new(T::default())),
+            _pin: PhantomPinned,
+        }
+    }
+}
+
+/// Annotates [`Self`] as a transparent wrapper for another type.
+///
+/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro.
+///
+/// # Examples
+///
+/// ```
+/// # use std::mem::ManuallyDrop;
+/// # use common::opaque::Wrapper;
+/// #[repr(transparent)]
+/// pub struct Example {
+///     inner: ManuallyDrop<String>,
+/// }
+///
+/// unsafe impl Wrapper for Example {
+///     type Wrapped = String;
+/// }
+/// ```
+///
+/// # Safety
+///
+/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type,
+/// whether directly or indirectly.
+///
+/// # Methods
+///
+/// By convention, types that implement Wrapper also implement the following
+/// methods:
+///
+/// ```ignore
+/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self;
+/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped;
+/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped;
+/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped;
+/// ```
+///
+/// They are not defined here to allow them to be `const`.
+///
+/// [`qemu_api_macros::Wrapper`]: ../../qemu_api_macros/derive.Wrapper.html
+pub unsafe trait Wrapper {
+    type Wrapped;
+}
+
+unsafe impl<T> Wrapper for Opaque<T> {
+    type Wrapped = T;
+}
diff --git a/rust/qemu-api/src/uninit.rs b/rust/common/src/uninit.rs
similarity index 98%
rename from rust/qemu-api/src/uninit.rs
rename to rust/common/src/uninit.rs
index b0a829729dd..e7f9fcd2e3f 100644
--- a/rust/qemu-api/src/uninit.rs
+++ b/rust/common/src/uninit.rs
@@ -63,7 +63,7 @@ fn deref_mut(&mut self) -> &mut MaybeUninit<U> {
 /// }
 ///
 /// # use std::mem::MaybeUninit;
-/// # use qemu_api::{assert_match, uninit_field_mut};
+/// # use common::{assert_match, uninit_field_mut};
 ///
 /// let mut s: MaybeUninit<S> = MaybeUninit::zeroed();
 /// uninit_field_mut!(s, x).write(5);
diff --git a/rust/common/src/zeroable.rs b/rust/common/src/zeroable.rs
new file mode 100644
index 00000000000..fd056deb1f6
--- /dev/null
+++ b/rust/common/src/zeroable.rs
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Defines a trait for structs that can be safely initialized with zero bytes.
+
+/// Encapsulates the requirement that
+/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
+/// behavior.
+///
+/// # Safety
+///
+/// Do not add this trait to a type unless all-zeroes is a valid value for the
+/// type.  In particular, raw pointers can be zero, but references and
+/// `NonNull<T>` cannot.
+pub unsafe trait Zeroable: Default {
+    /// Return a value of Self whose memory representation consists of all
+    /// zeroes, with the possible exclusion of padding bytes.
+    const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
+}
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 88ef110507d..6d15f107dfa 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -16,6 +16,7 @@ rust-version.workspace = true
 bilge = { version = "0.2.0" }
 bilge-impl = { version = "0.2.0" }
 bits = { path = "../../../bits" }
+common = { path = "../../../common" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 2a1be329abc..c4a9f531f7d 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -7,7 +7,8 @@ _libpl011_rs = static_library(
     bilge_rs,
     bilge_impl_rs,
     bits_rs,
-    qemu_api,
+    common_rs,
+    qemu_api_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 8411db8d00c..b4aa6c45f87 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -4,6 +4,7 @@
 
 use std::{ffi::CStr, mem::size_of};
 
+use common::{static_assert, uninit_field_mut};
 use qemu_api::{
     chardev::{CharBackend, Chardev, Event},
     impl_vmstate_forward, impl_vmstate_struct,
@@ -14,9 +15,7 @@
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
     qom::{ObjectImpl, Owned, ParentField, ParentInit},
-    static_assert,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    uninit_field_mut,
     vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
     vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused,
 };
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index ac5df23c1d0..ba7354f07e0 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -11,6 +11,7 @@ repository.workspace = true
 rust-version.workspace = true
 
 [dependencies]
+common = { path = "../../../common" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index c2d7c0532ca..c91d330439a 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -4,7 +4,8 @@ _libhpet_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   dependencies: [
-    qemu_api,
+    common_rs,
+    qemu_api_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index dd5326a40d4..72375d31550 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -10,6 +10,7 @@
     slice::from_ref,
 };
 
+use common::{bitops::IntegerExt, uninit_field_mut};
 use qemu_api::{
     bindings::{
         address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
@@ -27,7 +28,6 @@
     qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
     timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
-    uninit_field_mut,
     vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
     vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
 };
diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs
index 619d662ee1e..0605225fbb1 100644
--- a/rust/hw/timer/hpet/src/fw_cfg.rs
+++ b/rust/hw/timer/hpet/src/fw_cfg.rs
@@ -4,7 +4,8 @@
 
 use std::ptr::addr_of_mut;
 
-use qemu_api::{cell::bql_locked, zeroable::Zeroable};
+use common::Zeroable;
+use qemu_api::cell::bql_locked;
 
 /// Each `HPETState` represents a Event Timer Block. The v1 spec supports
 /// up to 8 blocks. QEMU only uses 1 block (in PC machine).
diff --git a/rust/meson.build b/rust/meson.build
index 331f11b7e72..402f8d66007 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -22,6 +22,7 @@ qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true)
 
 genrs = []
 
+subdir('common')
 subdir('qemu-api-macros')
 subdir('bits')
 subdir('qemu-api')
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index a65a7ce2fe9..49003a47801 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -97,7 +97,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
         .ident;
 
     Ok(quote! {
-        ::qemu_api::assert_field_type!(#name, #parent,
+        ::common::assert_field_type!(#name, #parent,
             ::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>);
 
         ::qemu_api::module_init! {
@@ -125,20 +125,20 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
     let typ = &field.ty;
 
     Ok(quote! {
-        unsafe impl ::qemu_api::cell::Wrapper for #name {
-            type Wrapped = <#typ as ::qemu_api::cell::Wrapper>::Wrapped;
+        unsafe impl ::common::opaque::Wrapper for #name {
+            type Wrapped = <#typ as ::common::opaque::Wrapper>::Wrapped;
         }
         impl #name {
-            pub unsafe fn from_raw<'a>(ptr: *mut <Self as ::qemu_api::cell::Wrapper>::Wrapped) -> &'a Self {
+            pub unsafe fn from_raw<'a>(ptr: *mut <Self as ::common::opaque::Wrapper>::Wrapped) -> &'a Self {
                 let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::<Self>();
                 unsafe { ptr.as_ref() }
             }
 
-            pub const fn as_mut_ptr(&self) -> *mut <Self as ::qemu_api::cell::Wrapper>::Wrapped {
+            pub const fn as_mut_ptr(&self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
                 self.0.as_mut_ptr()
             }
 
-            pub const fn as_ptr(&self) -> *const <Self as ::qemu_api::cell::Wrapper>::Wrapped {
+            pub const fn as_ptr(&self) -> *const <Self as ::common::opaque::Wrapper>::Wrapped {
                 self.0.as_ptr()
             }
 
@@ -146,7 +146,7 @@ pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void {
                 self.0.as_void_ptr()
             }
 
-            pub const fn raw_get(slot: *mut Self) -> *mut <Self as ::qemu_api::cell::Wrapper>::Wrapped {
+            pub const fn raw_get(slot: *mut Self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
                 slot.cast()
             }
         }
@@ -282,7 +282,7 @@ macro_rules! str_to_c_str {
                 offset: ::core::mem::offset_of!(#name, #field_name) as isize,
                 set_default: #set_default,
                 defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
-                ..::qemu_api::zeroable::Zeroable::ZERO
+                ..::common::Zeroable::ZERO
             }
         });
     }
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs
index 0e5a5728908..ef2806368d2 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -108,7 +108,7 @@ unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
                         offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
                         set_default: true,
                         defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
-                        ..::qemu_api::zeroable::Zeroable::ZERO
+                        ..::common::Zeroable::ZERO
                     }
                 ];
             }
@@ -135,7 +135,7 @@ unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
                         offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
                         set_default: true,
                         defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
-                        ..::qemu_api::zeroable::Zeroable::ZERO
+                        ..::common::Zeroable::ZERO
                     }
                 ];
             }
@@ -165,7 +165,7 @@ struct Foo {
             }
         },
         quote! {
-            ::qemu_api::assert_field_type!(
+            ::common::assert_field_type!(
                 Foo,
                 _unused,
                 ::qemu_api::qom::ParentField<<Foo as ::qemu_api::qom::ObjectImpl>::ParentType>
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index c5ed78035b6..2e0e2044912 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -14,6 +14,7 @@ repository.workspace = true
 rust-version.workspace = true
 
 [dependencies]
+common = { path = "../common" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 anyhow = { workspace = true }
 foreign = { workspace = true }
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 062009f1618..64af3caef58 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -52,13 +52,9 @@ _qemu_api_rs = static_library(
   structured_sources(
     [
       'src/lib.rs',
-      'src/assertions.rs',
       'src/bindings.rs',
-      'src/bitops.rs',
-      'src/callbacks.rs',
       'src/cell.rs',
       'src/chardev.rs',
-      'src/errno.rs',
       'src/error.rs',
       'src/irq.rs',
       'src/log.rs',
@@ -69,23 +65,21 @@ _qemu_api_rs = static_library(
       'src/qom.rs',
       'src/sysbus.rs',
       'src/timer.rs',
-      'src/uninit.rs',
       'src/vmstate.rs',
-      'src/zeroable.rs',
     ],
     {'.' : _qemu_api_bindings_inc_rs},
   ),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs,
+  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs,
                  qom, hwcore, chardev, migration],
 )
 
 rust.test('rust-qemu-api-tests', _qemu_api_rs,
           suite: ['unit', 'rust'])
 
-qemu_api = declare_dependency(link_with: [_qemu_api_rs],
+qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
   dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
 
 # Doctests are essentially integration tests, so they need the same dependencies.
@@ -94,7 +88,7 @@ qemu_api = declare_dependency(link_with: [_qemu_api_rs],
 rust.doctest('rust-qemu-api-doctests',
      _qemu_api_rs,
      protocol: 'rust',
-     dependencies: qemu_api,
+     dependencies: [qemu_api_rs],
      suite: ['doc', 'rust'])
 
 test('rust-qemu-api-integration',
@@ -104,7 +98,7 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [qemu_api]),
+        dependencies: [common_rs, qemu_api_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index b8104dea8be..3acdd903b52 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -20,6 +20,8 @@
 
 //! `bindgen`-generated declarations.
 
+use common::Zeroable;
+
 #[cfg(MESON)]
 include!("bindings.inc.rs");
 
@@ -56,3 +58,22 @@ unsafe impl Sync for VMStateField {}
 
 unsafe impl Send for VMStateInfo {}
 unsafe impl Sync for VMStateInfo {}
+
+// bindgen does not derive Default here
+#[allow(clippy::derivable_impls)]
+impl Default for crate::bindings::VMStateFlags {
+    fn default() -> Self {
+        Self(0)
+    }
+}
+
+unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
+unsafe impl Zeroable for crate::bindings::Property {}
+unsafe impl Zeroable for crate::bindings::VMStateFlags {}
+unsafe impl Zeroable for crate::bindings::VMStateField {}
+unsafe impl Zeroable for crate::bindings::VMStateDescription {}
+unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {}
+unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {}
+unsafe impl Zeroable for crate::bindings::MemoryRegionOps {}
+unsafe impl Zeroable for crate::bindings::MemTxAttrs {}
+unsafe impl Zeroable for crate::bindings::CharBackend {}
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index 9943d7286b6..d13848df200 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -141,82 +141,13 @@
 //! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
 //! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut).  The
 //! thread will panic if these rules are violated or if the BQL is not held.
-//!
-//! ## Opaque wrappers
-//!
-//! The cell types from the previous section are useful at the boundaries
-//! of code that requires interior mutability.  When writing glue code that
-//! interacts directly with C structs, however, it is useful to operate
-//! at a lower level.
-//!
-//! C functions often violate Rust's fundamental assumptions about memory
-//! safety by modifying memory even if it is shared.  Furthermore, C structs
-//! often start their life uninitialized and may be populated lazily.
-//!
-//! For this reason, this module provides the [`Opaque<T>`] type to opt out
-//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
-//! value is always through raw pointers, obtained via methods like
-//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
-//! pointers can then be passed to C functions or dereferenced; both actions
-//! require `unsafe` blocks, making it clear where safety guarantees must be
-//! manually verified. For example
-//!
-//! ```ignore
-//! unsafe {
-//!     let state = Opaque::<MyStruct>::uninit();
-//!     qemu_struct_init(state.as_mut_ptr());
-//! }
-//! ```
-//!
-//! [`Opaque<T>`] will usually be wrapped one level further, so that
-//! bridge methods can be added to the wrapper:
-//!
-//! ```ignore
-//! pub struct MyStruct(Opaque<bindings::MyStruct>);
-//!
-//! impl MyStruct {
-//!     fn new() -> Pin<Box<MyStruct>> {
-//!         let result = Box::pin(unsafe { Opaque::uninit() });
-//!         unsafe { qemu_struct_init(result.as_mut_ptr()) };
-//!         result
-//!     }
-//! }
-//! ```
-//!
-//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
-//! several advantages:
-//!
-//! * The choice of traits to be implemented is not limited by the
-//!   bindgen-generated code.  For example, [`Drop`] can be added without
-//!   disabling [`Copy`] on the underlying bindgen type
-//!
-//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
-//!   type rather than being automatically derived from the C struct's layout
-//!
-//! * Methods can be implemented in a separate crate from the bindgen-generated
-//!   bindings
-//!
-//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
-//!   implementations can be customized to be more readable than the raw C
-//!   struct representation
-//!
-//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
-//! assert in the code that the right lock is taken, to use it together
-//! with a custom lock guard type, or to let C code take the lock, as
-//! appropriate.  It is also possible to use it with non-thread-safe
-//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
-//! it is neither `Sync` nor `Send`.
-//!
-//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
-//! and only at FFI boundaries. For QEMU-specific types that need interior
-//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
 
 use std::{
     cell::{Cell, UnsafeCell},
     cmp::Ordering,
     fmt,
-    marker::{PhantomData, PhantomPinned},
-    mem::{self, MaybeUninit},
+    marker::PhantomData,
+    mem,
     ops::{Deref, DerefMut},
     ptr::NonNull,
 };
@@ -939,165 +870,3 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         (**self).fmt(f)
     }
 }
-
-/// Stores an opaque value that is shared with C code.
-///
-/// Often, C structs can changed when calling a C function even if they are
-/// behind a shared Rust reference, or they can be initialized lazily and have
-/// invalid bit patterns (e.g. `3` for a [`bool`]).  This goes against Rust's
-/// strict aliasing rules, which normally prevent mutation through shared
-/// references.
-///
-/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
-/// assume the usual constraints that Rust structs require, and allows using
-/// shared references on the Rust side.
-///
-/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
-/// of `T`.
-#[repr(transparent)]
-pub struct Opaque<T> {
-    value: UnsafeCell<MaybeUninit<T>>,
-    // PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
-    // one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
-    // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
-    _pin: PhantomPinned,
-}
-
-impl<T> Opaque<T> {
-    /// Creates a new shared reference from a C pointer
-    ///
-    /// # Safety
-    ///
-    /// The pointer must be valid, though it need not point to a valid value.
-    pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
-        let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
-        // SAFETY: Self is a transparent wrapper over T
-        unsafe { ptr.as_ref() }
-    }
-
-    /// Creates a new opaque object with uninitialized contents.
-    ///
-    /// # Safety
-    ///
-    /// Ultimately the pointer to the returned value will be dereferenced
-    /// in another `unsafe` block, for example when passing it to a C function,
-    /// but the functions containing the dereference are usually safe.  The
-    /// value returned from `uninit()` must be initialized and pinned before
-    /// calling them.
-    pub const unsafe fn uninit() -> Self {
-        Self {
-            value: UnsafeCell::new(MaybeUninit::uninit()),
-            _pin: PhantomPinned,
-        }
-    }
-
-    /// Creates a new opaque object with zeroed contents.
-    ///
-    /// # Safety
-    ///
-    /// Ultimately the pointer to the returned value will be dereferenced
-    /// in another `unsafe` block, for example when passing it to a C function,
-    /// but the functions containing the dereference are usually safe.  The
-    /// value returned from `uninit()` must be pinned (and possibly initialized)
-    /// before calling them.
-    pub const unsafe fn zeroed() -> Self {
-        Self {
-            value: UnsafeCell::new(MaybeUninit::zeroed()),
-            _pin: PhantomPinned,
-        }
-    }
-
-    /// Returns a raw mutable pointer to the opaque data.
-    pub const fn as_mut_ptr(&self) -> *mut T {
-        UnsafeCell::get(&self.value).cast()
-    }
-
-    /// Returns a raw pointer to the opaque data.
-    pub const fn as_ptr(&self) -> *const T {
-        self.as_mut_ptr().cast_const()
-    }
-
-    /// Returns a raw pointer to the opaque data that can be passed to a
-    /// C function as `void *`.
-    pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
-        UnsafeCell::get(&self.value).cast()
-    }
-
-    /// Converts a raw pointer to the wrapped type.
-    pub const fn raw_get(slot: *mut Self) -> *mut T {
-        // Compare with Linux's raw_get method, which goes through an UnsafeCell
-        // because it takes a *const Self instead.
-        slot.cast()
-    }
-}
-
-impl<T> fmt::Debug for Opaque<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut name: String = "Opaque<".to_string();
-        name += std::any::type_name::<T>();
-        name += ">";
-        f.debug_tuple(&name).field(&self.as_ptr()).finish()
-    }
-}
-
-impl<T: Default> Opaque<T> {
-    /// Creates a new opaque object with default contents.
-    ///
-    /// # Safety
-    ///
-    /// Ultimately the pointer to the returned value will be dereferenced
-    /// in another `unsafe` block, for example when passing it to a C function,
-    /// but the functions containing the dereference are usually safe.  The
-    /// value returned from `uninit()` must be pinned before calling them.
-    pub unsafe fn new() -> Self {
-        Self {
-            value: UnsafeCell::new(MaybeUninit::new(T::default())),
-            _pin: PhantomPinned,
-        }
-    }
-}
-
-/// Annotates [`Self`] as a transparent wrapper for another type.
-///
-/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro.
-///
-/// # Examples
-///
-/// ```
-/// # use std::mem::ManuallyDrop;
-/// # use qemu_api::cell::Wrapper;
-/// #[repr(transparent)]
-/// pub struct Example {
-///     inner: ManuallyDrop<String>,
-/// }
-///
-/// unsafe impl Wrapper for Example {
-///     type Wrapped = String;
-/// }
-/// ```
-///
-/// # Safety
-///
-/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type,
-/// whether directly or indirectly.
-///
-/// # Methods
-///
-/// By convention, types that implement Wrapper also implement the following
-/// methods:
-///
-/// ```ignore
-/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self;
-/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped;
-/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped;
-/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped;
-/// ```
-///
-/// They are not defined here to allow them to be `const`.
-pub unsafe trait Wrapper {
-    type Wrapped;
-}
-
-unsafe impl<T> Wrapper for Opaque<T> {
-    type Wrapped = T;
-}
diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs
index cb27be52569..5a351dcecb0 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -18,10 +18,11 @@
     slice,
 };
 
+use common::{callbacks::FnCall, errno, Opaque};
+
 use crate::{
     bindings,
-    callbacks::FnCall,
-    cell::{BqlRefMut, Opaque},
+    cell::{BqlRefCell, BqlRefMut},
     prelude::*,
 };
 
diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs
index e114fc4178b..8bac3cbec81 100644
--- a/rust/qemu-api/src/error.rs
+++ b/rust/qemu-api/src/error.rs
@@ -316,10 +316,11 @@ mod tests {
     use std::ffi::CStr;
 
     use anyhow::anyhow;
+    use common::assert_match;
     use foreign::OwnedPointer;
 
     use super::*;
-    use crate::{assert_match, bindings};
+    use crate::bindings;
 
     #[track_caller]
     fn error_for_test(msg: &CStr) -> OwnedPointer<Error> {
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index 1526e6f63a1..ea6b32848c5 100644
--- a/rust/qemu-api/src/irq.rs
+++ b/rust/qemu-api/src/irq.rs
@@ -10,9 +10,10 @@
     ptr,
 };
 
+use common::Opaque;
+
 use crate::{
     bindings::{self, qemu_set_irq},
-    cell::Opaque,
     prelude::*,
     qom::ObjectClass,
 };
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index daa2493bb69..6232e39e711 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -13,12 +13,8 @@
 #[rustfmt::skip]
 pub mod prelude;
 
-pub mod assertions;
-pub mod bitops;
-pub mod callbacks;
 pub mod cell;
 pub mod chardev;
-pub mod errno;
 pub mod error;
 pub mod irq;
 pub mod log;
@@ -28,9 +24,7 @@
 pub mod qom;
 pub mod sysbus;
 pub mod timer;
-pub mod uninit;
 pub mod vmstate;
-pub mod zeroable;
 
 // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
 // crate).
diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs
index a441b8c1f2e..d07f6272dc1 100644
--- a/rust/qemu-api/src/log.rs
+++ b/rust/qemu-api/src/log.rs
@@ -8,7 +8,9 @@
     ptr::NonNull,
 };
 
-use crate::{bindings, errno};
+use common::errno;
+
+use crate::bindings;
 
 #[repr(u32)]
 /// Represents specific error categories within QEMU's logging system.
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
index e40fad6cf19..f790cb5fd2d 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/qemu-api/src/memory.rs
@@ -10,14 +10,11 @@
 };
 
 pub use bindings::{hwaddr, MemTxAttrs};
+use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque};
 
 use crate::{
     bindings::{self, device_endian, memory_region_init_io},
-    callbacks::FnCall,
-    cell::Opaque,
     prelude::*,
-    uninit::MaybeUninitField,
-    zeroable::Zeroable,
 };
 
 pub struct MemoryRegionOps<T>(
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 8f9e23ee2c5..dcfe71497f0 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -4,13 +4,9 @@
 
 //! Commonly used traits and types for QEMU.
 
-pub use crate::bitops::IntegerExt;
-
 pub use crate::cell::BqlCell;
 pub use crate::cell::BqlRefCell;
 
-pub use crate::errno;
-
 pub use crate::log_mask_ln;
 
 pub use crate::qdev::DeviceMethods;
@@ -19,8 +15,8 @@
 pub use crate::qom::IsA;
 pub use crate::qom::Object;
 pub use crate::qom::ObjectCast;
-pub use crate::qom::ObjectDeref;
 pub use crate::qom::ObjectClassMethods;
+pub use crate::qom::ObjectDeref;
 pub use crate::qom::ObjectMethods;
 pub use crate::qom::ObjectType;
 
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index e2b4121cac5..8be125fae47 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -10,11 +10,11 @@
 };
 
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
+use common::{callbacks::FnCall, Opaque};
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
-    callbacks::FnCall,
-    cell::{bql_locked, Opaque},
+    cell::bql_locked,
     chardev::Chardev,
     error::{Error, Result},
     impl_vmstate_c_struct,
@@ -246,7 +246,7 @@ macro_rules! define_property {
             bitnr: $bitnr,
             set_default: true,
             defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
-            ..$crate::zeroable::Zeroable::ZERO
+            ..::common::zeroable::Zeroable::ZERO
         }
     };
     ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => {
@@ -257,7 +257,7 @@ macro_rules! define_property {
             offset: ::std::mem::offset_of!($state, $field) as isize,
             set_default: true,
             defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
-            ..$crate::zeroable::Zeroable::ZERO
+            ..::common::zeroable::Zeroable::ZERO
         }
     };
     ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => {
@@ -267,7 +267,7 @@ macro_rules! define_property {
             info: $prop,
             offset: ::std::mem::offset_of!($state, $field) as isize,
             set_default: false,
-            ..$crate::zeroable::Zeroable::ZERO
+            ..::common::zeroable::Zeroable::ZERO
         }
     };
 }
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 12c6fc6752c..49b4f03ccfc 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -102,13 +102,14 @@
 };
 
 pub use bindings::ObjectClass;
+use common::Opaque;
 
 use crate::{
     bindings::{
         self, object_class_dynamic_cast, object_dynamic_cast, object_get_class,
         object_get_typename, object_new, object_ref, object_unref, TypeInfo,
     },
-    cell::{bql_locked, Opaque},
+    cell::bql_locked,
     impl_vmstate_pointer,
 };
 
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index e92502a8fe6..4a5b4cbbf67 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -7,10 +7,11 @@
 use std::{ffi::CStr, ptr::addr_of_mut};
 
 pub use bindings::SysBusDeviceClass;
+use common::Opaque;
 
 use crate::{
     bindings,
-    cell::{bql_locked, Opaque},
+    cell::bql_locked,
     irq::{IRQState, InterruptSource},
     memory::MemoryRegion,
     prelude::*,
diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs
index 1e639eaf221..383e1a6e774 100644
--- a/rust/qemu-api/src/timer.rs
+++ b/rust/qemu-api/src/timer.rs
@@ -7,10 +7,10 @@
     pin::Pin,
 };
 
-use crate::{
-    bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType},
-    callbacks::FnCall,
-    cell::Opaque,
+use common::{callbacks::FnCall, Opaque};
+
+use crate::bindings::{
+    self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType,
 };
 
 /// A safe wrapper around [`bindings::QEMUTimer`].
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index ce42b031bc1..06aac3a73f4 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -35,14 +35,15 @@
     ptr::{addr_of, NonNull},
 };
 
-pub use crate::bindings::{MigrationPriority, VMStateField};
-use crate::{
-    bindings::{self, VMStateFlags},
+use common::{
     callbacks::FnCall,
     errno::{into_neg_errno, Errno},
-    zeroable::Zeroable,
+    Zeroable,
 };
 
+use crate::bindings::{self, VMStateFlags};
+pub use crate::bindings::{MigrationPriority, VMStateField};
+
 /// This macro is used to call a function with a generic argument bound
 /// to the type of a field.  The function must take a
 /// [`PhantomData`]`<T>` argument; `T` is the type of
@@ -271,7 +272,7 @@ unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmsta
 impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
 impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
 impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
-impl_vmstate_transparent!(crate::cell::Opaque<T> where T: VMState);
+impl_vmstate_transparent!(::common::Opaque<T> where T: VMState);
 
 #[macro_export]
 macro_rules! impl_vmstate_bitsized {
@@ -324,7 +325,7 @@ unsafe impl VMState for $type {
                 vmsd: ::std::ptr::addr_of!($vmsd),
                 size: ::std::mem::size_of::<$type>(),
                 flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
-                ..$crate::zeroable::Zeroable::ZERO
+                ..common::zeroable::Zeroable::ZERO
             };
         }
     };
@@ -367,7 +368,7 @@ macro_rules! vmstate_unused {
             size: $size,
             info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
             flags: $crate::bindings::VMStateFlags::VMS_BUFFER,
-            ..$crate::zeroable::Zeroable::ZERO
+            ..::common::Zeroable::ZERO
         }
     }};
 }
@@ -390,7 +391,7 @@ pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), b
 #[macro_export]
 macro_rules! vmstate_exist_fn {
     ($struct_name:ty, $test_fn:expr) => {{
-        const fn test_cb_builder__<T, F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>>(
+        const fn test_cb_builder__<T, F: for<'a> ::common::callbacks::FnCall<(&'a T, u8), bool>>(
             _phantom: ::core::marker::PhantomData<F>,
         ) -> $crate::vmstate::VMSFieldExistCb {
             const { assert!(F::IS_SOME) };
@@ -414,7 +415,7 @@ macro_rules! vmstate_fields {
             $($field),*,
             $crate::bindings::VMStateField {
                 flags: $crate::bindings::VMStateFlags::VMS_END,
-                ..$crate::zeroable::Zeroable::ZERO
+                ..::common::zeroable::Zeroable::ZERO
             }
         ];
         _FIELDS.as_ptr()
@@ -433,7 +434,7 @@ macro_rules! vmstate_validate {
                     | $crate::bindings::VMStateFlags::VMS_ARRAY.0,
             ),
             num: 0, // 0 elements: no data, only run test_fn callback
-            ..$crate::zeroable::Zeroable::ZERO
+            ..::common::zeroable::Zeroable::ZERO
         }
     };
 }
@@ -455,7 +456,7 @@ unsafe impl $crate::vmstate::VMState for $type {
                     vmsd: ::core::ptr::addr_of!(*VMSD),
                     size: ::core::mem::size_of::<$type>(),
                     flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
-                    ..$crate::zeroable::Zeroable::ZERO
+                    ..common::Zeroable::ZERO
                 }
             };
         }
diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs
deleted file mode 100644
index d8239d08563..00000000000
--- a/rust/qemu-api/src/zeroable.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-//! Defines a trait for structs that can be safely initialized with zero bytes.
-
-/// Encapsulates the requirement that
-/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
-/// behavior.
-///
-/// # Safety
-///
-/// Do not add this trait to a type unless all-zeroes is a valid value for the
-/// type.  In particular, raw pointers can be zero, but references and
-/// `NonNull<T>` cannot.
-pub unsafe trait Zeroable: Default {
-    /// Return a value of Self whose memory representation consists of all
-    /// zeroes, with the possible exclusion of padding bytes.
-    const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
-}
-
-// bindgen does not derive Default here
-#[allow(clippy::derivable_impls)]
-impl Default for crate::bindings::VMStateFlags {
-    fn default() -> Self {
-        Self(0)
-    }
-}
-
-unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
-unsafe impl Zeroable for crate::bindings::Property {}
-unsafe impl Zeroable for crate::bindings::VMStateFlags {}
-unsafe impl Zeroable for crate::bindings::VMStateField {}
-unsafe impl Zeroable for crate::bindings::VMStateDescription {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps {}
-unsafe impl Zeroable for crate::bindings::MemTxAttrs {}
-unsafe impl Zeroable for crate::bindings::CharBackend {}
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs
index 2c0670ba0ee..d9e5bcc4987 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -9,12 +9,13 @@
     slice,
 };
 
+use common::Opaque;
 use qemu_api::{
     bindings::{
         vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
         vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
     },
-    cell::{BqlCell, Opaque},
+    cell::BqlCell,
     impl_vmstate_forward, impl_vmstate_struct,
     vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField},
     vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate,
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 17/33] rust: make build.rs generic over various ./rust/projects
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (14 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 16/33] rust: split Rust-only "common" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  7:43   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 18/33] rust: split "util" crate Paolo Bonzini
                   ` (17 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Guess the name of the subdir from the manifest directory, instead of
hard-coding it. In the following commits, other crates can then link to
this file, instead of maintaining their own copy.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-5-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/build.rs | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
index 29d09456257..5654d1d5624 100644
--- a/rust/qemu-api/build.rs
+++ b/rust/qemu-api/build.rs
@@ -9,12 +9,14 @@
 use std::{env, fs::remove_file, io::Result, path::Path};
 
 fn main() -> Result<()> {
+    let manifest_dir = env!("CARGO_MANIFEST_DIR");
     let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
-        format!("{root}/rust/qemu-api/bindings.inc.rs")
+        let sub = get_rust_subdir(manifest_dir).unwrap();
+        format!("{root}/{sub}/bindings.inc.rs")
     } else {
         // Placing bindings.inc.rs in the source directory is supported
         // but not documented or encouraged.
-        format!("{}/src/bindings.inc.rs", env!("CARGO_MANIFEST_DIR"))
+        format!("{manifest_dir}/src/bindings.inc.rs")
     };
 
     let file = Path::new(&file);
@@ -41,3 +43,7 @@ fn main() -> Result<()> {
     println!("cargo:rerun-if-changed=build.rs");
     Ok(())
 }
+
+fn get_rust_subdir(path: &str) -> Option<&str> {
+    path.find("/rust").map(|index| &path[index + 1..])
+}
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 18/33] rust: split "util" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (15 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 17/33] rust: make build.rs generic over various ./rust/projects Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:00   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 19/33] rust: split "migration" crate Paolo Bonzini
                   ` (16 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-7-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                           |  1 +
 rust/qemu-api/wrapper.h               |  6 ---
 rust/util/wrapper.h                   | 32 ++++++++++++++++
 rust/Cargo.lock                       | 17 +++++++--
 rust/Cargo.toml                       |  5 +--
 rust/hw/char/pl011/Cargo.toml         |  1 +
 rust/hw/char/pl011/meson.build        |  1 +
 rust/hw/char/pl011/src/device.rs      |  7 ++--
 rust/hw/timer/hpet/Cargo.toml         |  1 +
 rust/hw/timer/hpet/meson.build        |  1 +
 rust/hw/timer/hpet/src/device.rs      |  6 +--
 rust/meson.build                      |  1 +
 rust/qemu-api-macros/src/lib.rs       |  2 +-
 rust/qemu-api-macros/src/tests.rs     |  2 +-
 rust/qemu-api/Cargo.toml              |  4 +-
 rust/qemu-api/build.rs                | 50 +-----------------------
 rust/qemu-api/meson.build             | 14 ++-----
 rust/qemu-api/src/bindings.rs         |  1 +
 rust/qemu-api/src/lib.rs              |  6 ---
 rust/qemu-api/src/prelude.rs          |  2 -
 rust/qemu-api/src/qdev.rs             |  4 +-
 rust/qemu-api/src/sysbus.rs           |  2 +-
 rust/qemu-api/src/vmstate.rs          |  2 +-
 rust/qemu-api/tests/tests.rs          |  2 +-
 rust/util/Cargo.toml                  | 23 +++++++++++
 rust/util/build.rs                    | 49 ++++++++++++++++++++++++
 rust/util/meson.build                 | 55 +++++++++++++++++++++++++++
 rust/util/src/bindings.rs             | 25 ++++++++++++
 rust/{qemu-api => util}/src/error.rs  |  5 +--
 rust/util/src/lib.rs                  |  9 +++++
 rust/{qemu-api => util}/src/log.rs    | 12 +++---
 rust/{qemu-api => util}/src/module.rs |  2 +-
 rust/{qemu-api => util}/src/timer.rs  |  0
 33 files changed, 243 insertions(+), 107 deletions(-)
 create mode 100644 rust/util/wrapper.h
 mode change 100644 => 120000 rust/qemu-api/build.rs
 create mode 100644 rust/util/Cargo.toml
 create mode 100644 rust/util/build.rs
 create mode 100644 rust/util/meson.build
 create mode 100644 rust/util/src/bindings.rs
 rename rust/{qemu-api => util}/src/error.rs (98%)
 create mode 100644 rust/util/src/lib.rs
 rename rust/{qemu-api => util}/src/log.rs (93%)
 rename rust/{qemu-api => util}/src/module.rs (97%)
 rename rust/{qemu-api => util}/src/timer.rs (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2feb743c502..b8d38b08549 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3517,6 +3517,7 @@ F: rust/common/
 F: rust/qemu-api
 F: rust/qemu-api-macros
 F: rust/rustfmt.toml
+F: rust/util/
 F: scripts/get-wraps-from-cargo-registry.py
 
 Rust-related patches CC here
diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h
index 15a1b19847f..cc7112406b2 100644
--- a/rust/qemu-api/wrapper.h
+++ b/rust/qemu-api/wrapper.h
@@ -48,9 +48,6 @@ typedef enum memory_order {
 #endif /* __CLANG_STDATOMIC_H */
 
 #include "qemu/osdep.h"
-#include "qemu/log.h"
-#include "qemu/log-for-trace.h"
-#include "qemu/module.h"
 #include "qemu-io.h"
 #include "system/system.h"
 #include "hw/sysbus.h"
@@ -61,11 +58,8 @@ typedef enum memory_order {
 #include "hw/qdev-properties.h"
 #include "hw/qdev-properties-system.h"
 #include "hw/irq.h"
-#include "qapi/error.h"
-#include "qapi/error-internal.h"
 #include "migration/vmstate.h"
 #include "chardev/char-serial.h"
 #include "exec/memattrs.h"
-#include "qemu/timer.h"
 #include "system/address-spaces.h"
 #include "hw/char/pl011.h"
diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h
new file mode 100644
index 00000000000..b9ed68a01d8
--- /dev/null
+++ b/rust/util/wrapper.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "qapi/error.h"
+#include "qapi/error-internal.h"
+#include "qemu/log-for-trace.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 71e8c7ed62a..757c03cbdec 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -73,6 +73,7 @@ dependencies = [
  "common",
  "qemu_api",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -100,6 +101,7 @@ dependencies = [
  "common",
  "qemu_api",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -138,11 +140,9 @@ dependencies = [
 name = "qemu_api"
 version = "0.1.0"
 dependencies = [
- "anyhow",
  "common",
- "foreign",
- "libc",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -180,6 +180,17 @@ version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
+[[package]]
+name = "util"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "common",
+ "foreign",
+ "libc",
+ "qemu_api_macros",
+]
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index c0426d42430..cfdd535e3bd 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -7,6 +7,7 @@ members = [
     "qemu-api",
     "hw/char/pl011",
     "hw/timer/hpet",
+    "util",
 ]
 
 [workspace.package]
@@ -24,9 +25,7 @@ foreign = "~0.3.1"
 libc = "0.2.162"
 
 [workspace.lints.rust]
-unexpected_cfgs = { level = "deny", check-cfg = [
-    'cfg(MESON)',
-] }
+unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] }
 
 # Occasionally, we may need to silence warnings and clippy lints that
 # were only introduced in newer Rust compiler versions.  Do not croak
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 6d15f107dfa..0cf9943fe86 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -17,6 +17,7 @@ bilge = { version = "0.2.0" }
 bilge-impl = { version = "0.2.0" }
 bits = { path = "../../../bits" }
 common = { path = "../../../common" }
+util = { path = "../../../util" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index c4a9f531f7d..8a931a4d03d 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -9,6 +9,7 @@ _libpl011_rs = static_library(
     bits_rs,
     common_rs,
     qemu_api_rs,
+    util_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index b4aa6c45f87..ab38d57fc41 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -9,8 +9,6 @@
     chardev::{CharBackend, Chardev, Event},
     impl_vmstate_forward, impl_vmstate_struct,
     irq::{IRQState, InterruptSource},
-    log::Log,
-    log_mask_ln,
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
@@ -19,6 +17,7 @@
     vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
     vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused,
 };
+use util::{log::Log, log_mask_ln};
 
 use crate::registers::{self, Interrupt, RegisterOffset};
 
@@ -172,7 +171,7 @@ impl ObjectImpl for PL011State {
 
 impl DeviceImpl for PL011State {
     const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_PL011);
-    const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
+    const REALIZE: Option<fn(&Self) -> util::Result<()>> = Some(Self::realize);
 }
 
 impl ResettablePhasesImpl for PL011State {
@@ -623,7 +622,7 @@ fn event(&self, event: Event) {
         }
     }
 
-    fn realize(&self) -> qemu_api::Result<()> {
+    fn realize(&self) -> util::Result<()> {
         self.char_backend
             .enable_handlers(self, Self::can_receive, Self::receive, Self::event);
         Ok(())
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index ba7354f07e0..dd9a5ed3d43 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -12,6 +12,7 @@ rust-version.workspace = true
 
 [dependencies]
 common = { path = "../../../common" }
+util = { path = "../../../util" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index c91d330439a..ca09660bf47 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -6,6 +6,7 @@ _libhpet_rs = static_library(
   dependencies: [
     common_rs,
     qemu_api_rs,
+    util_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 72375d31550..2be180fdedd 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -27,10 +27,10 @@
     qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
     qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
     vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
     vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
 };
+use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
 use crate::fw_cfg::HPETFwConfig;
 
@@ -728,7 +728,7 @@ fn post_init(&self) {
         }
     }
 
-    fn realize(&self) -> qemu_api::Result<()> {
+    fn realize(&self) -> util::Result<()> {
         if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS {
             Err(format!(
                 "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}"
@@ -1013,7 +1013,7 @@ unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState {
 
 impl DeviceImpl for HPETState {
     const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_HPET);
-    const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
+    const REALIZE: Option<fn(&Self) -> util::Result<()>> = Some(Self::realize);
 }
 
 impl ResettablePhasesImpl for HPETState {
diff --git a/rust/meson.build b/rust/meson.build
index 402f8d66007..a9d715e6e94 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -25,6 +25,7 @@ genrs = []
 subdir('common')
 subdir('qemu-api-macros')
 subdir('bits')
+subdir('util')
 subdir('qemu-api')
 
 subdir('hw')
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index 49003a47801..67650a9a269 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -100,7 +100,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
         ::common::assert_field_type!(#name, #parent,
             ::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>);
 
-        ::qemu_api::module_init! {
+        ::util::module_init! {
             MODULE_INIT_QOM => unsafe {
                 ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
             }
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs
index ef2806368d2..8e71ac6e677 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -170,7 +170,7 @@ struct Foo {
                 _unused,
                 ::qemu_api::qom::ParentField<<Foo as ::qemu_api::qom::ObjectImpl>::ParentType>
             );
-            ::qemu_api::module_init! {
+            ::util::module_init! {
                 MODULE_INIT_QOM => unsafe {
                     ::qemu_api::bindings::type_register_static(&<Foo as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
                 }
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 2e0e2044912..fbfb894421d 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -15,10 +15,8 @@ rust-version.workspace = true
 
 [dependencies]
 common = { path = "../common" }
+util = { path = "../util" }
 qemu_api_macros = { path = "../qemu-api-macros" }
-anyhow = { workspace = true }
-foreign = { workspace = true }
-libc = { workspace = true }
 
 [features]
 default = ["debug_cell"]
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
deleted file mode 100644
index 5654d1d5624..00000000000
--- a/rust/qemu-api/build.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#[cfg(unix)]
-use std::os::unix::fs::symlink as symlink_file;
-#[cfg(windows)]
-use std::os::windows::fs::symlink_file;
-use std::{env, fs::remove_file, io::Result, path::Path};
-
-fn main() -> Result<()> {
-    let manifest_dir = env!("CARGO_MANIFEST_DIR");
-    let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
-        let sub = get_rust_subdir(manifest_dir).unwrap();
-        format!("{root}/{sub}/bindings.inc.rs")
-    } else {
-        // Placing bindings.inc.rs in the source directory is supported
-        // but not documented or encouraged.
-        format!("{manifest_dir}/src/bindings.inc.rs")
-    };
-
-    let file = Path::new(&file);
-    if !Path::new(&file).exists() {
-        panic!(concat!(
-            "\n",
-            "    No generated C bindings found! Maybe you wanted one of\n",
-            "    `make clippy`, `make rustfmt`, `make rustdoc`?\n",
-            "\n",
-            "    For other uses of `cargo`, start a subshell with\n",
-            "    `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n",
-            "    the top of the build tree."
-        ));
-    }
-
-    let out_dir = env::var("OUT_DIR").unwrap();
-    let dest_path = format!("{out_dir}/bindings.inc.rs");
-    let dest_path = Path::new(&dest_path);
-    if dest_path.symlink_metadata().is_ok() {
-        remove_file(dest_path)?;
-    }
-    symlink_file(file, dest_path)?;
-
-    println!("cargo:rerun-if-changed=build.rs");
-    Ok(())
-}
-
-fn get_rust_subdir(path: &str) -> Option<&str> {
-    path.find("/rust").map(|index| &path[index + 1..])
-}
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
new file mode 120000
index 00000000000..71a3167885c
--- /dev/null
+++ b/rust/qemu-api/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 64af3caef58..7734f656a26 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -14,10 +14,8 @@ c_enums = [
   'MigrationPolicy',
   'MigrationPriority',
   'QEMUChrEvent',
-  'QEMUClockType',
   'ResetType',
   'device_endian',
-  'module_init_type',
 ]
 _qemu_api_bindgen_args = []
 foreach enum : c_enums
@@ -31,6 +29,7 @@ foreach enum : c_bitfields
   _qemu_api_bindgen_args += ['--bitfield-enum', enum]
 endforeach
 
+_qemu_api_bindgen_args += ['--blocklist-type', 'Error']
 # TODO: Remove this comment when the clang/libclang mismatch issue is solved.
 #
 # Rust bindings generation with `bindgen` might fail in some cases where the
@@ -55,16 +54,12 @@ _qemu_api_rs = static_library(
       'src/bindings.rs',
       'src/cell.rs',
       'src/chardev.rs',
-      'src/error.rs',
       'src/irq.rs',
-      'src/log.rs',
       'src/memory.rs',
-      'src/module.rs',
       'src/prelude.rs',
       'src/qdev.rs',
       'src/qom.rs',
       'src/sysbus.rs',
-      'src/timer.rs',
       'src/vmstate.rs',
     ],
     {'.' : _qemu_api_bindings_inc_rs},
@@ -72,13 +67,10 @@ _qemu_api_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs,
+  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, util_rs,
                  qom, hwcore, chardev, migration],
 )
 
-rust.test('rust-qemu-api-tests', _qemu_api_rs,
-          suite: ['unit', 'rust'])
-
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
   dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
 
@@ -98,7 +90,7 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [common_rs, qemu_api_rs]),
+        dependencies: [common_rs, util_rs, qemu_api_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 3acdd903b52..aedf42b652c 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -21,6 +21,7 @@
 //! `bindgen`-generated declarations.
 
 use common::Zeroable;
+use util::bindings::Error;
 
 #[cfg(MESON)]
 include!("bindings.inc.rs");
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 6232e39e711..54b252fb2c5 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -15,19 +15,13 @@
 
 pub mod cell;
 pub mod chardev;
-pub mod error;
 pub mod irq;
-pub mod log;
 pub mod memory;
-pub mod module;
 pub mod qdev;
 pub mod qom;
 pub mod sysbus;
-pub mod timer;
 pub mod vmstate;
 
 // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
 // crate).
 extern crate self as qemu_api;
-
-pub use error::{Error, Result};
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index dcfe71497f0..3d771481e40 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -7,8 +7,6 @@
 pub use crate::cell::BqlCell;
 pub use crate::cell::BqlRefCell;
 
-pub use crate::log_mask_ln;
-
 pub use crate::qdev::DeviceMethods;
 
 pub use crate::qom::InterfaceType;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 8be125fae47..d87479ce13b 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -11,12 +11,12 @@
 
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use common::{callbacks::FnCall, Opaque};
+use util::{Error, Result};
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
     cell::bql_locked,
     chardev::Chardev,
-    error::{Error, Result},
     impl_vmstate_c_struct,
     irq::InterruptSource,
     prelude::*,
@@ -183,7 +183,7 @@ pub trait DeviceImpl:
 /// readable/writeable from one thread at any time.
 unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(
     dev: *mut bindings::DeviceState,
-    errp: *mut *mut bindings::Error,
+    errp: *mut *mut util::bindings::Error,
 ) {
     let state = NonNull::new(dev).unwrap().cast::<T>();
     let result = T::REALIZE.unwrap()(unsafe { state.as_ref() });
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index 4a5b4cbbf67..2dbfc31dbda 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -114,7 +114,7 @@ fn sysbus_realize(&self) {
         unsafe {
             bindings::sysbus_realize(
                 self.upcast().as_mut_ptr(),
-                addr_of_mut!(bindings::error_fatal),
+                addr_of_mut!(util::bindings::error_fatal),
             );
         }
     }
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 06aac3a73f4..37e47cc4c6f 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -315,7 +315,7 @@ unsafe impl VMState for $type {
 impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16);
 impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
 impl_vmstate_scalar!(vmstate_info_uint64, u64);
-impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer);
+impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer);
 
 #[macro_export]
 macro_rules! impl_vmstate_c_struct {
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 13495687419..70ef4a80d57 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -5,7 +5,6 @@
 use std::{ffi::CStr, ptr::addr_of};
 
 use qemu_api::{
-    bindings::{module_call_init, module_init_type},
     cell::{self, BqlCell},
     prelude::*,
     qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
@@ -13,6 +12,7 @@
     sysbus::SysBusDevice,
     vmstate::{VMStateDescription, VMStateDescriptionBuilder},
 };
+use util::bindings::{module_call_init, module_init_type};
 
 mod vmstate_tests;
 
diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml
new file mode 100644
index 00000000000..637df61060a
--- /dev/null
+++ b/rust/util/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "util"
+version = "0.1.0"
+description = "Rust bindings for QEMU/util"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+anyhow = { workspace = true }
+foreign = { workspace = true }
+libc = { workspace = true }
+common = { path = "../common" }
+qemu_api_macros = { path = "../qemu-api-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/util/build.rs b/rust/util/build.rs
new file mode 100644
index 00000000000..5654d1d5624
--- /dev/null
+++ b/rust/util/build.rs
@@ -0,0 +1,49 @@
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#[cfg(unix)]
+use std::os::unix::fs::symlink as symlink_file;
+#[cfg(windows)]
+use std::os::windows::fs::symlink_file;
+use std::{env, fs::remove_file, io::Result, path::Path};
+
+fn main() -> Result<()> {
+    let manifest_dir = env!("CARGO_MANIFEST_DIR");
+    let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        let sub = get_rust_subdir(manifest_dir).unwrap();
+        format!("{root}/{sub}/bindings.inc.rs")
+    } else {
+        // Placing bindings.inc.rs in the source directory is supported
+        // but not documented or encouraged.
+        format!("{manifest_dir}/src/bindings.inc.rs")
+    };
+
+    let file = Path::new(&file);
+    if !Path::new(&file).exists() {
+        panic!(concat!(
+            "\n",
+            "    No generated C bindings found! Maybe you wanted one of\n",
+            "    `make clippy`, `make rustfmt`, `make rustdoc`?\n",
+            "\n",
+            "    For other uses of `cargo`, start a subshell with\n",
+            "    `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n",
+            "    the top of the build tree."
+        ));
+    }
+
+    let out_dir = env::var("OUT_DIR").unwrap();
+    let dest_path = format!("{out_dir}/bindings.inc.rs");
+    let dest_path = Path::new(&dest_path);
+    if dest_path.symlink_metadata().is_ok() {
+        remove_file(dest_path)?;
+    }
+    symlink_file(file, dest_path)?;
+
+    println!("cargo:rerun-if-changed=build.rs");
+    Ok(())
+}
+
+fn get_rust_subdir(path: &str) -> Option<&str> {
+    path.find("/rust").map(|index| &path[index + 1..])
+}
diff --git a/rust/util/meson.build b/rust/util/meson.build
new file mode 100644
index 00000000000..56e929349b8
--- /dev/null
+++ b/rust/util/meson.build
@@ -0,0 +1,55 @@
+_util_bindgen_args = []
+c_enums = [
+  'module_init_type',
+  'QEMUClockType',
+]
+foreach enum : c_enums
+  _util_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+#
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_util_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common + _util_bindgen_args,
+)
+
+_util_rs = static_library(
+  'util',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/error.rs',
+      'src/log.rs',
+      'src/module.rs',
+      'src/timer.rs',
+    ],
+    {'.': _util_bindings_inc_rs}
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_api_macros, qom, qemuutil],
+)
+
+util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom])
+
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-util-rs-doctests',
+     _util_rs,
+     protocol: 'rust',
+     dependencies: util_rs,
+     suite: ['doc', 'rust']
+)
diff --git a/rust/util/src/bindings.rs b/rust/util/src/bindings.rs
new file mode 100644
index 00000000000..9ffff12cded
--- /dev/null
+++ b/rust/util/src/bindings.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/qemu-api/src/error.rs b/rust/util/src/error.rs
similarity index 98%
rename from rust/qemu-api/src/error.rs
rename to rust/util/src/error.rs
index 8bac3cbec81..bfa5a8685bc 100644
--- a/rust/qemu-api/src/error.rs
+++ b/rust/util/src/error.rs
@@ -19,7 +19,7 @@
 //!
 //! This module is most commonly used at the boundary between C and Rust code;
 //! other code will usually access it through the
-//! [`qemu_api::Result`](crate::Result) type alias, and will use the
+//! [`utils::Result`](crate::Result) type alias, and will use the
 //! [`std::error::Error`] interface to let C errors participate in Rust's error
 //! handling functionality.
 //!
@@ -30,7 +30,7 @@
 //! type up to C code, or from a combination of the two.
 //!
 //! The third case, corresponding to [`Error::with_error`], is the only one that
-//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly.  Similar
+//! requires mentioning [`utils::Error`](crate::Error) explicitly.  Similar
 //! to how QEMU's C code handles errno values, the string and the
 //! `anyhow::Error` object will be concatenated with `:` as the separator.
 
@@ -320,7 +320,6 @@ mod tests {
     use foreign::OwnedPointer;
 
     use super::*;
-    use crate::bindings;
 
     #[track_caller]
     fn error_for_test(msg: &CStr) -> OwnedPointer<Error> {
diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs
new file mode 100644
index 00000000000..16c89b95174
--- /dev/null
+++ b/rust/util/src/lib.rs
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+pub mod error;
+pub mod log;
+pub mod module;
+pub mod timer;
+
+pub use error::{Error, Result};
diff --git a/rust/qemu-api/src/log.rs b/rust/util/src/log.rs
similarity index 93%
rename from rust/qemu-api/src/log.rs
rename to rust/util/src/log.rs
index d07f6272dc1..af9a3e91234 100644
--- a/rust/qemu-api/src/log.rs
+++ b/rust/util/src/log.rs
@@ -49,7 +49,7 @@ impl LogGuard {
     /// # Examples
     ///
     /// ```
-    /// # use qemu_api::log::LogGuard;
+    /// # use util::log::LogGuard;
     /// # use std::io::Write;
     /// if let Some(mut log) = LogGuard::new() {
     ///     writeln!(log, "test");
@@ -116,7 +116,7 @@ fn drop(&mut self) {
 /// # Example
 ///
 /// ```
-/// use qemu_api::{log::Log, log_mask_ln};
+/// use util::{log::Log, log_mask_ln};
 ///
 /// let error_address = 0xbad;
 /// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range");
@@ -126,7 +126,7 @@ fn drop(&mut self) {
 /// trailing `,`:
 ///
 /// ```
-/// use qemu_api::{log::Log, log_mask_ln};
+/// use util::{log::Log, log_mask_ln};
 ///
 /// let error_address = 0xbad;
 /// log_mask_ln!(
@@ -139,12 +139,12 @@ fn drop(&mut self) {
 macro_rules! log_mask_ln {
     ($mask:expr, $fmt:tt $($args:tt)*) => {{
         // Type assertion to enforce type `Log` for $mask
-        let _: Log = $mask;
+        let _: $crate::log::Log = $mask;
 
         if unsafe {
-            (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0
+            ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0
         } {
-            _ = ::qemu_api::log::LogGuard::log_fmt(
+            _ = $crate::log::LogGuard::log_fmt(
                 format_args!("{}\n", format_args!($fmt $($args)*)));
         }
     }};
diff --git a/rust/qemu-api/src/module.rs b/rust/util/src/module.rs
similarity index 97%
rename from rust/qemu-api/src/module.rs
rename to rust/util/src/module.rs
index fa5cea3598f..06c45fc142b 100644
--- a/rust/qemu-api/src/module.rs
+++ b/rust/util/src/module.rs
@@ -36,7 +36,7 @@ extern "C" fn ctor_fn() {
 
     // shortcut because it's quite common that $body needs unsafe {}
     ($type:ident => unsafe $body:block) => {
-        $crate::module_init! {
+        ::util::module_init! {
             $type => { unsafe { $body } }
         }
     };
diff --git a/rust/qemu-api/src/timer.rs b/rust/util/src/timer.rs
similarity index 100%
rename from rust/qemu-api/src/timer.rs
rename to rust/util/src/timer.rs
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 19/33] rust: split "migration" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (16 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 18/33] rust: split "util" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:09   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 20/33] rust: split "bql" crate Paolo Bonzini
                   ` (15 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-11-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                                 |  1 +
 rust/migration/wrapper.h                    | 51 ++++++++++++++++++++
 rust/qemu-api/wrapper.h                     |  1 -
 rust/Cargo.lock                             | 12 +++++
 rust/Cargo.toml                             |  1 +
 rust/hw/char/pl011/Cargo.toml               |  1 +
 rust/hw/char/pl011/meson.build              |  1 +
 rust/hw/char/pl011/src/device.rs            | 13 ++---
 rust/hw/char/pl011/src/registers.rs         |  2 +-
 rust/hw/timer/hpet/Cargo.toml               |  1 +
 rust/hw/timer/hpet/meson.build              |  1 +
 rust/hw/timer/hpet/src/device.rs            | 11 +++--
 rust/meson.build                            |  1 +
 rust/migration/Cargo.toml                   | 21 ++++++++
 rust/migration/build.rs                     |  1 +
 rust/migration/meson.build                  | 53 +++++++++++++++++++++
 rust/migration/src/bindings.rs              | 48 +++++++++++++++++++
 rust/migration/src/lib.rs                   |  6 +++
 rust/{qemu-api => migration}/src/vmstate.rs | 42 +++++++++-------
 rust/qemu-api/Cargo.toml                    |  1 +
 rust/qemu-api/meson.build                   | 12 ++---
 rust/qemu-api/src/bindings.rs               | 21 +-------
 rust/qemu-api/src/cell.rs                   |  4 +-
 rust/qemu-api/src/lib.rs                    |  1 -
 rust/qemu-api/src/prelude.rs                |  2 -
 rust/qemu-api/src/qdev.rs                   |  3 +-
 rust/qemu-api/src/qom.rs                    |  2 +-
 rust/qemu-api/tests/tests.rs                |  2 +-
 rust/qemu-api/tests/vmstate_tests.rs        |  4 +-
 29 files changed, 252 insertions(+), 68 deletions(-)
 create mode 100644 rust/migration/wrapper.h
 create mode 100644 rust/migration/Cargo.toml
 create mode 120000 rust/migration/build.rs
 create mode 100644 rust/migration/meson.build
 create mode 100644 rust/migration/src/bindings.rs
 create mode 100644 rust/migration/src/lib.rs
 rename rust/{qemu-api => migration}/src/vmstate.rs (95%)

diff --git a/MAINTAINERS b/MAINTAINERS
index b8d38b08549..447b01a4ffa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3514,6 +3514,7 @@ Rust
 M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 S: Maintained
 F: rust/common/
+F: rust/migration/
 F: rust/qemu-api
 F: rust/qemu-api-macros
 F: rust/rustfmt.toml
diff --git a/rust/migration/wrapper.h b/rust/migration/wrapper.h
new file mode 100644
index 00000000000..daf316aed41
--- /dev/null
+++ b/rust/migration/wrapper.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2024 Linaro Ltd.
+ *
+ * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+#include "migration/vmstate.h"
diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h
index cc7112406b2..b99df9f568a 100644
--- a/rust/qemu-api/wrapper.h
+++ b/rust/qemu-api/wrapper.h
@@ -58,7 +58,6 @@ typedef enum memory_order {
 #include "hw/qdev-properties.h"
 #include "hw/qdev-properties-system.h"
 #include "hw/irq.h"
-#include "migration/vmstate.h"
 #include "chardev/char-serial.h"
 #include "exec/memattrs.h"
 #include "system/address-spaces.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 757c03cbdec..048dd74757f 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -71,6 +71,7 @@ name = "hpet"
 version = "0.1.0"
 dependencies = [
  "common",
+ "migration",
  "qemu_api",
  "qemu_api_macros",
  "util",
@@ -91,6 +92,15 @@ version = "0.2.162"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
 
+[[package]]
+name = "migration"
+version = "0.1.0"
+dependencies = [
+ "common",
+ "qemu_api_macros",
+ "util",
+]
+
 [[package]]
 name = "pl011"
 version = "0.1.0"
@@ -99,6 +109,7 @@ dependencies = [
  "bilge-impl",
  "bits",
  "common",
+ "migration",
  "qemu_api",
  "qemu_api_macros",
  "util",
@@ -141,6 +152,7 @@ name = "qemu_api"
 version = "0.1.0"
 dependencies = [
  "common",
+ "migration",
  "qemu_api_macros",
  "util",
 ]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index cfdd535e3bd..e0958ef28a8 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -3,6 +3,7 @@ resolver = "2"
 members = [
     "bits",
     "common",
+    "migration",
     "qemu-api-macros",
     "qemu-api",
     "hw/char/pl011",
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 0cf9943fe86..7fd7531823d 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -18,6 +18,7 @@ bilge-impl = { version = "0.2.0" }
 bits = { path = "../../../bits" }
 common = { path = "../../../common" }
 util = { path = "../../../util" }
+migration = { path = "../../../migration" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 8a931a4d03d..2198fcee9bc 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -10,6 +10,7 @@ _libpl011_rs = static_library(
     common_rs,
     qemu_api_rs,
     util_rs,
+    migration_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index ab38d57fc41..225be34e08b 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -5,17 +5,18 @@
 use std::{ffi::CStr, mem::size_of};
 
 use common::{static_assert, uninit_field_mut};
+use migration::{
+    self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
+    vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder,
+};
 use qemu_api::{
     chardev::{CharBackend, Chardev, Event},
-    impl_vmstate_forward, impl_vmstate_struct,
     irq::{IRQState, InterruptSource},
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
     qom::{ObjectImpl, Owned, ParentField, ParentInit},
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
-    vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused,
 };
 use util::{log::Log, log_mask_ln};
 
@@ -458,10 +459,10 @@ pub fn fifo_rx_put(&mut self, value: registers::Data) -> bool {
         false
     }
 
-    pub fn post_load(&mut self) -> Result<(), vmstate::InvalidError> {
+    pub fn post_load(&mut self) -> Result<(), migration::InvalidError> {
         /* Sanity-check input state */
         if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
-            return Err(vmstate::InvalidError);
+            return Err(migration::InvalidError);
         }
 
         if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
@@ -640,7 +641,7 @@ fn update(&self) {
         }
     }
 
-    pub fn post_load(&self, _version_id: u8) -> Result<(), vmstate::InvalidError> {
+    pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError> {
         self.regs.borrow_mut().post_load()
     }
 }
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index 7ececd39f86..2bfbd81095e 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -10,7 +10,7 @@
 
 use bilge::prelude::*;
 use bits::bits;
-use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward};
+use migration::{impl_vmstate_bitsized, impl_vmstate_forward};
 
 /// Offset of each register from the base memory address of the device.
 #[doc(alias = "offset")]
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index dd9a5ed3d43..70acdf03d6e 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -13,6 +13,7 @@ rust-version.workspace = true
 [dependencies]
 common = { path = "../../../common" }
 util = { path = "../../../util" }
+migration = { path = "../../../migration" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index ca09660bf47..8cd70091e63 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -7,6 +7,7 @@ _libhpet_rs = static_library(
     common_rs,
     qemu_api_rs,
     util_rs,
+    migration_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 2be180fdedd..1c2253466d9 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -11,13 +11,16 @@
 };
 
 use common::{bitops::IntegerExt, uninit_field_mut};
+use migration::{
+    self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
+    VMStateDescription, VMStateDescriptionBuilder,
+};
 use qemu_api::{
     bindings::{
         address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
         qdev_prop_uint32, qdev_prop_usize,
     },
     cell::{BqlCell, BqlRefCell},
-    impl_vmstate_struct,
     irq::InterruptSource,
     memory::{
         hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
@@ -27,8 +30,6 @@
     qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
     qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
-    vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
 };
 use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
@@ -845,7 +846,7 @@ fn write(&self, addr: hwaddr, value: u64, size: u32) {
         }
     }
 
-    fn pre_save(&self) -> Result<(), vmstate::Infallible> {
+    fn pre_save(&self) -> Result<(), migration::Infallible> {
         if self.is_hpet_enabled() {
             self.counter.set(self.get_ticks());
         }
@@ -859,7 +860,7 @@ fn pre_save(&self) -> Result<(), vmstate::Infallible> {
         Ok(())
     }
 
-    fn post_load(&self, _version_id: u8) -> Result<(), vmstate::Infallible> {
+    fn post_load(&self, _version_id: u8) -> Result<(), migration::Infallible> {
         for timer in self.timers.iter().take(self.num_timers) {
             let mut t = timer.borrow_mut();
 
diff --git a/rust/meson.build b/rust/meson.build
index a9d715e6e94..826949b2e60 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -26,6 +26,7 @@ subdir('common')
 subdir('qemu-api-macros')
 subdir('bits')
 subdir('util')
+subdir('migration')
 subdir('qemu-api')
 
 subdir('hw')
diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml
new file mode 100644
index 00000000000..98e6df2109d
--- /dev/null
+++ b/rust/migration/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "migration"
+version = "0.1.0"
+description = "Rust bindings for QEMU/migration"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+util = { path = "../util" }
+qemu_api_macros = { path = "../qemu-api-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/migration/build.rs b/rust/migration/build.rs
new file mode 120000
index 00000000000..71a3167885c
--- /dev/null
+++ b/rust/migration/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/migration/meson.build b/rust/migration/meson.build
new file mode 100644
index 00000000000..5e820d43f50
--- /dev/null
+++ b/rust/migration/meson.build
@@ -0,0 +1,53 @@
+_migration_bindgen_args = []
+c_bitfields = [
+  'MigrationPolicy',
+  'MigrationPriority',
+  'VMStateFlags',
+]
+foreach enum : c_bitfields
+  _migration_bindgen_args += ['--bitfield-enum', enum]
+endforeach
+#
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_migration_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common + _migration_bindgen_args,
+  )
+
+_migration_rs = static_library(
+  'migration',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/vmstate.rs',
+    ],
+    {'.' : _migration_bindings_inc_rs},
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  link_with: [_util_rs],
+  dependencies: [common_rs],
+)
+
+migration_rs = declare_dependency(link_with: [_migration_rs],
+  dependencies: [migration, qemuutil])
+
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-migration-rs-doctests',
+     _migration_rs,
+     protocol: 'rust',
+     dependencies: migration_rs,
+     suite: ['doc', 'rust'])
diff --git a/rust/migration/src/bindings.rs b/rust/migration/src/bindings.rs
new file mode 100644
index 00000000000..8ce13a9000e
--- /dev/null
+++ b/rust/migration/src/bindings.rs
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+use common::Zeroable;
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+unsafe impl Send for VMStateDescription {}
+unsafe impl Sync for VMStateDescription {}
+
+unsafe impl Send for VMStateField {}
+unsafe impl Sync for VMStateField {}
+
+unsafe impl Send for VMStateInfo {}
+unsafe impl Sync for VMStateInfo {}
+
+// bindgen does not derive Default here
+#[allow(clippy::derivable_impls)]
+impl Default for VMStateFlags {
+    fn default() -> Self {
+        Self(0)
+    }
+}
+
+unsafe impl Zeroable for VMStateFlags {}
+unsafe impl Zeroable for VMStateField {}
+unsafe impl Zeroable for VMStateDescription {}
diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs
new file mode 100644
index 00000000000..5f51dde4406
--- /dev/null
+++ b/rust/migration/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+pub mod vmstate;
+pub use vmstate::*;
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/migration/src/vmstate.rs
similarity index 95%
rename from rust/qemu-api/src/vmstate.rs
rename to rust/migration/src/vmstate.rs
index 37e47cc4c6f..537d54e4368 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -52,7 +52,7 @@
 /// # Examples
 ///
 /// ```
-/// # use qemu_api::call_func_with_field;
+/// # use migration::call_func_with_field;
 /// # use core::marker::PhantomData;
 /// const fn size_of_field<T>(_: PhantomData<T>) -> usize {
 ///     std::mem::size_of::<T>()
@@ -125,17 +125,19 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 /// * scalar types (integer and `bool`)
 /// * the C struct `QEMUTimer`
 /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`,
-///   [`BqlCell`](crate::cell::BqlCell),
-///   [`BqlRefCell`](crate::cell::BqlRefCell)),
+///   [`BqlCell`], [`BqlRefCell`])
 /// * a raw pointer to any of the above
-/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of
-///   the above
+/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above
 /// * an array of any of the above
 ///
 /// In order to support other types, the trait `VMState` must be implemented
 /// for them.  The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward),
 /// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
 /// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this.
+///
+/// [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html
+/// [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html
+/// [`Owned`]: ../../qemu_api/qom/struct.Owned.html
 #[macro_export]
 macro_rules! vmstate_of {
     ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
@@ -161,7 +163,11 @@ macro_rules! vmstate_of {
     };
 }
 
-impl VMStateFlags {
+pub trait VMStateFlagsExt {
+    const VMS_VARRAY_FLAGS: VMStateFlags;
+}
+
+impl VMStateFlagsExt for VMStateFlags {
     const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags(
         VMStateFlags::VMS_VARRAY_INT32.0
             | VMStateFlags::VMS_VARRAY_UINT8.0
@@ -207,7 +213,7 @@ pub const fn with_pointer_flag(mut self) -> Self {
     }
 
     #[must_use]
-    pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField {
+    pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self {
         self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0);
         self.flags = VMStateFlags(self.flags.0 | flag.0);
         self.num = 0; // varray uses num_offset instead of num.
@@ -216,13 +222,13 @@ pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMState
 
     #[must_use]
     #[allow(unused_mut)]
-    pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField {
+    pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self {
         assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0);
         self.with_varray_flag_unchecked(flag)
     }
 
     #[must_use]
-    pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField {
+    pub const fn with_varray_multiply(mut self, num: u32) -> Self {
         assert!(num <= 0x7FFF_FFFFu32);
         self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0);
         self.num = num as i32;
@@ -237,7 +243,7 @@ pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField {
 /// # Examples
 ///
 /// ```
-/// # use qemu_api::impl_vmstate_forward;
+/// # use migration::impl_vmstate_forward;
 /// pub struct Fifo([u8; 16]);
 /// impl_vmstate_forward!(Fifo);
 /// ```
@@ -272,7 +278,7 @@ unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmsta
 impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
 impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
 impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
-impl_vmstate_transparent!(::common::Opaque<T> where T: VMState);
+impl_vmstate_transparent!(common::Opaque<T> where T: VMState);
 
 #[macro_export]
 macro_rules! impl_vmstate_bitsized {
@@ -294,12 +300,12 @@ unsafe impl $crate::vmstate::VMState for $type {
 
 macro_rules! impl_vmstate_scalar {
     ($info:ident, $type:ty$(, $varray_flag:ident)?) => {
-        unsafe impl VMState for $type {
-            const BASE: VMStateField = VMStateField {
+        unsafe impl $crate::vmstate::VMState for $type {
+            const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField {
                 info: addr_of!(bindings::$info),
                 size: mem::size_of::<$type>(),
-                flags: VMStateFlags::VMS_SINGLE,
-                ..Zeroable::ZERO
+                flags: $crate::vmstate::VMStateFlags::VMS_SINGLE,
+                ..::common::zeroable::Zeroable::ZERO
             };
             $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)?
         }
@@ -320,12 +326,12 @@ unsafe impl VMState for $type {
 #[macro_export]
 macro_rules! impl_vmstate_c_struct {
     ($type:ty, $vmsd:expr) => {
-        unsafe impl VMState for $type {
+        unsafe impl $crate::vmstate::VMState for $type {
             const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField {
                 vmsd: ::std::ptr::addr_of!($vmsd),
                 size: ::std::mem::size_of::<$type>(),
                 flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
-                ..common::zeroable::Zeroable::ZERO
+                ..::common::zeroable::Zeroable::ZERO
             };
         }
     };
@@ -391,7 +397,7 @@ pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), b
 #[macro_export]
 macro_rules! vmstate_exist_fn {
     ($struct_name:ty, $test_fn:expr) => {{
-        const fn test_cb_builder__<T, F: for<'a> ::common::callbacks::FnCall<(&'a T, u8), bool>>(
+        const fn test_cb_builder__<T, F: for<'a> ::common::FnCall<(&'a T, u8), bool>>(
             _phantom: ::core::marker::PhantomData<F>,
         ) -> $crate::vmstate::VMSFieldExistCb {
             const { assert!(F::IS_SOME) };
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index fbfb894421d..7276e67aa97 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -15,6 +15,7 @@ rust-version.workspace = true
 
 [dependencies]
 common = { path = "../common" }
+migration = { path = "../migration" }
 util = { path = "../util" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 7734f656a26..a6b5772d194 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -11,8 +11,6 @@ c_enums = [
   'GpioPolarity',
   'MachineInitPhase',
   'MemoryDeviceInfoKind',
-  'MigrationPolicy',
-  'MigrationPriority',
   'QEMUChrEvent',
   'ResetType',
   'device_endian',
@@ -23,12 +21,13 @@ foreach enum : c_enums
 endforeach
 c_bitfields = [
   'ClockEvent',
-  'VMStateFlags',
 ]
 foreach enum : c_bitfields
   _qemu_api_bindgen_args += ['--bitfield-enum', enum]
 endforeach
 
+_qemu_api_bindgen_args += ['--blocklist-type', 'VMStateDescription']
+
 _qemu_api_bindgen_args += ['--blocklist-type', 'Error']
 # TODO: Remove this comment when the clang/libclang mismatch issue is solved.
 #
@@ -60,15 +59,14 @@ _qemu_api_rs = static_library(
       'src/qdev.rs',
       'src/qom.rs',
       'src/sysbus.rs',
-      'src/vmstate.rs',
     ],
     {'.' : _qemu_api_bindings_inc_rs},
   ),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, util_rs,
-                 qom, hwcore, chardev, migration],
+  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
+                 util_rs, qom, hwcore, chardev],
 )
 
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
@@ -90,7 +88,7 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [common_rs, util_rs, qemu_api_rs]),
+        dependencies: [common_rs, util_rs, migration_rs, qemu_api_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index aedf42b652c..ce00a6e0e4c 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -21,6 +21,7 @@
 //! `bindgen`-generated declarations.
 
 use common::Zeroable;
+use migration::bindings::VMStateDescription;
 use util::bindings::Error;
 
 #[cfg(MESON)]
@@ -51,28 +52,8 @@ unsafe impl Sync for Property {}
 unsafe impl Send for TypeInfo {}
 unsafe impl Sync for TypeInfo {}
 
-unsafe impl Send for VMStateDescription {}
-unsafe impl Sync for VMStateDescription {}
-
-unsafe impl Send for VMStateField {}
-unsafe impl Sync for VMStateField {}
-
-unsafe impl Send for VMStateInfo {}
-unsafe impl Sync for VMStateInfo {}
-
-// bindgen does not derive Default here
-#[allow(clippy::derivable_impls)]
-impl Default for crate::bindings::VMStateFlags {
-    fn default() -> Self {
-        Self(0)
-    }
-}
-
 unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
 unsafe impl Zeroable for crate::bindings::Property {}
-unsafe impl Zeroable for crate::bindings::VMStateFlags {}
-unsafe impl Zeroable for crate::bindings::VMStateField {}
-unsafe impl Zeroable for crate::bindings::VMStateDescription {}
 unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {}
 unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {}
 unsafe impl Zeroable for crate::bindings::MemoryRegionOps {}
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index d13848df200..b80a0fd80b7 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -152,7 +152,9 @@
     ptr::NonNull,
 };
 
-use crate::{bindings, impl_vmstate_transparent};
+use migration::impl_vmstate_transparent;
+
+use crate::bindings;
 
 /// An internal function that is used by doctests.
 pub fn bql_start_test() {
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 54b252fb2c5..55386f66978 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -20,7 +20,6 @@
 pub mod qdev;
 pub mod qom;
 pub mod sysbus;
-pub mod vmstate;
 
 // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
 // crate).
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 3d771481e40..c10c1711581 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -21,5 +21,3 @@
 pub use crate::qom_isa;
 
 pub use crate::sysbus::SysBusDeviceMethods;
-
-pub use crate::vmstate::VMState;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index d87479ce13b..c81ae7cf45c 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -11,17 +11,16 @@
 
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use common::{callbacks::FnCall, Opaque};
+use migration::{impl_vmstate_c_struct, VMStateDescription};
 use util::{Error, Result};
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
     cell::bql_locked,
     chardev::Chardev,
-    impl_vmstate_c_struct,
     irq::InterruptSource,
     prelude::*,
     qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
-    vmstate::VMStateDescription,
 };
 
 /// A safe wrapper around [`bindings::Clock`].
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 49b4f03ccfc..7f2f7797e44 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -103,6 +103,7 @@
 
 pub use bindings::ObjectClass;
 use common::Opaque;
+use migration::impl_vmstate_pointer;
 
 use crate::{
     bindings::{
@@ -110,7 +111,6 @@
         object_get_typename, object_new, object_ref, object_unref, TypeInfo,
     },
     cell::bql_locked,
-    impl_vmstate_pointer,
 };
 
 /// A safe wrapper around [`bindings::Object`].
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 70ef4a80d57..92e3534d3c1 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -4,13 +4,13 @@
 
 use std::{ffi::CStr, ptr::addr_of};
 
+use migration::{VMStateDescription, VMStateDescriptionBuilder};
 use qemu_api::{
     cell::{self, BqlCell},
     prelude::*,
     qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
     qom::{ObjectImpl, ParentField},
     sysbus::SysBusDevice,
-    vmstate::{VMStateDescription, VMStateDescriptionBuilder},
 };
 use util::bindings::{module_call_init, module_init_type};
 
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs
index d9e5bcc4987..47fc15149b4 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -10,16 +10,16 @@
 };
 
 use common::Opaque;
-use qemu_api::{
+use migration::{
     bindings::{
         vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
         vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
     },
-    cell::BqlCell,
     impl_vmstate_forward, impl_vmstate_struct,
     vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField},
     vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate,
 };
+use qemu_api::cell::BqlCell;
 
 const FOO_ARRAY_MAX: usize = 3;
 
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 20/33] rust: split "bql" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (17 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 19/33] rust: split "migration" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:31   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 21/33] rust: split "qom" crate Paolo Bonzini
                   ` (14 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Unfortunately, an example had to be compile-time disabled, since it
relies on higher level crates (qdev, irq etc). The alternative is
probably to move that code to an example in qemu-api or elsewere and
make a link to it, or include_str.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-12-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                          |  1 +
 rust/bql/wrapper.h                   | 27 ++++++++
 rust/Cargo.lock                      | 10 +++
 rust/Cargo.toml                      |  1 +
 rust/bql/Cargo.toml                  | 23 +++++++
 rust/bql/build.rs                    |  1 +
 rust/bql/meson.build                 | 52 ++++++++++++++++
 rust/bql/src/bindings.rs             | 25 ++++++++
 rust/{qemu-api => bql}/src/cell.rs   | 92 ++++++++++------------------
 rust/bql/src/lib.rs                  | 29 +++++++++
 rust/common/src/opaque.rs            |  4 +-
 rust/hw/char/pl011/Cargo.toml        |  1 +
 rust/hw/char/pl011/meson.build       |  1 +
 rust/hw/char/pl011/src/device.rs     |  1 +
 rust/hw/timer/hpet/Cargo.toml        |  1 +
 rust/hw/timer/hpet/meson.build       |  1 +
 rust/hw/timer/hpet/src/device.rs     |  2 +-
 rust/hw/timer/hpet/src/fw_cfg.rs     |  5 +-
 rust/meson.build                     |  1 +
 rust/migration/src/vmstate.rs        |  4 +-
 rust/qemu-api/Cargo.toml             |  5 +-
 rust/qemu-api/meson.build            | 18 +-----
 rust/qemu-api/src/chardev.rs         | 17 +++--
 rust/qemu-api/src/irq.rs             |  1 +
 rust/qemu-api/src/lib.rs             |  1 -
 rust/qemu-api/src/prelude.rs         |  3 -
 rust/qemu-api/src/qdev.rs            |  5 +-
 rust/qemu-api/src/qom.rs             | 13 ++--
 rust/qemu-api/src/sysbus.rs          | 13 ++--
 rust/qemu-api/tests/tests.rs         |  4 +-
 rust/qemu-api/tests/vmstate_tests.rs |  2 +-
 31 files changed, 244 insertions(+), 120 deletions(-)
 create mode 100644 rust/bql/wrapper.h
 create mode 100644 rust/bql/Cargo.toml
 create mode 120000 rust/bql/build.rs
 create mode 100644 rust/bql/meson.build
 create mode 100644 rust/bql/src/bindings.rs
 rename rust/{qemu-api => bql}/src/cell.rs (92%)
 create mode 100644 rust/bql/src/lib.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 447b01a4ffa..2e87f18e2bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3513,6 +3513,7 @@ F: include/hw/registerfields.h
 Rust
 M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 S: Maintained
+F: rust/bql/
 F: rust/common/
 F: rust/migration/
 F: rust/qemu-api
diff --git a/rust/bql/wrapper.h b/rust/bql/wrapper.h
new file mode 100644
index 00000000000..2ef9a96e1d3
--- /dev/null
+++ b/rust/bql/wrapper.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "qemu/main-loop.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 048dd74757f..73ca9582a56 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -44,6 +44,13 @@ dependencies = [
  "qemu_api_macros",
 ]
 
+[[package]]
+name = "bql"
+version = "0.1.0"
+dependencies = [
+ "migration",
+]
+
 [[package]]
 name = "common"
 version = "0.1.0"
@@ -70,6 +77,7 @@ dependencies = [
 name = "hpet"
 version = "0.1.0"
 dependencies = [
+ "bql",
  "common",
  "migration",
  "qemu_api",
@@ -108,6 +116,7 @@ dependencies = [
  "bilge",
  "bilge-impl",
  "bits",
+ "bql",
  "common",
  "migration",
  "qemu_api",
@@ -151,6 +160,7 @@ dependencies = [
 name = "qemu_api"
 version = "0.1.0"
 dependencies = [
+ "bql",
  "common",
  "migration",
  "qemu_api_macros",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index e0958ef28a8..8be90da8ff7 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -2,6 +2,7 @@
 resolver = "2"
 members = [
     "bits",
+    "bql",
     "common",
     "migration",
     "qemu-api-macros",
diff --git a/rust/bql/Cargo.toml b/rust/bql/Cargo.toml
new file mode 100644
index 00000000000..1041bd4ea93
--- /dev/null
+++ b/rust/bql/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "bql"
+version = "0.1.0"
+description = "Rust bindings for QEMU/BQL"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+migration = { path = "../migration" }
+
+[features]
+default = ["debug_cell"]
+debug_cell = []
+
+[lints]
+workspace = true
diff --git a/rust/bql/build.rs b/rust/bql/build.rs
new file mode 120000
index 00000000000..71a3167885c
--- /dev/null
+++ b/rust/bql/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/bql/meson.build b/rust/bql/meson.build
new file mode 100644
index 00000000000..f369209dfdd
--- /dev/null
+++ b/rust/bql/meson.build
@@ -0,0 +1,52 @@
+_bql_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+if get_option('debug_mutex')
+  _bql_cfg += ['--cfg', 'feature="debug_cell"']
+endif
+
+#
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_bql_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common,
+)
+
+_bql_rs = static_library(
+  'bql',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/cell.rs',
+    ],
+    {'.': _bql_bindings_inc_rs}
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  rust_args: _bql_cfg,
+  link_with: [_migration_rs],
+)
+
+bql_rs = declare_dependency(link_with: [_bql_rs],
+  dependencies: [qemuutil])
+
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-bql-rs-doctests',
+     _bql_rs,
+     protocol: 'rust',
+     dependencies: bql_rs,
+     suite: ['doc', 'rust'])
diff --git a/rust/bql/src/bindings.rs b/rust/bql/src/bindings.rs
new file mode 100644
index 00000000000..9ffff12cded
--- /dev/null
+++ b/rust/bql/src/bindings.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/qemu-api/src/cell.rs b/rust/bql/src/cell.rs
similarity index 92%
rename from rust/qemu-api/src/cell.rs
rename to rust/bql/src/cell.rs
index b80a0fd80b7..25007427edc 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/bql/src/cell.rs
@@ -75,9 +75,10 @@
 //!
 //! ### Example
 //!
-//! ```
+//! ```ignore
+//! # use bql::BqlRefCell;
 //! # use qemu_api::prelude::*;
-//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState};
+//! # use qemu_api::{irq::InterruptSource, irq::IRQState};
 //! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
 //! # const N_GPIOS: usize = 8;
 //! # struct PL061Registers { /* ... */ }
@@ -141,7 +142,6 @@
 //! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
 //! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut).  The
 //! thread will panic if these rules are violated or if the BQL is not held.
-
 use std::{
     cell::{Cell, UnsafeCell},
     cmp::Ordering,
@@ -154,30 +154,6 @@
 
 use migration::impl_vmstate_transparent;
 
-use crate::bindings;
-
-/// An internal function that is used by doctests.
-pub fn bql_start_test() {
-    // SAFETY: integration tests are run with --test-threads=1, while
-    // unit tests and doctests are not multithreaded and do not have
-    // any BQL-protected data.  Just set bql_locked to true.
-    unsafe {
-        bindings::rust_bql_mock_lock();
-    }
-}
-
-pub fn bql_locked() -> bool {
-    // SAFETY: the function does nothing but return a thread-local bool
-    unsafe { bindings::bql_locked() }
-}
-
-fn bql_block_unlock(increase: bool) {
-    // SAFETY: this only adjusts a counter
-    unsafe {
-        bindings::bql_block_unlock(increase);
-    }
-}
-
 /// A mutable memory location that is protected by the Big QEMU Lock.
 ///
 /// # Memory layout
@@ -256,8 +232,8 @@ impl<T> BqlCell<T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlCell::new(5);
     /// ```
@@ -273,8 +249,8 @@ pub const fn new(value: T) -> BqlCell<T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlCell::new(5);
     ///
@@ -291,8 +267,8 @@ pub fn set(&self, val: T) {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlCell;
+    /// # bql::start_test();
     ///
     /// let cell = BqlCell::new(5);
     /// assert_eq!(cell.get(), 5);
@@ -301,7 +277,7 @@ pub fn set(&self, val: T) {
     /// ```
     #[inline]
     pub fn replace(&self, val: T) -> T {
-        assert!(bql_locked());
+        assert!(crate::is_locked());
         // SAFETY: This can cause data races if called from multiple threads,
         // but it won't happen as long as C code accesses the value
         // under BQL protection only.
@@ -313,8 +289,8 @@ pub fn replace(&self, val: T) -> T {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlCell::new(5);
     /// let five = c.into_inner();
@@ -322,7 +298,7 @@ pub fn replace(&self, val: T) -> T {
     /// assert_eq!(five, 5);
     /// ```
     pub fn into_inner(self) -> T {
-        assert!(bql_locked());
+        assert!(crate::is_locked());
         self.value.into_inner()
     }
 }
@@ -333,8 +309,8 @@ impl<T: Copy> BqlCell<T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlCell::new(5);
     ///
@@ -342,7 +318,7 @@ impl<T: Copy> BqlCell<T> {
     /// ```
     #[inline]
     pub fn get(&self) -> T {
-        assert!(bql_locked());
+        assert!(crate::is_locked());
         // SAFETY: This can cause data races if called from multiple threads,
         // but it won't happen as long as C code accesses the value
         // under BQL protection only.
@@ -356,8 +332,8 @@ impl<T> BqlCell<T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlCell::new(5);
     ///
@@ -375,8 +351,8 @@ impl<T: Default> BqlCell<T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlCell::new(5);
     /// let five = c.take();
@@ -447,7 +423,7 @@ impl<T> BqlRefCell<T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlRefCell;
+    /// use bql::BqlRefCell;
     ///
     /// let c = BqlRefCell::new(5);
     /// ```
@@ -506,8 +482,8 @@ fn panic_already_borrowed(&self) -> ! {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlRefCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlRefCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlRefCell::new(5);
     ///
@@ -518,8 +494,8 @@ fn panic_already_borrowed(&self) -> ! {
     /// An example of panic:
     ///
     /// ```should_panic
-    /// use qemu_api::cell::BqlRefCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlRefCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlRefCell::new(5);
     ///
@@ -536,7 +512,7 @@ pub fn borrow(&self) -> BqlRef<'_, T> {
                 self.borrowed_at.set(Some(std::panic::Location::caller()));
             }
 
-            bql_block_unlock(true);
+            crate::block_unlock(true);
 
             // SAFETY: `BorrowRef` ensures that there is only immutable access
             // to the value while borrowed.
@@ -560,8 +536,8 @@ pub fn borrow(&self) -> BqlRef<'_, T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlRefCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlRefCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlRefCell::new("hello".to_owned());
     ///
@@ -573,8 +549,8 @@ pub fn borrow(&self) -> BqlRef<'_, T> {
     /// An example of panic:
     ///
     /// ```should_panic
-    /// use qemu_api::cell::BqlRefCell;
-    /// # qemu_api::cell::bql_start_test();
+    /// use bql::BqlRefCell;
+    /// # bql::start_test();
     ///
     /// let c = BqlRefCell::new(5);
     /// let m = c.borrow();
@@ -591,7 +567,7 @@ pub fn borrow_mut(&self) -> BqlRefMut<'_, T> {
             }
 
             // SAFETY: this only adjusts a counter
-            bql_block_unlock(true);
+            crate::block_unlock(true);
 
             // SAFETY: `BorrowRefMut` guarantees unique access.
             let value = unsafe { NonNull::new_unchecked(self.value.get()) };
@@ -610,7 +586,7 @@ pub fn borrow_mut(&self) -> BqlRefMut<'_, T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlRefCell;
+    /// use bql::BqlRefCell;
     ///
     /// let c = BqlRefCell::new(5);
     ///
@@ -737,7 +713,7 @@ fn drop(&mut self) {
         let borrow = self.borrow.get();
         debug_assert!(is_reading(borrow));
         self.borrow.set(borrow - 1);
-        bql_block_unlock(false)
+        crate::block_unlock(false)
     }
 }
 
@@ -827,7 +803,7 @@ fn drop(&mut self) {
         let borrow = self.borrow.get();
         debug_assert!(is_writing(borrow));
         self.borrow.set(borrow + 1);
-        bql_block_unlock(false)
+        crate::block_unlock(false)
     }
 }
 
diff --git a/rust/bql/src/lib.rs b/rust/bql/src/lib.rs
new file mode 100644
index 00000000000..ef08221e9c1
--- /dev/null
+++ b/rust/bql/src/lib.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+mod bindings;
+use bindings::{bql_block_unlock, bql_locked, rust_bql_mock_lock};
+
+mod cell;
+pub use cell::*;
+
+/// An internal function that is used by doctests.
+pub fn start_test() {
+    // SAFETY: integration tests are run with --test-threads=1, while
+    // unit tests and doctests are not multithreaded and do not have
+    // any BQL-protected data.  Just set bql_locked to true.
+    unsafe {
+        rust_bql_mock_lock();
+    }
+}
+
+pub fn is_locked() -> bool {
+    // SAFETY: the function does nothing but return a thread-local bool
+    unsafe { bql_locked() }
+}
+
+pub fn block_unlock(increase: bool) {
+    // SAFETY: this only adjusts a counter
+    unsafe {
+        bql_block_unlock(increase);
+    }
+}
diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs
index d25a5f3ae1a..97ed3e84522 100644
--- a/rust/common/src/opaque.rs
+++ b/rust/common/src/opaque.rs
@@ -69,8 +69,8 @@
 //! and only at FFI boundaries. For QEMU-specific types that need interior
 //! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
 //!
-//! [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html
-//! [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html
+//! [`BqlCell`]: ../../bql/cell/struct.BqlCell.html
+//! [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html
 use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull};
 
 /// Stores an opaque value that is shared with C code.
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 7fd7531823d..1a1d4ba7152 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -18,6 +18,7 @@ bilge-impl = { version = "0.2.0" }
 bits = { path = "../../../bits" }
 common = { path = "../../../common" }
 util = { path = "../../../util" }
+bql = { path = "../../../bql" }
 migration = { path = "../../../migration" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 2198fcee9bc..8561c4c14ae 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -11,6 +11,7 @@ _libpl011_rs = static_library(
     qemu_api_rs,
     util_rs,
     migration_rs,
+    bql_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 225be34e08b..00ae4328250 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -4,6 +4,7 @@
 
 use std::{ffi::CStr, mem::size_of};
 
+use bql::BqlRefCell;
 use common::{static_assert, uninit_field_mut};
 use migration::{
     self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 70acdf03d6e..9fcec38bfa6 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -14,6 +14,7 @@ rust-version.workspace = true
 common = { path = "../../../common" }
 util = { path = "../../../util" }
 migration = { path = "../../../migration" }
+bql = { path = "../../../bql" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index 8cd70091e63..43a62db0d01 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -8,6 +8,7 @@ _libhpet_rs = static_library(
     qemu_api_rs,
     util_rs,
     migration_rs,
+    bql_rs,
     qemu_api_macros,
   ],
 )
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 1c2253466d9..9658e071c26 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -10,6 +10,7 @@
     slice::from_ref,
 };
 
+use bql::{BqlCell, BqlRefCell};
 use common::{bitops::IntegerExt, uninit_field_mut};
 use migration::{
     self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
@@ -20,7 +21,6 @@
         address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
         qdev_prop_uint32, qdev_prop_usize,
     },
-    cell::{BqlCell, BqlRefCell},
     irq::InterruptSource,
     memory::{
         hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs
index 0605225fbb1..e569b57b93b 100644
--- a/rust/hw/timer/hpet/src/fw_cfg.rs
+++ b/rust/hw/timer/hpet/src/fw_cfg.rs
@@ -5,7 +5,6 @@
 use std::ptr::addr_of_mut;
 
 use common::Zeroable;
-use qemu_api::cell::bql_locked;
 
 /// Each `HPETState` represents a Event Timer Block. The v1 spec supports
 /// up to 8 blocks. QEMU only uses 1 block (in PC machine).
@@ -38,7 +37,7 @@ unsafe impl Zeroable for HPETFwConfig {}
 
 impl HPETFwConfig {
     pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         // SAFETY: all accesses go through these methods, which guarantee
         // that the accesses are protected by the BQL.
         let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
@@ -58,7 +57,7 @@ pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
     }
 
     pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         // SAFETY: all accesses go through these methods, which guarantee
         // that the accesses are protected by the BQL.
         let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
diff --git a/rust/meson.build b/rust/meson.build
index 826949b2e60..2ba1ea2280d 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -27,6 +27,7 @@ subdir('qemu-api-macros')
 subdir('bits')
 subdir('util')
 subdir('migration')
+subdir('bql')
 subdir('qemu-api')
 
 subdir('hw')
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 537d54e4368..d714aacb7e3 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -135,8 +135,8 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 /// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
 /// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this.
 ///
-/// [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html
-/// [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html
+/// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html
+/// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html
 /// [`Owned`]: ../../qemu_api/qom/struct.Owned.html
 #[macro_export]
 macro_rules! vmstate_of {
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 7276e67aa97..6e9427f80c8 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -17,11 +17,8 @@ rust-version.workspace = true
 common = { path = "../common" }
 migration = { path = "../migration" }
 util = { path = "../util" }
+bql = { path = "../bql" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 
-[features]
-default = ["debug_cell"]
-debug_cell = []
-
 [lints]
 workspace = true
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index a6b5772d194..a47ee6c1a3d 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -2,10 +2,6 @@ _qemu_api_cfg = run_command(rustc_args,
   '--config-headers', config_host_h, '--features', files('Cargo.toml'),
   capture: true, check: true).stdout().strip().splitlines()
 
-if get_option('debug_mutex')
-  _qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
-endif
-
 c_enums = [
   'DeviceCategory',
   'GpioPolarity',
@@ -51,7 +47,6 @@ _qemu_api_rs = static_library(
     [
       'src/lib.rs',
       'src/bindings.rs',
-      'src/cell.rs',
       'src/chardev.rs',
       'src/irq.rs',
       'src/memory.rs',
@@ -65,22 +60,13 @@ _qemu_api_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
+  dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
                  util_rs, qom, hwcore, chardev],
 )
 
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
   dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
 
-# Doctests are essentially integration tests, so they need the same dependencies.
-# Note that running them requires the object files for C code, so place them
-# in a separate suite that is run by the "build" CI jobs rather than "check".
-rust.doctest('rust-qemu-api-doctests',
-     _qemu_api_rs,
-     protocol: 'rust',
-     dependencies: [qemu_api_rs],
-     suite: ['doc', 'rust'])
-
 test('rust-qemu-api-integration',
     executable(
         'rust-qemu-api-integration',
@@ -88,7 +74,7 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [common_rs, util_rs, migration_rs, qemu_api_rs]),
+        dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs
index 5a351dcecb0..2ec90cc0b2e 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -18,13 +18,10 @@
     slice,
 };
 
+use bql::{BqlRefCell, BqlRefMut};
 use common::{callbacks::FnCall, errno, Opaque};
 
-use crate::{
-    bindings,
-    cell::{BqlRefCell, BqlRefMut},
-    prelude::*,
-};
+use crate::{bindings, prelude::*};
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
@@ -44,13 +41,15 @@ pub struct CharBackend {
     _pin: PhantomPinned,
 }
 
-impl Write for BqlRefMut<'_, bindings::CharBackend> {
+pub struct CharBackendMut<'a>(BqlRefMut<'a, bindings::CharBackend>);
+
+impl Write for CharBackendMut<'_> {
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
 
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        let chr: &mut bindings::CharBackend = self;
+        let chr: &mut bindings::CharBackend = &mut self.0;
 
         let len = buf.len().try_into().unwrap();
         let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -58,7 +57,7 @@ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
     }
 
     fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
-        let chr: &mut bindings::CharBackend = self;
+        let chr: &mut bindings::CharBackend = &mut self.0;
 
         let len = buf.len().try_into().unwrap();
         let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -198,7 +197,7 @@ pub fn accept_input(&self) {
     /// the big QEMU lock while the character device is borrowed, as
     /// that might cause C code to write to the character device.
     pub fn borrow_mut(&self) -> impl Write + '_ {
-        self.inner.borrow_mut()
+        CharBackendMut(self.inner.borrow_mut())
     }
 
     /// Send a continuous stream of zero bits on the line if `enabled` is
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index ea6b32848c5..3063fbe97aa 100644
--- a/rust/qemu-api/src/irq.rs
+++ b/rust/qemu-api/src/irq.rs
@@ -10,6 +10,7 @@
     ptr,
 };
 
+use bql::BqlCell;
 use common::Opaque;
 
 use crate::{
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 55386f66978..6cd9e5b9905 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -13,7 +13,6 @@
 #[rustfmt::skip]
 pub mod prelude;
 
-pub mod cell;
 pub mod chardev;
 pub mod irq;
 pub mod memory;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index c10c1711581..9da7313016d 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -4,9 +4,6 @@
 
 //! Commonly used traits and types for QEMU.
 
-pub use crate::cell::BqlCell;
-pub use crate::cell::BqlRefCell;
-
 pub use crate::qdev::DeviceMethods;
 
 pub use crate::qom::InterfaceType;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index c81ae7cf45c..74a82b87105 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -16,7 +16,6 @@
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
-    cell::bql_locked,
     chardev::Chardev,
     irq::InterruptSource,
     prelude::*,
@@ -322,7 +321,7 @@ fn do_init_clock_in(
             cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
             events: ClockEvent,
         ) -> Owned<Clock> {
-            assert!(bql_locked());
+            assert!(bql::is_locked());
 
             // SAFETY: the clock is heap allocated, but qdev_init_clock_in()
             // does not gift the reference to its caller; so use Owned::from to
@@ -393,7 +392,7 @@ pub trait DeviceMethods: ObjectDeref
     Self::Target: IsA<DeviceState>,
 {
     fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         let c_propname = CString::new(propname).unwrap();
         let chr: &Chardev = chr;
         unsafe {
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 7f2f7797e44..032701af652 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -105,12 +105,9 @@
 use common::Opaque;
 use migration::impl_vmstate_pointer;
 
-use crate::{
-    bindings::{
-        self, object_class_dynamic_cast, object_dynamic_cast, object_get_class,
-        object_get_typename, object_new, object_ref, object_unref, TypeInfo,
-    },
-    cell::bql_locked,
+use crate::bindings::{
+    self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename,
+    object_new, object_ref, object_unref, TypeInfo,
 };
 
 /// A safe wrapper around [`bindings::Object`].
@@ -873,7 +870,7 @@ impl<T: ObjectType> ObjectDeref for Owned<T> {}
 
 impl<T: ObjectType> Drop for Owned<T> {
     fn drop(&mut self) {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         // SAFETY: creation method is unsafe, and whoever calls it has
         // responsibility that the pointer is valid, and remains valid
         // throughout the lifetime of the `Owned<T>` and its clones.
@@ -897,7 +894,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 pub trait ObjectClassMethods: IsA<Object> {
     /// Return a new reference counted instance of this class
     fn new() -> Owned<Self> {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         // SAFETY: the object created by object_new is allocated on
         // the heap and has a reference count of 1
         unsafe {
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index 2dbfc31dbda..b21883246e9 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -11,7 +11,6 @@
 
 use crate::{
     bindings,
-    cell::bql_locked,
     irq::{IRQState, InterruptSource},
     memory::MemoryRegion,
     prelude::*,
@@ -56,7 +55,7 @@ pub trait SysBusDeviceMethods: ObjectDeref
     /// region with a number that corresponds to the order of calls to
     /// `init_mmio`.
     fn init_mmio(&self, iomem: &MemoryRegion) {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         unsafe {
             bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr());
         }
@@ -67,7 +66,7 @@ fn init_mmio(&self, iomem: &MemoryRegion) {
     /// whoever creates the sysbus device will refer to the interrupts with
     /// a number that corresponds to the order of calls to `init_irq`.
     fn init_irq(&self, irq: &InterruptSource) {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         unsafe {
             bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr());
         }
@@ -75,7 +74,7 @@ fn init_irq(&self, irq: &InterruptSource) {
 
     // TODO: do we want a type like GuestAddress here?
     fn mmio_addr(&self, id: u32) -> Option<u64> {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         // SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and
         // the SysBusDevice must be initialized to get an IsA<SysBusDevice>.
         let sbd = unsafe { *self.upcast().as_ptr() };
@@ -89,7 +88,7 @@ fn mmio_addr(&self, id: u32) -> Option<u64> {
 
     // TODO: do we want a type like GuestAddress here?
     fn mmio_map(&self, id: u32, addr: u64) {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         let id: i32 = id.try_into().unwrap();
         unsafe {
             bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr);
@@ -100,7 +99,7 @@ fn mmio_map(&self, id: u32, addr: u64) {
     // object_property_set_link) adds a reference to the IRQState,
     // which can prolong its life
     fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) {
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         let id: i32 = id.try_into().unwrap();
         let irq: &IRQState = irq;
         unsafe {
@@ -110,7 +109,7 @@ fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) {
 
     fn sysbus_realize(&self) {
         // TODO: return an Error
-        assert!(bql_locked());
+        assert!(bql::is_locked());
         unsafe {
             bindings::sysbus_realize(
                 self.upcast().as_mut_ptr(),
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 92e3534d3c1..e72ba08aefe 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -4,9 +4,9 @@
 
 use std::{ffi::CStr, ptr::addr_of};
 
+use bql::BqlCell;
 use migration::{VMStateDescription, VMStateDescriptionBuilder};
 use qemu_api::{
-    cell::{self, BqlCell},
     prelude::*,
     qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
     qom::{ObjectImpl, ParentField},
@@ -94,7 +94,7 @@ pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) {
 fn init_qom() {
     static ONCE: BqlCell<bool> = BqlCell::new(false);
 
-    cell::bql_start_test();
+    bql::start_test();
     if !ONCE.get() {
         unsafe {
             module_call_init(module_init_type::MODULE_INIT_QOM);
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs
index 47fc15149b4..fa9bbd6a122 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -9,6 +9,7 @@
     slice,
 };
 
+use bql::BqlCell;
 use common::Opaque;
 use migration::{
     bindings::{
@@ -19,7 +20,6 @@
     vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField},
     vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate,
 };
-use qemu_api::cell::BqlCell;
 
 const FOO_ARRAY_MAX: usize = 3;
 
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 21/33] rust: split "qom" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (18 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 20/33] rust: split "bql" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-08 10:49 ` [PATCH 22/33] rust: split "chardev" crate Paolo Bonzini
                   ` (13 subsequent siblings)
  33 siblings, 0 replies; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Zhao Liu

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Link: https://lore.kernel.org/r/20250827104147.717203-13-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                       |  1 +
 rust/qom/wrapper.h                | 27 +++++++++++++++++++
 rust/Cargo.lock                   | 14 ++++++++++
 rust/Cargo.toml                   |  1 +
 rust/hw/char/pl011/Cargo.toml     |  1 +
 rust/hw/char/pl011/meson.build    |  1 +
 rust/hw/char/pl011/src/device.rs  |  2 +-
 rust/hw/timer/hpet/Cargo.toml     |  1 +
 rust/hw/timer/hpet/meson.build    |  1 +
 rust/hw/timer/hpet/src/device.rs  |  3 +--
 rust/meson.build                  |  1 +
 rust/migration/src/vmstate.rs     |  2 +-
 rust/qemu-api-macros/src/lib.rs   |  4 +--
 rust/qemu-api-macros/src/tests.rs |  4 +--
 rust/qemu-api/Cargo.toml          |  1 +
 rust/qemu-api/meson.build         | 15 +++++++----
 rust/qemu-api/src/bindings.rs     |  1 +
 rust/qemu-api/src/chardev.rs      |  3 ++-
 rust/qemu-api/src/irq.rs          | 10 +++----
 rust/qemu-api/src/lib.rs          |  1 -
 rust/qemu-api/src/memory.rs       |  7 +++--
 rust/qemu-api/src/prelude.rs      | 11 --------
 rust/qemu-api/src/qdev.rs         |  5 ++--
 rust/qemu-api/src/sysbus.rs       |  4 +--
 rust/qemu-api/tests/tests.rs      |  3 +--
 rust/qom/Cargo.toml               | 23 +++++++++++++++++
 rust/qom/build.rs                 |  1 +
 rust/qom/meson.build              | 43 +++++++++++++++++++++++++++++++
 rust/qom/src/bindings.rs          | 25 ++++++++++++++++++
 rust/qom/src/lib.rs               | 11 ++++++++
 rust/qom/src/prelude.rs           | 12 +++++++++
 rust/{qemu-api => qom}/src/qom.rs |  4 +--
 32 files changed, 199 insertions(+), 44 deletions(-)
 create mode 100644 rust/qom/wrapper.h
 create mode 100644 rust/qom/Cargo.toml
 create mode 120000 rust/qom/build.rs
 create mode 100644 rust/qom/meson.build
 create mode 100644 rust/qom/src/bindings.rs
 create mode 100644 rust/qom/src/lib.rs
 create mode 100644 rust/qom/src/prelude.rs
 rename rust/{qemu-api => qom}/src/qom.rs (99%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2e87f18e2bf..70840abddc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3518,6 +3518,7 @@ F: rust/common/
 F: rust/migration/
 F: rust/qemu-api
 F: rust/qemu-api-macros
+F: rust/qom/
 F: rust/rustfmt.toml
 F: rust/util/
 F: scripts/get-wraps-from-cargo-registry.py
diff --git a/rust/qom/wrapper.h b/rust/qom/wrapper.h
new file mode 100644
index 00000000000..3b71bcd3f5b
--- /dev/null
+++ b/rust/qom/wrapper.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "qom/object.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 73ca9582a56..442eadf08fa 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -82,6 +82,7 @@ dependencies = [
  "migration",
  "qemu_api",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -121,6 +122,7 @@ dependencies = [
  "migration",
  "qemu_api",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -164,6 +166,7 @@ dependencies = [
  "common",
  "migration",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -176,6 +179,17 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "qom"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "common",
+ "migration",
+ "qemu_api_macros",
+ "util",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.36"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 8be90da8ff7..0516c16591c 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -7,6 +7,7 @@ members = [
     "migration",
     "qemu-api-macros",
     "qemu-api",
+    "qom",
     "hw/char/pl011",
     "hw/timer/hpet",
     "util",
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 1a1d4ba7152..da89f78727f 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -20,6 +20,7 @@ common = { path = "../../../common" }
 util = { path = "../../../util" }
 bql = { path = "../../../bql" }
 migration = { path = "../../../migration" }
+qom = { path = "../../../qom" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 8561c4c14ae..af9393c9da9 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -13,6 +13,7 @@ _libpl011_rs = static_library(
     migration_rs,
     bql_rs,
     qemu_api_macros,
+    qom_rs,
   ],
 )
 
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 00ae4328250..63651b9dcdf 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -16,9 +16,9 @@
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
-    qom::{ObjectImpl, Owned, ParentField, ParentInit},
     sysbus::{SysBusDevice, SysBusDeviceImpl},
 };
+use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
 use util::{log::Log, log_mask_ln};
 
 use crate::registers::{self, Interrupt, RegisterOffset};
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 9fcec38bfa6..19456ec72b5 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -15,6 +15,7 @@ common = { path = "../../../common" }
 util = { path = "../../../util" }
 migration = { path = "../../../migration" }
 bql = { path = "../../../bql" }
+qom = { path = "../../../qom" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index 43a62db0d01..50ccdee4a93 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -10,6 +10,7 @@ _libhpet_rs = static_library(
     migration_rs,
     bql_rs,
     qemu_api_macros,
+    qom_rs,
   ],
 )
 
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 9658e071c26..404569aa2de 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -27,10 +27,9 @@
     },
     prelude::*,
     qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
-    qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
-    qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
 };
+use qom::{prelude::*, ObjectImpl, ParentField, ParentInit};
 use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
 use crate::fw_cfg::HPETFwConfig;
diff --git a/rust/meson.build b/rust/meson.build
index 2ba1ea2280d..043603d4169 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -28,6 +28,7 @@ subdir('bits')
 subdir('util')
 subdir('migration')
 subdir('bql')
+subdir('qom')
 subdir('qemu-api')
 
 subdir('hw')
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index d714aacb7e3..c05c4a1fd66 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -137,7 +137,7 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 ///
 /// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html
 /// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html
-/// [`Owned`]: ../../qemu_api/qom/struct.Owned.html
+/// [`Owned`]: ../../qom/qom/struct.Owned.html
 #[macro_export]
 macro_rules! vmstate_of {
     ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index 67650a9a269..e643e57ebdf 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -98,11 +98,11 @@ fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
 
     Ok(quote! {
         ::common::assert_field_type!(#name, #parent,
-            ::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>);
+            ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>);
 
         ::util::module_init! {
             MODULE_INIT_QOM => unsafe {
-                ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
+                ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO);
             }
         }
     })
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs
index 8e71ac6e677..76e6c57479e 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -168,11 +168,11 @@ struct Foo {
             ::common::assert_field_type!(
                 Foo,
                 _unused,
-                ::qemu_api::qom::ParentField<<Foo as ::qemu_api::qom::ObjectImpl>::ParentType>
+                ::qom::ParentField<<Foo as ::qom::ObjectImpl>::ParentType>
             );
             ::util::module_init! {
                 MODULE_INIT_QOM => unsafe {
-                    ::qemu_api::bindings::type_register_static(&<Foo as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
+                    ::qom::type_register_static(&<Foo as ::qom::ObjectImpl>::TYPE_INFO);
                 }
             }
         }
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 6e9427f80c8..9d11becb284 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -18,6 +18,7 @@ common = { path = "../common" }
 migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
+qom = { path = "../qom" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 
 [lints]
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index a47ee6c1a3d..11e43bb6468 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -22,9 +22,15 @@ foreach enum : c_bitfields
   _qemu_api_bindgen_args += ['--bitfield-enum', enum]
 endforeach
 
-_qemu_api_bindgen_args += ['--blocklist-type', 'VMStateDescription']
+blocked_type = [
+  'ObjectClass',
+  'VMStateDescription',
+  'Error',
+]
+foreach type: blocked_type
+  _qemu_api_bindgen_args += ['--blocklist-type', type]
+endforeach
 
-_qemu_api_bindgen_args += ['--blocklist-type', 'Error']
 # TODO: Remove this comment when the clang/libclang mismatch issue is solved.
 #
 # Rust bindings generation with `bindgen` might fail in some cases where the
@@ -52,7 +58,6 @@ _qemu_api_rs = static_library(
       'src/memory.rs',
       'src/prelude.rs',
       'src/qdev.rs',
-      'src/qom.rs',
       'src/sysbus.rs',
     ],
     {'.' : _qemu_api_bindings_inc_rs},
@@ -61,7 +66,7 @@ _qemu_api_rs = static_library(
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
   dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
-                 util_rs, qom, hwcore, chardev],
+                 qom_rs, util_rs, hwcore, chardev],
 )
 
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
@@ -74,7 +79,7 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api_rs]),
+        dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index ce00a6e0e4c..525f136ae2b 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -22,6 +22,7 @@
 
 use common::Zeroable;
 use migration::bindings::VMStateDescription;
+use qom::bindings::ObjectClass;
 use util::bindings::Error;
 
 #[cfg(MESON)]
diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs
index 2ec90cc0b2e..072d806e4a3 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -20,8 +20,9 @@
 
 use bql::{BqlRefCell, BqlRefMut};
 use common::{callbacks::FnCall, errno, Opaque};
+use qom::prelude::*;
 
-use crate::{bindings, prelude::*};
+use crate::bindings;
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index 3063fbe97aa..fead2bbe8e0 100644
--- a/rust/qemu-api/src/irq.rs
+++ b/rust/qemu-api/src/irq.rs
@@ -12,12 +12,9 @@
 
 use bql::BqlCell;
 use common::Opaque;
+use qom::{prelude::*, ObjectClass};
 
-use crate::{
-    bindings::{self, qemu_set_irq},
-    prelude::*,
-    qom::ObjectClass,
-};
+use crate::bindings::{self, qemu_set_irq};
 
 /// An opaque wrapper around [`bindings::IRQState`].
 #[repr(transparent)]
@@ -36,7 +33,7 @@
 ///
 /// Interrupts are implemented as a pointer to the interrupt "sink", which has
 /// type [`IRQState`].  A device exposes its source as a QOM link property using
-/// a function such as [`SysBusDeviceMethods::init_irq`], and
+/// a function such as [`crate::sysbus::SysBusDeviceMethods::init_irq`], and
 /// initially leaves the pointer to a NULL value, representing an unconnected
 /// interrupt. To connect it, whoever creates the device fills the pointer with
 /// the sink's `IRQState *`, for example using `sysbus_connect_irq`.  Because
@@ -114,4 +111,5 @@ unsafe impl ObjectType for IRQState {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) };
 }
+
 qom_isa!(IRQState: Object);
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 6cd9e5b9905..0541050e667 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -17,7 +17,6 @@
 pub mod irq;
 pub mod memory;
 pub mod qdev;
-pub mod qom;
 pub mod sysbus;
 
 // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
index f790cb5fd2d..ecbbd9b6046 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/qemu-api/src/memory.rs
@@ -11,11 +11,9 @@
 
 pub use bindings::{hwaddr, MemTxAttrs};
 use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque};
+use qom::prelude::*;
 
-use crate::{
-    bindings::{self, device_endian, memory_region_init_io},
-    prelude::*,
-};
+use crate::bindings::{self, device_endian, memory_region_init_io};
 
 pub struct MemoryRegionOps<T>(
     bindings::MemoryRegionOps,
@@ -186,6 +184,7 @@ unsafe impl ObjectType for MemoryRegion {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) };
 }
+
 qom_isa!(MemoryRegion: Object);
 
 /// A special `MemTxAttrs` constant, used to indicate that no memory
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 9da7313016d..9e9d1c82474 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -6,15 +6,4 @@
 
 pub use crate::qdev::DeviceMethods;
 
-pub use crate::qom::InterfaceType;
-pub use crate::qom::IsA;
-pub use crate::qom::Object;
-pub use crate::qom::ObjectCast;
-pub use crate::qom::ObjectClassMethods;
-pub use crate::qom::ObjectDeref;
-pub use crate::qom::ObjectMethods;
-pub use crate::qom::ObjectType;
-
-pub use crate::qom_isa;
-
 pub use crate::sysbus::SysBusDeviceMethods;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 74a82b87105..3daf9dda2b5 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -12,14 +12,13 @@
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use common::{callbacks::FnCall, Opaque};
 use migration::{impl_vmstate_c_struct, VMStateDescription};
+use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit};
 use util::{Error, Result};
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
     chardev::Chardev,
     irq::InterruptSource,
-    prelude::*,
-    qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
 };
 
 /// A safe wrapper around [`bindings::Clock`].
@@ -291,6 +290,7 @@ unsafe impl ObjectType for DeviceState {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
 }
+
 qom_isa!(DeviceState: Object);
 
 /// Initialization methods take a [`ParentInit`] and can be called as
@@ -453,6 +453,7 @@ unsafe impl ObjectType for Clock {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
 }
+
 qom_isa!(Clock: Object);
 
 impl_vmstate_c_struct!(Clock, bindings::vmstate_clock);
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index b21883246e9..b883d7eaf18 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -8,14 +8,13 @@
 
 pub use bindings::SysBusDeviceClass;
 use common::Opaque;
+use qom::{prelude::*, Owned};
 
 use crate::{
     bindings,
     irq::{IRQState, InterruptSource},
     memory::MemoryRegion,
-    prelude::*,
     qdev::{DeviceImpl, DeviceState},
-    qom::Owned,
 };
 
 /// A safe wrapper around [`bindings::SysBusDevice`].
@@ -31,6 +30,7 @@ unsafe impl ObjectType for SysBusDevice {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
 }
+
 qom_isa!(SysBusDevice: DeviceState, Object);
 
 // TODO: add virtual methods
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index e72ba08aefe..f2e5eb9f4f4 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -7,11 +7,10 @@
 use bql::BqlCell;
 use migration::{VMStateDescription, VMStateDescriptionBuilder};
 use qemu_api::{
-    prelude::*,
     qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
-    qom::{ObjectImpl, ParentField},
     sysbus::SysBusDevice,
 };
+use qom::{prelude::*, ObjectImpl, ParentField};
 use util::bindings::{module_call_init, module_init_type};
 
 mod vmstate_tests;
diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml
new file mode 100644
index 00000000000..46bbf7c7fe4
--- /dev/null
+++ b/rust/qom/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "qom"
+version = "0.1.0"
+description = "Rust bindings for QEMU/QOM"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+bql = { path = "../bql" }
+migration = { path = "../migration" }
+qemu_api_macros = { path = "../qemu-api-macros" }
+util = { path = "../util" }
+
+[lints]
+workspace = true
diff --git a/rust/qom/build.rs b/rust/qom/build.rs
new file mode 120000
index 00000000000..71a3167885c
--- /dev/null
+++ b/rust/qom/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/qom/meson.build b/rust/qom/meson.build
new file mode 100644
index 00000000000..84a65cb737e
--- /dev/null
+++ b/rust/qom/meson.build
@@ -0,0 +1,43 @@
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_qom_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common,
+)
+
+_qom_rs = static_library(
+  'qom',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/prelude.rs',
+      'src/qom.rs',
+    ],
+    {'.': _qom_bindings_inc_rs}
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  link_with: [_bql_rs, _migration_rs],
+  dependencies: [common_rs, qemu_api_macros],
+)
+
+qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_api_macros, qom])
+
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-qom-rs-doctests',
+     _qom_rs,
+     protocol: 'rust',
+     dependencies: qom_rs,
+     suite: ['doc', 'rust'])
diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs
new file mode 100644
index 00000000000..9ffff12cded
--- /dev/null
+++ b/rust/qom/src/bindings.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs
new file mode 100644
index 00000000000..204c6fea2ff
--- /dev/null
+++ b/rust/qom/src/lib.rs
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+// preserve one-item-per-"use" syntax, it is clearer
+// for prelude-like modules
+#[rustfmt::skip]
+pub mod prelude;
+
+mod qom;
+pub use qom::*;
diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs
new file mode 100644
index 00000000000..00a60959771
--- /dev/null
+++ b/rust/qom/src/prelude.rs
@@ -0,0 +1,12 @@
+//! Traits and essential types intended for blanket imports.
+
+pub use crate::qom::InterfaceType;
+pub use crate::qom::IsA;
+pub use crate::qom::Object;
+pub use crate::qom::ObjectCast;
+pub use crate::qom::ObjectClassMethods;
+pub use crate::qom::ObjectDeref;
+pub use crate::qom::ObjectMethods;
+pub use crate::qom::ObjectType;
+
+pub use crate::qom_isa;
diff --git a/rust/qemu-api/src/qom.rs b/rust/qom/src/qom.rs
similarity index 99%
rename from rust/qemu-api/src/qom.rs
rename to rust/qom/src/qom.rs
index 032701af652..3ea1ad9c5be 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qom/src/qom.rs
@@ -101,7 +101,6 @@
     ptr::NonNull,
 };
 
-pub use bindings::ObjectClass;
 use common::Opaque;
 use migration::impl_vmstate_pointer;
 
@@ -109,6 +108,7 @@
     self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename,
     object_new, object_ref, object_unref, TypeInfo,
 };
+pub use crate::bindings::{type_register_static, ObjectClass};
 
 /// A safe wrapper around [`bindings::Object`].
 #[repr(transparent)]
@@ -146,7 +146,7 @@ macro_rules! qom_isa {
         $(
             // SAFETY: it is the caller responsibility to have $parent as the
             // first field
-            unsafe impl $crate::qom::IsA<$parent> for $struct {}
+            unsafe impl $crate::IsA<$parent> for $struct {}
 
             impl AsRef<$parent> for $struct {
                 fn as_ref(&self) -> &$parent {
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 22/33] rust: split "chardev" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (19 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 21/33] rust: split "qom" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:35   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 23/33] rust: split "system" crate Paolo Bonzini
                   ` (12 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-14-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                               |  1 +
 rust/chardev/wrapper.h                    | 28 ++++++++++++++++
 rust/qemu-api/wrapper.h                   |  2 --
 rust/Cargo.lock                           | 14 ++++++++
 rust/chardev/Cargo.toml                   | 24 +++++++++++++
 rust/chardev/build.rs                     |  1 +
 rust/chardev/meson.build                  | 41 +++++++++++++++++++++++
 rust/chardev/src/bindings.rs              | 36 ++++++++++++++++++++
 rust/{qemu-api => chardev}/src/chardev.rs |  0
 rust/chardev/src/lib.rs                   |  6 ++++
 rust/hw/char/pl011/Cargo.toml             |  1 +
 rust/hw/char/pl011/meson.build            |  1 +
 rust/hw/char/pl011/src/device.rs          |  2 +-
 rust/meson.build                          |  1 +
 rust/qemu-api/Cargo.toml                  |  1 +
 rust/qemu-api/meson.build                 |  9 +++--
 rust/qemu-api/src/bindings.rs             |  9 +----
 rust/qemu-api/src/lib.rs                  |  1 -
 rust/qemu-api/src/qdev.rs                 |  6 ++--
 19 files changed, 164 insertions(+), 20 deletions(-)
 create mode 100644 rust/chardev/wrapper.h
 create mode 100644 rust/chardev/Cargo.toml
 create mode 120000 rust/chardev/build.rs
 create mode 100644 rust/chardev/meson.build
 create mode 100644 rust/chardev/src/bindings.rs
 rename rust/{qemu-api => chardev}/src/chardev.rs (100%)
 create mode 100644 rust/chardev/src/lib.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 70840abddc8..0d9b7a36675 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3514,6 +3514,7 @@ Rust
 M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 S: Maintained
 F: rust/bql/
+F: rust/chardev/
 F: rust/common/
 F: rust/migration/
 F: rust/qemu-api
diff --git a/rust/chardev/wrapper.h b/rust/chardev/wrapper.h
new file mode 100644
index 00000000000..65ede6ea6d7
--- /dev/null
+++ b/rust/chardev/wrapper.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "chardev/char-fe.h"
+#include "chardev/char-serial.h"
diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h
index b99df9f568a..07dbc9987a8 100644
--- a/rust/qemu-api/wrapper.h
+++ b/rust/qemu-api/wrapper.h
@@ -52,13 +52,11 @@ typedef enum memory_order {
 #include "system/system.h"
 #include "hw/sysbus.h"
 #include "system/memory.h"
-#include "chardev/char-fe.h"
 #include "hw/clock.h"
 #include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
 #include "hw/qdev-properties-system.h"
 #include "hw/irq.h"
-#include "chardev/char-serial.h"
 #include "exec/memattrs.h"
 #include "system/address-spaces.h"
 #include "hw/char/pl011.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 442eadf08fa..ae852c55508 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -51,6 +51,18 @@ dependencies = [
  "migration",
 ]
 
+[[package]]
+name = "chardev"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "common",
+ "migration",
+ "qemu_api_macros",
+ "qom",
+ "util",
+]
+
 [[package]]
 name = "common"
 version = "0.1.0"
@@ -118,6 +130,7 @@ dependencies = [
  "bilge-impl",
  "bits",
  "bql",
+ "chardev",
  "common",
  "migration",
  "qemu_api",
@@ -163,6 +176,7 @@ name = "qemu_api"
 version = "0.1.0"
 dependencies = [
  "bql",
+ "chardev",
  "common",
  "migration",
  "qemu_api_macros",
diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml
new file mode 100644
index 00000000000..7df9c677fc7
--- /dev/null
+++ b/rust/chardev/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "chardev"
+version = "0.1.0"
+description = "Rust bindings for QEMU/chardev"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+bql = { path = "../bql" }
+migration = { path = "../migration" }
+qom = { path = "../qom" }
+util = { path = "../util" }
+qemu_api_macros = { path = "../qemu-api-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/chardev/build.rs b/rust/chardev/build.rs
new file mode 120000
index 00000000000..71a3167885c
--- /dev/null
+++ b/rust/chardev/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build
new file mode 100644
index 00000000000..5d333e232bd
--- /dev/null
+++ b/rust/chardev/meson.build
@@ -0,0 +1,41 @@
+c_enums = [
+  'QEMUChrEvent',
+]
+_chardev_bindgen_args = []
+foreach enum : c_enums
+  _chardev_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_chardev_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common + _chardev_bindgen_args,
+)
+
+_chardev_rs = static_library(
+  'chardev',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/chardev.rs',
+    ],
+    {'.': _chardev_bindings_inc_rs}
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs],
+  dependencies: [common_rs, qemu_api_macros],
+)
+
+chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev, qemuutil])
diff --git a/rust/chardev/src/bindings.rs b/rust/chardev/src/bindings.rs
new file mode 100644
index 00000000000..2d98026d627
--- /dev/null
+++ b/rust/chardev/src/bindings.rs
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+use common::Zeroable;
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+// SAFETY: these are implemented in C; the bindings need to assert that the
+// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
+// When bindings for character devices are introduced, this can be
+// moved to the Opaque<> wrapper in src/chardev.rs.
+unsafe impl Send for CharBackend {}
+unsafe impl Sync for CharBackend {}
+
+unsafe impl Zeroable for CharBackend {}
diff --git a/rust/qemu-api/src/chardev.rs b/rust/chardev/src/chardev.rs
similarity index 100%
rename from rust/qemu-api/src/chardev.rs
rename to rust/chardev/src/chardev.rs
diff --git a/rust/chardev/src/lib.rs b/rust/chardev/src/lib.rs
new file mode 100644
index 00000000000..2e549f99d91
--- /dev/null
+++ b/rust/chardev/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+mod chardev;
+pub use chardev::*;
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index da89f78727f..f7ad5f8e08f 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -21,6 +21,7 @@ util = { path = "../../../util" }
 bql = { path = "../../../bql" }
 migration = { path = "../../../migration" }
 qom = { path = "../../../qom" }
+chardev = { path = "../../../chardev" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index af9393c9da9..aaf911c5f42 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -14,6 +14,7 @@ _libpl011_rs = static_library(
     bql_rs,
     qemu_api_macros,
     qom_rs,
+    chardev_rs,
   ],
 )
 
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 63651b9dcdf..bc64061fb39 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -5,13 +5,13 @@
 use std::{ffi::CStr, mem::size_of};
 
 use bql::BqlRefCell;
+use chardev::{CharBackend, Chardev, Event};
 use common::{static_assert, uninit_field_mut};
 use migration::{
     self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
     vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder,
 };
 use qemu_api::{
-    chardev::{CharBackend, Chardev, Event},
     irq::{IRQState, InterruptSource},
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
diff --git a/rust/meson.build b/rust/meson.build
index 043603d4169..4d9e2912232 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -29,6 +29,7 @@ subdir('util')
 subdir('migration')
 subdir('bql')
 subdir('qom')
+subdir('chardev')
 subdir('qemu-api')
 
 subdir('hw')
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 9d11becb284..3bf2dafa6df 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -15,6 +15,7 @@ rust-version.workspace = true
 
 [dependencies]
 common = { path = "../common" }
+chardev = { path = "../chardev" }
 migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 11e43bb6468..a47f178b698 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -7,7 +7,6 @@ c_enums = [
   'GpioPolarity',
   'MachineInitPhase',
   'MemoryDeviceInfoKind',
-  'QEMUChrEvent',
   'ResetType',
   'device_endian',
 ]
@@ -23,9 +22,10 @@ foreach enum : c_bitfields
 endforeach
 
 blocked_type = [
+  'Chardev',
+  'Error',
   'ObjectClass',
   'VMStateDescription',
-  'Error',
 ]
 foreach type: blocked_type
   _qemu_api_bindgen_args += ['--blocklist-type', type]
@@ -53,7 +53,6 @@ _qemu_api_rs = static_library(
     [
       'src/lib.rs',
       'src/bindings.rs',
-      'src/chardev.rs',
       'src/irq.rs',
       'src/memory.rs',
       'src/prelude.rs',
@@ -65,8 +64,8 @@ _qemu_api_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
-                 qom_rs, util_rs, hwcore, chardev],
+  dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
+                 qom_rs, util_rs, hwcore],
 )
 
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 525f136ae2b..526bcf8e31b 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -20,6 +20,7 @@
 
 //! `bindgen`-generated declarations.
 
+use chardev::bindings::Chardev;
 use common::Zeroable;
 use migration::bindings::VMStateDescription;
 use qom::bindings::ObjectClass;
@@ -31,13 +32,6 @@
 #[cfg(not(MESON))]
 include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
 
-// SAFETY: these are implemented in C; the bindings need to assert that the
-// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
-// When bindings for character devices are introduced, this can be
-// moved to the Opaque<> wrapper in src/chardev.rs.
-unsafe impl Send for CharBackend {}
-unsafe impl Sync for CharBackend {}
-
 // SAFETY: this is a pure data struct
 unsafe impl Send for CoalescedMemoryRange {}
 unsafe impl Sync for CoalescedMemoryRange {}
@@ -59,4 +53,3 @@ unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {}
 unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {}
 unsafe impl Zeroable for crate::bindings::MemoryRegionOps {}
 unsafe impl Zeroable for crate::bindings::MemTxAttrs {}
-unsafe impl Zeroable for crate::bindings::CharBackend {}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 0541050e667..d96096899d0 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -13,7 +13,6 @@
 #[rustfmt::skip]
 pub mod prelude;
 
-pub mod chardev;
 pub mod irq;
 pub mod memory;
 pub mod qdev;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 3daf9dda2b5..7efc796f502 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -10,6 +10,7 @@
 };
 
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
+use chardev::Chardev;
 use common::{callbacks::FnCall, Opaque};
 use migration::{impl_vmstate_c_struct, VMStateDescription};
 use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit};
@@ -17,7 +18,6 @@
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
-    chardev::Chardev,
     irq::InterruptSource,
 };
 
@@ -137,8 +137,8 @@ unsafe impl QDevProp for u64 {
     const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 };
 }
 
-/// Use [`bindings::qdev_prop_chr`] for [`crate::chardev::CharBackend`].
-unsafe impl QDevProp for crate::chardev::CharBackend {
+/// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`].
+unsafe impl QDevProp for chardev::CharBackend {
     const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr };
 }
 
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 23/33] rust: split "system" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (20 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 22/33] rust: split "chardev" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:38   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 24/33] rust: split "hwcore" crate Paolo Bonzini
                   ` (11 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-15-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                             |  1 +
 rust/qemu-api/wrapper.h                 |  3 --
 rust/system/wrapper.h                   | 29 +++++++++++++++++
 rust/Cargo.lock                         | 13 ++++++++
 rust/Cargo.toml                         |  1 +
 rust/bql/src/cell.rs                    |  5 ++-
 rust/hw/char/pl011/Cargo.toml           |  1 +
 rust/hw/char/pl011/meson.build          |  1 +
 rust/hw/char/pl011/src/device.rs        |  2 +-
 rust/hw/timer/hpet/Cargo.toml           |  1 +
 rust/hw/timer/hpet/meson.build          |  1 +
 rust/hw/timer/hpet/src/device.rs        | 12 +++----
 rust/meson.build                        |  1 +
 rust/qemu-api/Cargo.toml                |  1 +
 rust/qemu-api/meson.build               |  7 +++--
 rust/qemu-api/src/bindings.rs           | 14 +--------
 rust/qemu-api/src/lib.rs                |  1 -
 rust/qemu-api/src/sysbus.rs             |  2 +-
 rust/system/Cargo.toml                  | 22 +++++++++++++
 rust/system/build.rs                    |  1 +
 rust/system/meson.build                 | 42 +++++++++++++++++++++++++
 rust/system/src/bindings.rs             | 41 ++++++++++++++++++++++++
 rust/system/src/lib.rs                  |  6 ++++
 rust/{qemu-api => system}/src/memory.rs |  2 +-
 24 files changed, 177 insertions(+), 33 deletions(-)
 create mode 100644 rust/system/wrapper.h
 create mode 100644 rust/system/Cargo.toml
 create mode 120000 rust/system/build.rs
 create mode 100644 rust/system/meson.build
 create mode 100644 rust/system/src/bindings.rs
 create mode 100644 rust/system/src/lib.rs
 rename rust/{qemu-api => system}/src/memory.rs (99%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0d9b7a36675..2b5be3c45c3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3521,6 +3521,7 @@ F: rust/qemu-api
 F: rust/qemu-api-macros
 F: rust/qom/
 F: rust/rustfmt.toml
+F: rust/system/
 F: rust/util/
 F: scripts/get-wraps-from-cargo-registry.py
 
diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h
index 07dbc9987a8..564733b9035 100644
--- a/rust/qemu-api/wrapper.h
+++ b/rust/qemu-api/wrapper.h
@@ -49,14 +49,11 @@ typedef enum memory_order {
 
 #include "qemu/osdep.h"
 #include "qemu-io.h"
-#include "system/system.h"
 #include "hw/sysbus.h"
-#include "system/memory.h"
 #include "hw/clock.h"
 #include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
 #include "hw/qdev-properties-system.h"
 #include "hw/irq.h"
 #include "exec/memattrs.h"
-#include "system/address-spaces.h"
 #include "hw/char/pl011.h"
diff --git a/rust/system/wrapper.h b/rust/system/wrapper.h
new file mode 100644
index 00000000000..48abde85052
--- /dev/null
+++ b/rust/system/wrapper.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "system/system.h"
+#include "system/memory.h"
+#include "system/address-spaces.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index ae852c55508..e6b75f30bef 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -95,6 +95,7 @@ dependencies = [
  "qemu_api",
  "qemu_api_macros",
  "qom",
+ "system",
  "util",
 ]
 
@@ -136,6 +137,7 @@ dependencies = [
  "qemu_api",
  "qemu_api_macros",
  "qom",
+ "system",
  "util",
 ]
 
@@ -181,6 +183,7 @@ dependencies = [
  "migration",
  "qemu_api_macros",
  "qom",
+ "system",
  "util",
 ]
 
@@ -224,6 +227,16 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "system"
+version = "0.1.0"
+dependencies = [
+ "common",
+ "qemu_api_macros",
+ "qom",
+ "util",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 0516c16591c..8e210d277a2 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -8,6 +8,7 @@ members = [
     "qemu-api-macros",
     "qemu-api",
     "qom",
+    "system",
     "hw/char/pl011",
     "hw/timer/hpet",
     "util",
diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs
index 25007427edc..24ab294b60d 100644
--- a/rust/bql/src/cell.rs
+++ b/rust/bql/src/cell.rs
@@ -77,9 +77,8 @@
 //!
 //! ```ignore
 //! # use bql::BqlRefCell;
-//! # use qemu_api::prelude::*;
-//! # use qemu_api::{irq::InterruptSource, irq::IRQState};
-//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
+//! # use qom::{Owned, ParentField};
+//! # use system::{InterruptSource, IRQState, SysBusDevice};
 //! # const N_GPIOS: usize = 8;
 //! # struct PL061Registers { /* ... */ }
 //! # unsafe impl ObjectType for PL061State {
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index f7ad5f8e08f..e4b1c3f1eb8 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -22,6 +22,7 @@ bql = { path = "../../../bql" }
 migration = { path = "../../../migration" }
 qom = { path = "../../../qom" }
 chardev = { path = "../../../chardev" }
+system = { path = "../../../system" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index aaf911c5f42..fae6e1b9c9d 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -15,6 +15,7 @@ _libpl011_rs = static_library(
     qemu_api_macros,
     qom_rs,
     chardev_rs,
+    system_rs,
   ],
 )
 
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index bc64061fb39..c65db5a5174 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -13,12 +13,12 @@
 };
 use qemu_api::{
     irq::{IRQState, InterruptSource},
-    memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
     sysbus::{SysBusDevice, SysBusDeviceImpl},
 };
 use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
+use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
 use util::{log::Log, log_mask_ln};
 
 use crate::registers::{self, Interrupt, RegisterOffset};
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 19456ec72b5..a95b1271c64 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -16,6 +16,7 @@ util = { path = "../../../util" }
 migration = { path = "../../../migration" }
 bql = { path = "../../../bql" }
 qom = { path = "../../../qom" }
+system = { path = "../../../system" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index 50ccdee4a93..c4ffe020f6b 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -11,6 +11,7 @@ _libhpet_rs = static_library(
     bql_rs,
     qemu_api_macros,
     qom_rs,
+    system_rs,
   ],
 )
 
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 404569aa2de..841c2ba3375 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -17,19 +17,17 @@
     VMStateDescription, VMStateDescriptionBuilder,
 };
 use qemu_api::{
-    bindings::{
-        address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
-        qdev_prop_uint32, qdev_prop_usize,
-    },
+    bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize},
     irq::InterruptSource,
-    memory::{
-        hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
-    },
     prelude::*,
     qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
     sysbus::{SysBusDevice, SysBusDeviceImpl},
 };
 use qom::{prelude::*, ObjectImpl, ParentField, ParentInit};
+use system::{
+    bindings::{address_space_memory, address_space_stl_le, hwaddr},
+    MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
+};
 use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
 use crate::fw_cfg::HPETFwConfig;
diff --git a/rust/meson.build b/rust/meson.build
index 4d9e2912232..d8b71f55061 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -29,6 +29,7 @@ subdir('util')
 subdir('migration')
 subdir('bql')
 subdir('qom')
+subdir('system')
 subdir('chardev')
 subdir('qemu-api')
 
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 3bf2dafa6df..2884c1d460b 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -20,6 +20,7 @@ migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
 qom = { path = "../qom" }
+system = { path = "../system" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 
 [lints]
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index a47f178b698..92e2581a64e 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -8,7 +8,6 @@ c_enums = [
   'MachineInitPhase',
   'MemoryDeviceInfoKind',
   'ResetType',
-  'device_endian',
 ]
 _qemu_api_bindgen_args = []
 foreach enum : c_enums
@@ -24,8 +23,11 @@ endforeach
 blocked_type = [
   'Chardev',
   'Error',
+  'MemTxAttrs',
+  'MemoryRegion',
   'ObjectClass',
   'VMStateDescription',
+  'device_endian',
 ]
 foreach type: blocked_type
   _qemu_api_bindgen_args += ['--blocklist-type', type]
@@ -54,7 +56,6 @@ _qemu_api_rs = static_library(
       'src/lib.rs',
       'src/bindings.rs',
       'src/irq.rs',
-      'src/memory.rs',
       'src/prelude.rs',
       'src/qdev.rs',
       'src/sysbus.rs',
@@ -65,7 +66,7 @@ _qemu_api_rs = static_library(
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
   dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
-                 qom_rs, util_rs, hwcore],
+                 qom_rs, system_rs, util_rs, hwcore],
 )
 
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 526bcf8e31b..63b805c76e4 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -24,6 +24,7 @@
 use common::Zeroable;
 use migration::bindings::VMStateDescription;
 use qom::bindings::ObjectClass;
+use system::bindings::{device_endian, MemTxAttrs, MemoryRegion};
 use util::bindings::Error;
 
 #[cfg(MESON)]
@@ -32,15 +33,6 @@
 #[cfg(not(MESON))]
 include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
 
-// SAFETY: this is a pure data struct
-unsafe impl Send for CoalescedMemoryRange {}
-unsafe impl Sync for CoalescedMemoryRange {}
-
-// SAFETY: these are constants and vtables; the Send and Sync requirements
-// are deferred to the unsafe callbacks that they contain
-unsafe impl Send for MemoryRegionOps {}
-unsafe impl Sync for MemoryRegionOps {}
-
 unsafe impl Send for Property {}
 unsafe impl Sync for Property {}
 
@@ -49,7 +41,3 @@ unsafe impl Sync for TypeInfo {}
 
 unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
 unsafe impl Zeroable for crate::bindings::Property {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps {}
-unsafe impl Zeroable for crate::bindings::MemTxAttrs {}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index d96096899d0..8d574404789 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -14,7 +14,6 @@
 pub mod prelude;
 
 pub mod irq;
-pub mod memory;
 pub mod qdev;
 pub mod sysbus;
 
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index b883d7eaf18..dda71ebda70 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -9,11 +9,11 @@
 pub use bindings::SysBusDeviceClass;
 use common::Opaque;
 use qom::{prelude::*, Owned};
+use system::MemoryRegion;
 
 use crate::{
     bindings,
     irq::{IRQState, InterruptSource},
-    memory::MemoryRegion,
     qdev::{DeviceImpl, DeviceState},
 };
 
diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
new file mode 100644
index 00000000000..6803895e080
--- /dev/null
+++ b/rust/system/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "system"
+version = "0.1.0"
+description = "Rust bindings for QEMU/system"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+qom = { path = "../qom" }
+util = { path = "../util" }
+qemu_api_macros = { path = "../qemu-api-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/system/build.rs b/rust/system/build.rs
new file mode 120000
index 00000000000..71a3167885c
--- /dev/null
+++ b/rust/system/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/system/meson.build b/rust/system/meson.build
new file mode 100644
index 00000000000..ae9b932d29d
--- /dev/null
+++ b/rust/system/meson.build
@@ -0,0 +1,42 @@
+c_enums = [
+  'device_endian',
+]
+_system_bindgen_args = []
+foreach enum : c_enums
+  _system_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_system_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common + _system_bindgen_args,
+)
+
+_system_rs = static_library(
+  'system',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/memory.rs',
+    ],
+    {'.': _system_bindings_inc_rs}
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs],
+  dependencies: [common_rs, qemu_api_macros],
+)
+
+system_rs = declare_dependency(link_with: [_system_rs],
+  dependencies: [qemu_api_macros, hwcore])
diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs
new file mode 100644
index 00000000000..43edd98807a
--- /dev/null
+++ b/rust/system/src/bindings.rs
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+use common::Zeroable;
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+// SAFETY: these are constants and vtables; the Send and Sync requirements
+// are deferred to the unsafe callbacks that they contain
+unsafe impl Send for MemoryRegionOps {}
+unsafe impl Sync for MemoryRegionOps {}
+
+// SAFETY: this is a pure data struct
+unsafe impl Send for CoalescedMemoryRange {}
+unsafe impl Sync for CoalescedMemoryRange {}
+
+unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_1 {}
+unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_2 {}
+unsafe impl Zeroable for MemoryRegionOps {}
+unsafe impl Zeroable for MemTxAttrs {}
diff --git a/rust/system/src/lib.rs b/rust/system/src/lib.rs
new file mode 100644
index 00000000000..aafe9a866c9
--- /dev/null
+++ b/rust/system/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+mod memory;
+pub use memory::*;
diff --git a/rust/qemu-api/src/memory.rs b/rust/system/src/memory.rs
similarity index 99%
rename from rust/qemu-api/src/memory.rs
rename to rust/system/src/memory.rs
index ecbbd9b6046..29568ed7676 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/system/src/memory.rs
@@ -9,11 +9,11 @@
     marker::PhantomData,
 };
 
-pub use bindings::{hwaddr, MemTxAttrs};
 use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque};
 use qom::prelude::*;
 
 use crate::bindings::{self, device_endian, memory_region_init_io};
+pub use crate::bindings::{hwaddr, MemTxAttrs};
 
 pub struct MemoryRegionOps<T>(
     bindings::MemoryRegionOps,
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 24/33] rust: split "hwcore" crate
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (21 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 23/33] rust: split "system" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:41   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 25/33] rust: rename qemu_api_macros -> qemu_macros Paolo Bonzini
                   ` (10 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-16-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                               |  1 +
 rust/hw/core/wrapper.h                    | 32 +++++++++
 rust/qemu-api/wrapper.h                   |  6 --
 rust/Cargo.lock                           | 17 +++++
 rust/Cargo.toml                           |  1 +
 rust/bindings/src/lib.rs                  | 64 ++++++++++++++++++
 rust/hw/char/pl011/Cargo.toml             |  1 +
 rust/hw/char/pl011/meson.build            |  1 +
 rust/hw/char/pl011/src/device.rs          | 10 ++-
 rust/hw/core/Cargo.toml                   | 26 ++++++++
 rust/hw/core/build.rs                     |  1 +
 rust/hw/core/meson.build                  | 80 +++++++++++++++++++++++
 rust/hw/core/src/bindings.rs              | 41 ++++++++++++
 rust/{qemu-api => hw/core}/src/irq.rs     |  0
 rust/hw/core/src/lib.rs                   | 14 ++++
 rust/{qemu-api => hw/core}/src/qdev.rs    |  2 +-
 rust/{qemu-api => hw/core}/src/sysbus.rs  |  0
 rust/{qemu-api => hw/core}/tests/tests.rs |  7 +-
 rust/hw/timer/hpet/Cargo.toml             |  1 +
 rust/hw/timer/hpet/meson.build            |  1 +
 rust/hw/timer/hpet/src/device.rs          | 26 ++++----
 rust/meson.build                          |  1 +
 rust/qemu-api-macros/src/lib.rs           | 10 +--
 rust/qemu-api-macros/src/tests.rs         | 20 +++---
 rust/qemu-api/Cargo.toml                  |  1 +
 rust/qemu-api/meson.build                 | 17 +----
 rust/qemu-api/src/bindings.rs             | 10 ---
 rust/qemu-api/src/lib.rs                  |  4 --
 rust/qemu-api/src/prelude.rs              |  4 --
 29 files changed, 318 insertions(+), 81 deletions(-)
 create mode 100644 rust/hw/core/wrapper.h
 create mode 100644 rust/bindings/src/lib.rs
 create mode 100644 rust/hw/core/Cargo.toml
 create mode 120000 rust/hw/core/build.rs
 create mode 100644 rust/hw/core/meson.build
 create mode 100644 rust/hw/core/src/bindings.rs
 rename rust/{qemu-api => hw/core}/src/irq.rs (100%)
 create mode 100644 rust/hw/core/src/lib.rs
 rename rust/{qemu-api => hw/core}/src/qdev.rs (99%)
 rename rust/{qemu-api => hw/core}/src/sysbus.rs (100%)
 rename rust/{qemu-api => hw/core}/tests/tests.rs (97%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2b5be3c45c3..50dda5d119e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3516,6 +3516,7 @@ S: Maintained
 F: rust/bql/
 F: rust/chardev/
 F: rust/common/
+F: rust/hw/core/
 F: rust/migration/
 F: rust/qemu-api
 F: rust/qemu-api-macros
diff --git a/rust/hw/core/wrapper.h b/rust/hw/core/wrapper.h
new file mode 100644
index 00000000000..3bdbd1249e4
--- /dev/null
+++ b/rust/hw/core/wrapper.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "hw/sysbus.h"
+#include "hw/clock.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/irq.h"
diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h
index 564733b9035..7c9c20b14fe 100644
--- a/rust/qemu-api/wrapper.h
+++ b/rust/qemu-api/wrapper.h
@@ -49,11 +49,5 @@ typedef enum memory_order {
 
 #include "qemu/osdep.h"
 #include "qemu-io.h"
-#include "hw/sysbus.h"
-#include "hw/clock.h"
-#include "hw/qdev-clock.h"
-#include "hw/qdev-properties.h"
-#include "hw/qdev-properties-system.h"
-#include "hw/irq.h"
 #include "exec/memattrs.h"
 #include "hw/char/pl011.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index e6b75f30bef..77118e882b5 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -91,6 +91,7 @@ version = "0.1.0"
 dependencies = [
  "bql",
  "common",
+ "hwcore",
  "migration",
  "qemu_api",
  "qemu_api_macros",
@@ -99,6 +100,20 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "hwcore"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "chardev",
+ "common",
+ "migration",
+ "qemu_api_macros",
+ "qom",
+ "system",
+ "util",
+]
+
 [[package]]
 name = "itertools"
 version = "0.11.0"
@@ -133,6 +148,7 @@ dependencies = [
  "bql",
  "chardev",
  "common",
+ "hwcore",
  "migration",
  "qemu_api",
  "qemu_api_macros",
@@ -180,6 +196,7 @@ dependencies = [
  "bql",
  "chardev",
  "common",
+ "hwcore",
  "migration",
  "qemu_api_macros",
  "qom",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 8e210d277a2..8ec07d20651 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -9,6 +9,7 @@ members = [
     "qemu-api",
     "qom",
     "system",
+    "hw/core",
     "hw/char/pl011",
     "hw/timer/hpet",
     "util",
diff --git a/rust/bindings/src/lib.rs b/rust/bindings/src/lib.rs
new file mode 100644
index 00000000000..5bf03b13706
--- /dev/null
+++ b/rust/bindings/src/lib.rs
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc
+)]
+
+//! `bindgen`-generated declarations.
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+// SAFETY: these are implemented in C; the bindings need to assert that the
+// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
+// When bindings for character devices are introduced, this can be
+// moved to the Opaque<> wrapper in src/chardev.rs.
+unsafe impl Send for CharBackend {}
+unsafe impl Sync for CharBackend {}
+
+// SAFETY: this is a pure data struct
+unsafe impl Send for CoalescedMemoryRange {}
+unsafe impl Sync for CoalescedMemoryRange {}
+
+// SAFETY: these are constants and vtables; the Send and Sync requirements
+// are deferred to the unsafe callbacks that they contain
+unsafe impl Send for MemoryRegionOps {}
+unsafe impl Sync for MemoryRegionOps {}
+
+unsafe impl Send for Property {}
+unsafe impl Sync for Property {}
+
+unsafe impl Send for TypeInfo {}
+unsafe impl Sync for TypeInfo {}
+
+unsafe impl Send for VMStateDescription {}
+unsafe impl Sync for VMStateDescription {}
+
+unsafe impl Send for VMStateField {}
+unsafe impl Sync for VMStateField {}
+
+unsafe impl Send for VMStateInfo {}
+unsafe impl Sync for VMStateInfo {}
+
+// bindgen does not derive Default here
+#[allow(clippy::derivable_impls)]
+impl Default for VMStateFlags {
+    fn default() -> Self {
+        Self(0)
+    }
+}
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index e4b1c3f1eb8..830d88586bb 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -23,6 +23,7 @@ migration = { path = "../../../migration" }
 qom = { path = "../../../qom" }
 chardev = { path = "../../../chardev" }
 system = { path = "../../../system" }
+hwcore = { path = "../../../hw/core" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
 
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index fae6e1b9c9d..fac04321133 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -16,6 +16,7 @@ _libpl011_rs = static_library(
     qom_rs,
     chardev_rs,
     system_rs,
+    hwcore_rs,
   ],
 )
 
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index c65db5a5174..a6a17d9f2dc 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -7,16 +7,14 @@
 use bql::BqlRefCell;
 use chardev::{CharBackend, Chardev, Event};
 use common::{static_assert, uninit_field_mut};
+use hwcore::{
+    Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource,
+    ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
+};
 use migration::{
     self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
     vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder,
 };
-use qemu_api::{
-    irq::{IRQState, InterruptSource},
-    prelude::*,
-    qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
-    sysbus::{SysBusDevice, SysBusDeviceImpl},
-};
 use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
 use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
 use util::{log::Log, log_mask_ln};
diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml
new file mode 100644
index 00000000000..0b353802644
--- /dev/null
+++ b/rust/hw/core/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "hwcore"
+version = "0.1.0"
+description = "Rust bindings for QEMU/hwcore"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../../common" }
+bql = { path = "../../bql" }
+qom = { path = "../../qom" }
+chardev = { path = "../../chardev" }
+migration = { path = "../../migration" }
+system = { path = "../../system" }
+util = { path = "../../util" }
+qemu_api_macros = { path = "../../qemu-api-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/hw/core/build.rs b/rust/hw/core/build.rs
new file mode 120000
index 00000000000..2a79ee31b8c
--- /dev/null
+++ b/rust/hw/core/build.rs
@@ -0,0 +1 @@
+../../util/build.rs
\ No newline at end of file
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
new file mode 100644
index 00000000000..7dd1ade6f0f
--- /dev/null
+++ b/rust/hw/core/meson.build
@@ -0,0 +1,80 @@
+_hwcore_bindgen_args = []
+c_enums = [
+  'DeviceCategory',
+  'GpioPolarity',
+  'MachineInitPhase',
+  'ResetType',
+]
+foreach enum : c_enums
+  _hwcore_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+blocked_type = [
+  'Chardev',
+  'Error',
+  'ObjectClass',
+  'MemoryRegion',
+  'VMStateDescription',
+]
+foreach type: blocked_type
+  _hwcore_bindgen_args += ['--blocklist-type', type]
+endforeach
+
+c_bitfields = [
+  'ClockEvent',
+]
+foreach enum : c_bitfields
+  _hwcore_bindgen_args += ['--bitfield-enum', enum]
+endforeach
+
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_hwcore_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common + _hwcore_bindgen_args,
+)
+
+_hwcore_rs = static_library(
+  'hwcore',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/irq.rs',
+      'src/qdev.rs',
+      'src/sysbus.rs',
+    ],
+    {'.': _hwcore_bindings_inc_rs}
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs],
+  dependencies: [qemu_api_macros, common_rs],
+)
+
+hwcore_rs = declare_dependency(link_with: [_hwcore_rs],
+  dependencies: [qom_rs, hwcore])
+
+test('rust-hwcore-rs-integration',
+    executable(
+        'rust-hwcore-rs-integration',
+        files('tests/tests.rs'),
+        override_options: ['rust_std=2021', 'build.rust_std=2021'],
+        rust_args: ['--test'],
+        install: false,
+        dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_api_macros, util_rs]),
+    args: [
+        '--test', '--test-threads', '1',
+        '--format', 'pretty',
+    ],
+    protocol: 'rust',
+    suite: ['unit', 'rust'])
diff --git a/rust/hw/core/src/bindings.rs b/rust/hw/core/src/bindings.rs
new file mode 100644
index 00000000000..919c02b56ae
--- /dev/null
+++ b/rust/hw/core/src/bindings.rs
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+use chardev::bindings::Chardev;
+use common::Zeroable;
+use migration::bindings::VMStateDescription;
+use qom::bindings::ObjectClass;
+use system::bindings::MemoryRegion;
+use util::bindings::Error;
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+unsafe impl Send for Property {}
+unsafe impl Sync for Property {}
+
+unsafe impl Send for TypeInfo {}
+unsafe impl Sync for TypeInfo {}
+
+unsafe impl Zeroable for Property__bindgen_ty_1 {}
+unsafe impl Zeroable for Property {}
diff --git a/rust/qemu-api/src/irq.rs b/rust/hw/core/src/irq.rs
similarity index 100%
rename from rust/qemu-api/src/irq.rs
rename to rust/hw/core/src/irq.rs
diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs
new file mode 100644
index 00000000000..c5588d9bc27
--- /dev/null
+++ b/rust/hw/core/src/lib.rs
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub use qom;
+
+pub mod bindings;
+
+mod irq;
+pub use irq::*;
+
+mod qdev;
+pub use qdev::*;
+
+mod sysbus;
+pub use sysbus::*;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/hw/core/src/qdev.rs
similarity index 99%
rename from rust/qemu-api/src/qdev.rs
rename to rust/hw/core/src/qdev.rs
index 7efc796f502..8e9702ce0bb 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -9,13 +9,13 @@
     ptr::NonNull,
 };
 
-pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use chardev::Chardev;
 use common::{callbacks::FnCall, Opaque};
 use migration::{impl_vmstate_c_struct, VMStateDescription};
 use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit};
 use util::{Error, Result};
 
+pub use crate::bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
     irq::InterruptSource,
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
similarity index 100%
rename from rust/qemu-api/src/sysbus.rs
rename to rust/hw/core/src/sysbus.rs
diff --git a/rust/qemu-api/tests/tests.rs b/rust/hw/core/tests/tests.rs
similarity index 97%
rename from rust/qemu-api/tests/tests.rs
rename to rust/hw/core/tests/tests.rs
index f2e5eb9f4f4..21ee301fa68 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -5,16 +5,11 @@
 use std::{ffi::CStr, ptr::addr_of};
 
 use bql::BqlCell;
+use hwcore::{DeviceImpl, DeviceState, ResettablePhasesImpl, SysBusDevice};
 use migration::{VMStateDescription, VMStateDescriptionBuilder};
-use qemu_api::{
-    qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
-    sysbus::SysBusDevice,
-};
 use qom::{prelude::*, ObjectImpl, ParentField};
 use util::bindings::{module_call_init, module_init_type};
 
-mod vmstate_tests;
-
 // Test that macros can compile.
 pub const VMSTATE: VMStateDescription<DummyState> = VMStateDescriptionBuilder::<DummyState>::new()
     .name(c"name")
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index a95b1271c64..e28d66f6457 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -19,6 +19,7 @@ qom = { path = "../../../qom" }
 system = { path = "../../../system" }
 qemu_api = { path = "../../../qemu-api" }
 qemu_api_macros = { path = "../../../qemu-api-macros" }
+hwcore = { path = "../../../hw/core" }
 
 [lints]
 workspace = true
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index c4ffe020f6b..e6f99b67785 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -12,6 +12,7 @@ _libhpet_rs = static_library(
     qemu_api_macros,
     qom_rs,
     system_rs,
+    hwcore_rs,
   ],
 )
 
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 841c2ba3375..3031539744f 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -12,17 +12,15 @@
 
 use bql::{BqlCell, BqlRefCell};
 use common::{bitops::IntegerExt, uninit_field_mut};
+use hwcore::{
+    bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize},
+    declare_properties, define_property, DeviceImpl, DeviceMethods, DeviceState, InterruptSource,
+    Property, ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
+};
 use migration::{
     self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
     VMStateDescription, VMStateDescriptionBuilder,
 };
-use qemu_api::{
-    bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize},
-    irq::InterruptSource,
-    prelude::*,
-    qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
-    sysbus::{SysBusDevice, SysBusDeviceImpl},
-};
 use qom::{prelude::*, ObjectImpl, ParentField, ParentInit};
 use system::{
     bindings::{address_space_memory, address_space_stl_le, hwaddr},
@@ -904,9 +902,9 @@ impl ObjectImpl for HPETState {
 }
 
 // TODO: Make these properties user-configurable!
-qemu_api::declare_properties! {
+declare_properties! {
     HPET_PROPERTIES,
-    qemu_api::define_property!(
+    define_property!(
         c"timers",
         HPETState,
         num_timers,
@@ -914,7 +912,7 @@ impl ObjectImpl for HPETState {
         u8,
         default = HPET_MIN_TIMERS
     ),
-    qemu_api::define_property!(
+    define_property!(
         c"msi",
         HPETState,
         flags,
@@ -923,7 +921,7 @@ impl ObjectImpl for HPETState {
         bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8,
         default = false,
     ),
-    qemu_api::define_property!(
+    define_property!(
         c"hpet-intcap",
         HPETState,
         int_route_cap,
@@ -931,7 +929,7 @@ impl ObjectImpl for HPETState {
         u32,
         default = 0
     ),
-    qemu_api::define_property!(
+    define_property!(
         c"hpet-offset-saved",
         HPETState,
         hpet_offset_saved,
@@ -1004,8 +1002,8 @@ impl ObjectImpl for HPETState {
         .build();
 
 // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the
-// qemu_api::declare_properties macro.
-unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState {
+// hwcore::declare_properties macro.
+unsafe impl hwcore::DevicePropertiesImpl for HPETState {
     const PROPERTIES: &'static [Property] = &HPET_PROPERTIES;
 }
 
diff --git a/rust/meson.build b/rust/meson.build
index d8b71f55061..041b0a473e5 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -31,6 +31,7 @@ subdir('bql')
 subdir('qom')
 subdir('system')
 subdir('chardev')
+subdir('hw/core')
 subdir('qemu-api')
 
 subdir('hw')
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index e643e57ebdf..830b4326985 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -272,24 +272,24 @@ macro_rules! str_to_c_str {
             },
         )?;
         let field_ty = field.ty.clone();
-        let qdev_prop = quote! { <#field_ty as ::qemu_api::qdev::QDevProp>::VALUE };
+        let qdev_prop = quote! { <#field_ty as ::hwcore::QDevProp>::VALUE };
         let set_default = defval.is_some();
         let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 }));
         properties_expanded.push(quote! {
-            ::qemu_api::bindings::Property {
+            ::hwcore::bindings::Property {
                 name: ::std::ffi::CStr::as_ptr(#prop_name),
                 info: #qdev_prop ,
                 offset: ::core::mem::offset_of!(#name, #field_name) as isize,
                 set_default: #set_default,
-                defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
+                defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
                 ..::common::Zeroable::ZERO
             }
         });
     }
 
     Ok(quote_spanned! {input.span() =>
-        unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name {
-            const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
+        unsafe impl ::hwcore::DevicePropertiesImpl for #name {
+            const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
                 #(#properties_expanded),*
             ];
         }
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs
index 76e6c57479e..9ab7eab7f37 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -100,14 +100,14 @@ pub struct DummyState {
             }
         },
         quote! {
-            unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
-                const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
-                    ::qemu_api::bindings::Property {
+            unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
+                const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+                    ::hwcore::bindings::Property {
                         name: ::std::ffi::CStr::as_ptr(c"migrate_clock"),
-                        info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
+                        info: <bool as ::hwcore::QDevProp>::VALUE,
                         offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
                         set_default: true,
-                        defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
+                        defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
                         ..::common::Zeroable::ZERO
                     }
                 ];
@@ -127,14 +127,14 @@ pub struct DummyState {
             }
         },
         quote! {
-            unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
-                const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
-                    ::qemu_api::bindings::Property {
+            unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
+                const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+                    ::hwcore::bindings::Property {
                         name: ::std::ffi::CStr::as_ptr(c"migrate-clk"),
-                        info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
+                        info: <bool as ::hwcore::QDevProp>::VALUE,
                         offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
                         set_default: true,
-                        defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
+                        defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
                         ..::common::Zeroable::ZERO
                     }
                 ];
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 2884c1d460b..9e7afc7e3ad 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -16,6 +16,7 @@ rust-version.workspace = true
 [dependencies]
 common = { path = "../common" }
 chardev = { path = "../chardev" }
+hwcore = { path = "../hw/core" }
 migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 92e2581a64e..2dc638782ca 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -3,22 +3,12 @@ _qemu_api_cfg = run_command(rustc_args,
   capture: true, check: true).stdout().strip().splitlines()
 
 c_enums = [
-  'DeviceCategory',
-  'GpioPolarity',
-  'MachineInitPhase',
   'MemoryDeviceInfoKind',
-  'ResetType',
 ]
 _qemu_api_bindgen_args = []
 foreach enum : c_enums
   _qemu_api_bindgen_args += ['--rustified-enum', enum]
 endforeach
-c_bitfields = [
-  'ClockEvent',
-]
-foreach enum : c_bitfields
-  _qemu_api_bindgen_args += ['--bitfield-enum', enum]
-endforeach
 
 blocked_type = [
   'Chardev',
@@ -55,17 +45,14 @@ _qemu_api_rs = static_library(
     [
       'src/lib.rs',
       'src/bindings.rs',
-      'src/irq.rs',
       'src/prelude.rs',
-      'src/qdev.rs',
-      'src/sysbus.rs',
     ],
     {'.' : _qemu_api_bindings_inc_rs},
   ),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
+  dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_api_macros,
                  qom_rs, system_rs, util_rs, hwcore],
 )
 
@@ -75,7 +62,7 @@ qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
 test('rust-qemu-api-integration',
     executable(
         'rust-qemu-api-integration',
-        files('tests/tests.rs', 'tests/vmstate_tests.rs'),
+        files('tests/vmstate_tests.rs'),
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 63b805c76e4..9c863e9b5b4 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -21,7 +21,6 @@
 //! `bindgen`-generated declarations.
 
 use chardev::bindings::Chardev;
-use common::Zeroable;
 use migration::bindings::VMStateDescription;
 use qom::bindings::ObjectClass;
 use system::bindings::{device_endian, MemTxAttrs, MemoryRegion};
@@ -32,12 +31,3 @@
 
 #[cfg(not(MESON))]
 include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
-
-unsafe impl Send for Property {}
-unsafe impl Sync for Property {}
-
-unsafe impl Send for TypeInfo {}
-unsafe impl Sync for TypeInfo {}
-
-unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
-unsafe impl Zeroable for crate::bindings::Property {}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 8d574404789..21b886035f3 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -13,10 +13,6 @@
 #[rustfmt::skip]
 pub mod prelude;
 
-pub mod irq;
-pub mod qdev;
-pub mod sysbus;
-
 // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
 // crate).
 extern crate self as qemu_api;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 9e9d1c82474..8db56f9f817 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -3,7 +3,3 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 //! Commonly used traits and types for QEMU.
-
-pub use crate::qdev::DeviceMethods;
-
-pub use crate::sysbus::SysBusDeviceMethods;
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 25/33] rust: rename qemu_api_macros -> qemu_macros
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (22 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 24/33] rust: split "hwcore" crate Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:41   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 26/33] rust/hpet: drop now unneeded qemu_api dep Paolo Bonzini
                   ` (9 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Since "qemu_api" is no longer the unique crate to provide APIs.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-17-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                                   |  2 +-
 rust/Cargo.lock                               | 22 +++++++++----------
 rust/Cargo.toml                               |  2 +-
 rust/bits/Cargo.toml                          |  2 +-
 rust/bits/meson.build                         |  2 +-
 rust/bits/src/lib.rs                          |  4 ++--
 rust/chardev/Cargo.toml                       |  2 +-
 rust/chardev/meson.build                      |  4 ++--
 rust/chardev/src/chardev.rs                   |  2 +-
 rust/common/src/opaque.rs                     |  4 ++--
 rust/hw/char/pl011/Cargo.toml                 |  2 +-
 rust/hw/char/pl011/meson.build                |  4 ++--
 rust/hw/char/pl011/src/device.rs              |  4 ++--
 rust/hw/char/pl011/src/registers.rs           |  2 +-
 rust/hw/core/Cargo.toml                       |  2 +-
 rust/hw/core/meson.build                      |  4 ++--
 rust/hw/core/src/irq.rs                       |  2 +-
 rust/hw/core/src/qdev.rs                      |  6 ++---
 rust/hw/core/src/sysbus.rs                    |  2 +-
 rust/hw/core/tests/tests.rs                   |  4 ++--
 rust/hw/timer/hpet/Cargo.toml                 |  2 +-
 rust/hw/timer/hpet/meson.build                |  4 ++--
 rust/hw/timer/hpet/src/device.rs              |  6 ++---
 rust/meson.build                              |  2 +-
 rust/migration/Cargo.toml                     |  2 +-
 rust/qemu-api/Cargo.toml                      |  2 +-
 rust/qemu-api/meson.build                     |  4 ++--
 .../Cargo.toml                                |  2 +-
 .../meson.build                               | 10 ++++-----
 .../src/bits.rs                               |  0
 .../src/lib.rs                                |  0
 .../src/tests.rs                              |  0
 rust/qom/Cargo.toml                           |  2 +-
 rust/qom/meson.build                          |  4 ++--
 rust/qom/src/qom.rs                           |  4 ++--
 rust/system/Cargo.toml                        |  2 +-
 rust/system/meson.build                       |  4 ++--
 rust/system/src/memory.rs                     |  2 +-
 rust/util/Cargo.toml                          |  2 +-
 rust/util/meson.build                         |  2 +-
 rust/util/src/timer.rs                        |  4 ++--
 41 files changed, 69 insertions(+), 69 deletions(-)
 rename rust/{qemu-api-macros => qemu-macros}/Cargo.toml (94%)
 rename rust/{qemu-api-macros => qemu-macros}/meson.build (63%)
 rename rust/{qemu-api-macros => qemu-macros}/src/bits.rs (100%)
 rename rust/{qemu-api-macros => qemu-macros}/src/lib.rs (100%)
 rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 50dda5d119e..92cf6929bd8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3519,7 +3519,7 @@ F: rust/common/
 F: rust/hw/core/
 F: rust/migration/
 F: rust/qemu-api
-F: rust/qemu-api-macros
+F: rust/qemu-macros/
 F: rust/qom/
 F: rust/rustfmt.toml
 F: rust/system/
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 77118e882b5..021ce6dd48c 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -41,7 +41,7 @@ dependencies = [
 name = "bits"
 version = "0.1.0"
 dependencies = [
- "qemu_api_macros",
+ "qemu_macros",
 ]
 
 [[package]]
@@ -58,7 +58,7 @@ dependencies = [
  "bql",
  "common",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "util",
 ]
@@ -94,7 +94,7 @@ dependencies = [
  "hwcore",
  "migration",
  "qemu_api",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -108,7 +108,7 @@ dependencies = [
  "chardev",
  "common",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -134,7 +134,7 @@ name = "migration"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_api_macros",
+ "qemu_macros",
  "util",
 ]
 
@@ -151,7 +151,7 @@ dependencies = [
  "hwcore",
  "migration",
  "qemu_api",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -198,14 +198,14 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
 ]
 
 [[package]]
-name = "qemu_api_macros"
+name = "qemu_macros"
 version = "0.1.0"
 dependencies = [
  "proc-macro2",
@@ -220,7 +220,7 @@ dependencies = [
  "bql",
  "common",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "util",
 ]
 
@@ -249,7 +249,7 @@ name = "system"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "util",
 ]
@@ -268,7 +268,7 @@ dependencies = [
  "common",
  "foreign",
  "libc",
- "qemu_api_macros",
+ "qemu_macros",
 ]
 
 [[package]]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 8ec07d20651..b2a5c230fa2 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -5,7 +5,7 @@ members = [
     "bql",
     "common",
     "migration",
-    "qemu-api-macros",
+    "qemu-macros",
     "qemu-api",
     "qom",
     "system",
diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml
index 1ff38a41175..7fce972b270 100644
--- a/rust/bits/Cargo.toml
+++ b/rust/bits/Cargo.toml
@@ -13,7 +13,7 @@ repository.workspace = true
 rust-version.workspace = true
 
 [dependencies]
-qemu_api_macros = { path = "../qemu-api-macros" }
+qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/bits/meson.build b/rust/bits/meson.build
index 2a41e138c54..359ca86f155 100644
--- a/rust/bits/meson.build
+++ b/rust/bits/meson.build
@@ -3,7 +3,7 @@ _bits_rs = static_library(
   'src/lib.rs',
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
-  dependencies: [qemu_api_macros],
+  dependencies: [qemu_macros],
 )
 
 bits_rs = declare_dependency(link_with: _bits_rs)
diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs
index e9d15ad0cb5..1bc882fde18 100644
--- a/rust/bits/src/lib.rs
+++ b/rust/bits/src/lib.rs
@@ -380,11 +380,11 @@ fn from(x: $type) -> Self {
     };
 
     { $type:ty: $expr:expr } => {
-        ::qemu_api_macros::bits_const_internal! { $type @ ($expr) }
+        ::qemu_macros::bits_const_internal! { $type @ ($expr) }
     };
 
     { $type:ty as $int_type:ty: $expr:expr } => {
-        (::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
+        (::qemu_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
     };
 }
 
diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml
index 7df9c677fc7..c1391773078 100644
--- a/rust/chardev/Cargo.toml
+++ b/rust/chardev/Cargo.toml
@@ -18,7 +18,7 @@ bql = { path = "../bql" }
 migration = { path = "../migration" }
 qom = { path = "../qom" }
 util = { path = "../util" }
-qemu_api_macros = { path = "../qemu-api-macros" }
+qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build
index 5d333e232bd..a2fa3268d21 100644
--- a/rust/chardev/meson.build
+++ b/rust/chardev/meson.build
@@ -35,7 +35,7 @@ _chardev_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs],
-  dependencies: [common_rs, qemu_api_macros],
+  dependencies: [common_rs, qemu_macros],
 )
 
-chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev, qemuutil])
+chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev, qemuutil])
diff --git a/rust/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs
index 072d806e4a3..cb6f99398eb 100644
--- a/rust/chardev/src/chardev.rs
+++ b/rust/chardev/src/chardev.rs
@@ -26,7 +26,7 @@
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
-#[derive(qemu_api_macros::Wrapper)]
+#[derive(qemu_macros::Wrapper)]
 pub struct Chardev(Opaque<bindings::Chardev>);
 
 pub type ChardevClass = bindings::ChardevClass;
diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs
index 97ed3e84522..3b3263acaa3 100644
--- a/rust/common/src/opaque.rs
+++ b/rust/common/src/opaque.rs
@@ -192,7 +192,7 @@ pub unsafe fn new() -> Self {
 
 /// Annotates [`Self`] as a transparent wrapper for another type.
 ///
-/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro.
+/// Usually defined via the [`qemu_macros::Wrapper`] derive macro.
 ///
 /// # Examples
 ///
@@ -228,7 +228,7 @@ pub unsafe fn new() -> Self {
 ///
 /// They are not defined here to allow them to be `const`.
 ///
-/// [`qemu_api_macros::Wrapper`]: ../../qemu_api_macros/derive.Wrapper.html
+/// [`qemu_macros::Wrapper`]: ../../qemu_macros/derive.Wrapper.html
 pub unsafe trait Wrapper {
     type Wrapped;
 }
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 830d88586bb..9e451fc0aa8 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -25,7 +25,7 @@ chardev = { path = "../../../chardev" }
 system = { path = "../../../system" }
 hwcore = { path = "../../../hw/core" }
 qemu_api = { path = "../../../qemu-api" }
-qemu_api_macros = { path = "../../../qemu-api-macros" }
+qemu_macros = { path = "../../../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index fac04321133..bad6a839c39 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -12,7 +12,7 @@ _libpl011_rs = static_library(
     util_rs,
     migration_rs,
     bql_rs,
-    qemu_api_macros,
+    qemu_macros,
     qom_rs,
     chardev_rs,
     system_rs,
@@ -24,6 +24,6 @@ rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
   link_whole: [_libpl011_rs],
   # Putting proc macro crates in `dependencies` is necessary for Meson to find
   # them when compiling the root per-target static rust lib.
-  dependencies: [bilge_impl_rs, qemu_api_macros],
+  dependencies: [bilge_impl_rs, qemu_macros],
   variables: {'crate': 'pl011'},
 )])
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index a6a17d9f2dc..3010b6d9839 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -97,7 +97,7 @@ pub struct PL011Registers {
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
+#[derive(qemu_macros::Object, qemu_macros::Device)]
 /// PL011 Device Model in QEMU
 pub struct PL011State {
     pub parent_obj: ParentField<SysBusDevice>,
@@ -683,7 +683,7 @@ pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError>
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
+#[derive(qemu_macros::Object, qemu_macros::Device)]
 /// PL011 Luminary device model.
 pub struct PL011Luminary {
     parent_obj: ParentField<PL011State>,
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index 2bfbd81095e..a1c41347ed5 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -16,7 +16,7 @@
 #[doc(alias = "offset")]
 #[allow(non_camel_case_types)]
 #[repr(u64)]
-#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)]
+#[derive(Debug, Eq, PartialEq, qemu_macros::TryInto)]
 pub enum RegisterOffset {
     /// Data Register
     ///
diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml
index 0b353802644..0eb9ffee263 100644
--- a/rust/hw/core/Cargo.toml
+++ b/rust/hw/core/Cargo.toml
@@ -20,7 +20,7 @@ chardev = { path = "../../chardev" }
 migration = { path = "../../migration" }
 system = { path = "../../system" }
 util = { path = "../../util" }
-qemu_api_macros = { path = "../../qemu-api-macros" }
+qemu_macros = { path = "../../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
index 7dd1ade6f0f..67eacf854fe 100644
--- a/rust/hw/core/meson.build
+++ b/rust/hw/core/meson.build
@@ -58,7 +58,7 @@ _hwcore_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs],
-  dependencies: [qemu_api_macros, common_rs],
+  dependencies: [qemu_macros, common_rs],
 )
 
 hwcore_rs = declare_dependency(link_with: [_hwcore_rs],
@@ -71,7 +71,7 @@ test('rust-hwcore-rs-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_api_macros, util_rs]),
+        dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_macros, util_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs
index fead2bbe8e0..d8d964cad27 100644
--- a/rust/hw/core/src/irq.rs
+++ b/rust/hw/core/src/irq.rs
@@ -18,7 +18,7 @@
 
 /// An opaque wrapper around [`bindings::IRQState`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct IRQState(Opaque<bindings::IRQState>);
 
 /// Interrupt sources are used by devices to pass changes to a value (typically
diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs
index 8e9702ce0bb..c9faf44a71d 100644
--- a/rust/hw/core/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -23,7 +23,7 @@
 
 /// A safe wrapper around [`bindings::Clock`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct Clock(Opaque<bindings::Clock>);
 
 unsafe impl Send for Clock {}
@@ -31,7 +31,7 @@ unsafe impl Sync for Clock {}
 
 /// A safe wrapper around [`bindings::DeviceState`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct DeviceState(Opaque<bindings::DeviceState>);
 
 unsafe impl Send for DeviceState {}
@@ -101,7 +101,7 @@ pub trait ResettablePhasesImpl {
 
 /// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type.
 ///
-/// This trait is used by [`qemu_api_macros::Device`] derive macro.
+/// This trait is used by [`qemu_macros::Device`] derive macro.
 ///
 /// Base types that already have `qdev_prop_*` globals in the QEMU API should
 /// use those values as exported by the [`bindings`] module, instead of
diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
index dda71ebda70..92c7449b802 100644
--- a/rust/hw/core/src/sysbus.rs
+++ b/rust/hw/core/src/sysbus.rs
@@ -19,7 +19,7 @@
 
 /// A safe wrapper around [`bindings::SysBusDevice`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct SysBusDevice(Opaque<bindings::SysBusDevice>);
 
 unsafe impl Send for SysBusDevice {}
diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs
index 21ee301fa68..2f08b8f3bfe 100644
--- a/rust/hw/core/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -17,7 +17,7 @@
     .build();
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
+#[derive(qemu_macros::Object, qemu_macros::Device)]
 pub struct DummyState {
     parent: ParentField<DeviceState>,
     #[property(rename = "migrate-clk", default = true)]
@@ -54,7 +54,7 @@ impl DeviceImpl for DummyState {
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object, qemu_api_macros::Device)]
+#[derive(qemu_macros::Object, qemu_macros::Device)]
 pub struct DummyChildState {
     parent: ParentField<DummyState>,
 }
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index e28d66f6457..68e8187bb82 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -18,7 +18,7 @@ bql = { path = "../../../bql" }
 qom = { path = "../../../qom" }
 system = { path = "../../../system" }
 qemu_api = { path = "../../../qemu-api" }
-qemu_api_macros = { path = "../../../qemu-api-macros" }
+qemu_macros = { path = "../../../qemu-macros" }
 hwcore = { path = "../../../hw/core" }
 
 [lints]
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index e6f99b67785..3b94d5ec0a7 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -9,7 +9,7 @@ _libhpet_rs = static_library(
     util_rs,
     migration_rs,
     bql_rs,
-    qemu_api_macros,
+    qemu_macros,
     qom_rs,
     system_rs,
     hwcore_rs,
@@ -20,6 +20,6 @@ rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency(
   link_whole: [_libhpet_rs],
   # Putting proc macro crates in `dependencies` is necessary for Meson to find
   # them when compiling the root per-target static rust lib.
-  dependencies: [qemu_api_macros],
+  dependencies: [qemu_macros],
   variables: {'crate': 'hpet'},
 )])
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 3031539744f..07e0f639fc4 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -97,7 +97,7 @@
 /// Timer N Interrupt Routing Capability (bits 32:63)
 const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32;
 
-#[derive(qemu_api_macros::TryInto)]
+#[derive(qemu_macros::TryInto)]
 #[repr(u64)]
 #[allow(non_camel_case_types)]
 /// Timer registers, masked by 0x18
@@ -110,7 +110,7 @@ enum TimerRegister {
     ROUTE = 16,
 }
 
-#[derive(qemu_api_macros::TryInto)]
+#[derive(qemu_macros::TryInto)]
 #[repr(u64)]
 #[allow(non_camel_case_types)]
 /// Global registers
@@ -520,7 +520,7 @@ fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) {
 
 /// HPET Event Timer Block Abstraction
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_macros::Object)]
 pub struct HPETState {
     parent_obj: ParentField<SysBusDevice>,
     iomem: MemoryRegion,
diff --git a/rust/meson.build b/rust/meson.build
index 041b0a473e5..9f6a0b161d2 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -23,7 +23,7 @@ qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true)
 genrs = []
 
 subdir('common')
-subdir('qemu-api-macros')
+subdir('qemu-macros')
 subdir('bits')
 subdir('util')
 subdir('migration')
diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml
index 98e6df2109d..66af81e0a35 100644
--- a/rust/migration/Cargo.toml
+++ b/rust/migration/Cargo.toml
@@ -15,7 +15,7 @@ rust-version.workspace = true
 [dependencies]
 common = { path = "../common" }
 util = { path = "../util" }
-qemu_api_macros = { path = "../qemu-api-macros" }
+qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 9e7afc7e3ad..9abb88aa1f8 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -20,9 +20,9 @@ hwcore = { path = "../hw/core" }
 migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
+qemu_macros = { path = "../qemu-macros" }
 qom = { path = "../qom" }
 system = { path = "../system" }
-qemu_api_macros = { path = "../qemu-api-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 2dc638782ca..fe81f16d990 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -52,12 +52,12 @@ _qemu_api_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_api_macros,
+  dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_macros,
                  qom_rs, system_rs, util_rs, hwcore],
 )
 
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
-  dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
+  dependencies: [qemu_macros, qom, hwcore, chardev, migration])
 
 test('rust-qemu-api-integration',
     executable(
diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-macros/Cargo.toml
similarity index 94%
rename from rust/qemu-api-macros/Cargo.toml
rename to rust/qemu-macros/Cargo.toml
index 0cd40c8e168..3b6f1d337f8 100644
--- a/rust/qemu-api-macros/Cargo.toml
+++ b/rust/qemu-macros/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "qemu_api_macros"
+name = "qemu_macros"
 version = "0.1.0"
 authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
 description = "Rust bindings for QEMU - Utility macros"
diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-macros/meson.build
similarity index 63%
rename from rust/qemu-api-macros/meson.build
rename to rust/qemu-macros/meson.build
index 2152bcb99b3..d0b2992e204 100644
--- a/rust/qemu-api-macros/meson.build
+++ b/rust/qemu-macros/meson.build
@@ -1,5 +1,5 @@
-_qemu_api_macros_rs = rust.proc_macro(
-  'qemu_api_macros',
+_qemu_macros_rs = rust.proc_macro(
+  'qemu_macros',
   files('src/lib.rs'),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_args: [
@@ -14,9 +14,9 @@ _qemu_api_macros_rs = rust.proc_macro(
   ],
 )
 
-qemu_api_macros = declare_dependency(
-  link_with: _qemu_api_macros_rs,
+qemu_macros = declare_dependency(
+  link_with: _qemu_macros_rs,
 )
 
-rust.test('rust-qemu-api-macros-tests', _qemu_api_macros_rs,
+rust.test('rust-qemu-macros-tests', _qemu_macros_rs,
           suite: ['unit', 'rust'])
diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-macros/src/bits.rs
similarity index 100%
rename from rust/qemu-api-macros/src/bits.rs
rename to rust/qemu-macros/src/bits.rs
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs
similarity index 100%
rename from rust/qemu-api-macros/src/lib.rs
rename to rust/qemu-macros/src/lib.rs
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs
similarity index 100%
rename from rust/qemu-api-macros/src/tests.rs
rename to rust/qemu-macros/src/tests.rs
diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml
index 46bbf7c7fe4..060ad2ec349 100644
--- a/rust/qom/Cargo.toml
+++ b/rust/qom/Cargo.toml
@@ -16,7 +16,7 @@ rust-version.workspace = true
 common = { path = "../common" }
 bql = { path = "../bql" }
 migration = { path = "../migration" }
-qemu_api_macros = { path = "../qemu-api-macros" }
+qemu_macros = { path = "../qemu-macros" }
 util = { path = "../util" }
 
 [lints]
diff --git a/rust/qom/meson.build b/rust/qom/meson.build
index 84a65cb737e..40c51b71b23 100644
--- a/rust/qom/meson.build
+++ b/rust/qom/meson.build
@@ -28,10 +28,10 @@ _qom_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   link_with: [_bql_rs, _migration_rs],
-  dependencies: [common_rs, qemu_api_macros],
+  dependencies: [common_rs, qemu_macros],
 )
 
-qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_api_macros, qom])
+qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom])
 
 # Doctests are essentially integration tests, so they need the same dependencies.
 # Note that running them requires the object files for C code, so place them
diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs
index 3ea1ad9c5be..2cd1d850112 100644
--- a/rust/qom/src/qom.rs
+++ b/rust/qom/src/qom.rs
@@ -112,7 +112,7 @@
 
 /// A safe wrapper around [`bindings::Object`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct Object(Opaque<bindings::Object>);
 
 unsafe impl Send for Object {}
@@ -173,7 +173,7 @@ fn as_ref(&self) -> &$parent {
 ///
 /// ```ignore
 /// #[repr(C)]
-/// #[derive(qemu_api_macros::Object)]
+/// #[derive(qemu_macros::Object)]
 /// pub struct MyDevice {
 ///     parent: ParentField<DeviceState>,
 ///     ...
diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
index 6803895e080..d8338c8348d 100644
--- a/rust/system/Cargo.toml
+++ b/rust/system/Cargo.toml
@@ -16,7 +16,7 @@ rust-version.workspace = true
 common = { path = "../common" }
 qom = { path = "../qom" }
 util = { path = "../util" }
-qemu_api_macros = { path = "../qemu-api-macros" }
+qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/system/meson.build b/rust/system/meson.build
index ae9b932d29d..9f88166f3d3 100644
--- a/rust/system/meson.build
+++ b/rust/system/meson.build
@@ -35,8 +35,8 @@ _system_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs],
-  dependencies: [common_rs, qemu_api_macros],
+  dependencies: [common_rs, qemu_macros],
 )
 
 system_rs = declare_dependency(link_with: [_system_rs],
-  dependencies: [qemu_api_macros, hwcore])
+  dependencies: [qemu_macros, hwcore])
diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs
index 29568ed7676..7312f809f51 100644
--- a/rust/system/src/memory.rs
+++ b/rust/system/src/memory.rs
@@ -129,7 +129,7 @@ fn default() -> Self {
 
 /// A safe wrapper around [`bindings::MemoryRegion`].
 #[repr(transparent)]
-#[derive(qemu_api_macros::Wrapper)]
+#[derive(qemu_macros::Wrapper)]
 pub struct MemoryRegion(Opaque<bindings::MemoryRegion>);
 
 unsafe impl Send for MemoryRegion {}
diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml
index 637df61060a..18e6619ca06 100644
--- a/rust/util/Cargo.toml
+++ b/rust/util/Cargo.toml
@@ -17,7 +17,7 @@ anyhow = { workspace = true }
 foreign = { workspace = true }
 libc = { workspace = true }
 common = { path = "../common" }
-qemu_api_macros = { path = "../qemu-api-macros" }
+qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/util/meson.build b/rust/util/meson.build
index 56e929349b8..197872c9b23 100644
--- a/rust/util/meson.build
+++ b/rust/util/meson.build
@@ -39,7 +39,7 @@ _util_rs = static_library(
   ),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
-  dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_api_macros, qom, qemuutil],
+  dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_macros, qom, qemuutil],
 )
 
 util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom])
diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs
index 383e1a6e774..622b6ee3097 100644
--- a/rust/util/src/timer.rs
+++ b/rust/util/src/timer.rs
@@ -15,14 +15,14 @@
 
 /// A safe wrapper around [`bindings::QEMUTimer`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct Timer(Opaque<bindings::QEMUTimer>);
 
 unsafe impl Send for Timer {}
 unsafe impl Sync for Timer {}
 
 #[repr(transparent)]
-#[derive(qemu_api_macros::Wrapper)]
+#[derive(qemu_macros::Wrapper)]
 pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);
 
 unsafe impl Send for TimerListGroup {}
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 26/33] rust/hpet: drop now unneeded qemu_api dep
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (23 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 25/33] rust: rename qemu_api_macros -> qemu_macros Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:42   ` Zhao Liu
  2025-09-08 10:49 ` [PATCH 27/33] rust/pl011: drop dependency on qemu_api Paolo Bonzini
                   ` (8 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-18-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/Cargo.lock                | 1 -
 rust/hw/timer/hpet/Cargo.toml  | 1 -
 rust/hw/timer/hpet/meson.build | 1 -
 3 files changed, 3 deletions(-)

diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 021ce6dd48c..c407029afed 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -93,7 +93,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_api",
  "qemu_macros",
  "qom",
  "system",
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 68e8187bb82..08bf97af553 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -17,7 +17,6 @@ migration = { path = "../../../migration" }
 bql = { path = "../../../bql" }
 qom = { path = "../../../qom" }
 system = { path = "../../../system" }
-qemu_api = { path = "../../../qemu-api" }
 qemu_macros = { path = "../../../qemu-macros" }
 hwcore = { path = "../../../hw/core" }
 
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index 3b94d5ec0a7..8ab26630d95 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -5,7 +5,6 @@ _libhpet_rs = static_library(
   rust_abi: 'rust',
   dependencies: [
     common_rs,
-    qemu_api_rs,
     util_rs,
     migration_rs,
     bql_rs,
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 27/33] rust/pl011: drop dependency on qemu_api
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (24 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 26/33] rust/hpet: drop now unneeded qemu_api dep Paolo Bonzini
@ 2025-09-08 10:49 ` Paolo Bonzini
  2025-09-12  8:45   ` Zhao Liu
  2025-09-08 10:50 ` [PATCH 28/33] rust: repurpose qemu_api -> tests Paolo Bonzini
                   ` (7 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-19-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/hw/char/pl011/wrapper.h       | 51 ++++++++++++++++++++++++++++++
 rust/Cargo.lock                    |  1 -
 rust/hw/char/pl011/Cargo.toml      |  1 -
 rust/hw/char/pl011/build.rs        |  1 +
 rust/hw/char/pl011/meson.build     | 27 ++++++++++++++--
 rust/hw/char/pl011/src/bindings.rs | 27 ++++++++++++++++
 rust/hw/char/pl011/src/device.rs   |  2 +-
 rust/hw/char/pl011/src/lib.rs      |  1 +
 8 files changed, 106 insertions(+), 5 deletions(-)
 create mode 100644 rust/hw/char/pl011/wrapper.h
 create mode 120000 rust/hw/char/pl011/build.rs
 create mode 100644 rust/hw/char/pl011/src/bindings.rs

diff --git a/rust/hw/char/pl011/wrapper.h b/rust/hw/char/pl011/wrapper.h
new file mode 100644
index 00000000000..87a5a589c8e
--- /dev/null
+++ b/rust/hw/char/pl011/wrapper.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2024 Linaro Ltd.
+ *
+ * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+  memory_order_relaxed,
+  memory_order_consume,
+  memory_order_acquire,
+  memory_order_release,
+  memory_order_acq_rel,
+  memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+#include "hw/char/pl011.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index c407029afed..2018d13fbf5 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -149,7 +149,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_api",
  "qemu_macros",
  "qom",
  "system",
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 9e451fc0aa8..285d25c2178 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -24,7 +24,6 @@ qom = { path = "../../../qom" }
 chardev = { path = "../../../chardev" }
 system = { path = "../../../system" }
 hwcore = { path = "../../../hw/core" }
-qemu_api = { path = "../../../qemu-api" }
 qemu_macros = { path = "../../../qemu-macros" }
 
 [lints]
diff --git a/rust/hw/char/pl011/build.rs b/rust/hw/char/pl011/build.rs
new file mode 120000
index 00000000000..5f5060db356
--- /dev/null
+++ b/rust/hw/char/pl011/build.rs
@@ -0,0 +1 @@
+../../../util/build.rs
\ No newline at end of file
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index bad6a839c39..a14993f6921 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -1,6 +1,30 @@
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_libpl011_bindings_inc_rs = rust.bindgen(
+  input: 'wrapper.h',
+  dependencies: common_ss.all_dependencies(),
+  output: 'bindings.inc.rs',
+  include_directories: bindings_incdir,
+  bindgen_version: ['>=0.60.0'],
+  args: bindgen_args_common,
+)
+
 _libpl011_rs = static_library(
   'pl011',
-  files('src/lib.rs'),
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/bindings.rs',
+      'src/device.rs',
+      'src/registers.rs',
+    ],
+    {'.' : _libpl011_bindings_inc_rs},
+  ),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   dependencies: [
@@ -8,7 +32,6 @@ _libpl011_rs = static_library(
     bilge_impl_rs,
     bits_rs,
     common_rs,
-    qemu_api_rs,
     util_rs,
     migration_rs,
     bql_rs,
diff --git a/rust/hw/char/pl011/src/bindings.rs b/rust/hw/char/pl011/src/bindings.rs
new file mode 100644
index 00000000000..bd5ea840cb2
--- /dev/null
+++ b/rust/hw/char/pl011/src/bindings.rs
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    unnecessary_transmutes,
+    unsafe_op_in_unsafe_fn,
+    clippy::pedantic,
+    clippy::restriction,
+    clippy::style,
+    clippy::missing_const_for_fn,
+    clippy::ptr_offset_with_cast,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+    clippy::too_many_arguments
+)]
+
+//! `bindgen`-generated declarations.
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 3010b6d9839..85626a969d4 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -130,7 +130,7 @@ pub struct PL011State {
 // structs, so the size of the Rust version must not be any larger
 // than the size of the C one. If this assert triggers you need to
 // expand the padding_for_rust[] array in the C PL011State struct.
-static_assert!(size_of::<PL011State>() <= size_of::<qemu_api::bindings::PL011State>());
+static_assert!(size_of::<PL011State>() <= size_of::<crate::bindings::PL011State>());
 
 qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
 
diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs
index 2b70d2ff560..0c19b708c0a 100644
--- a/rust/hw/char/pl011/src/lib.rs
+++ b/rust/hw/char/pl011/src/lib.rs
@@ -12,6 +12,7 @@
 //! See [`PL011State`](crate::device::PL011State) for the device model type and
 //! the [`registers`] module for register types.
 
+mod bindings;
 mod device;
 mod registers;
 
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 28/33] rust: repurpose qemu_api -> tests
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (25 preceding siblings ...)
  2025-09-08 10:49 ` [PATCH 27/33] rust/pl011: drop dependency on qemu_api Paolo Bonzini
@ 2025-09-08 10:50 ` Paolo Bonzini
  2025-09-12  8:57   ` Zhao Liu
  2025-09-08 10:50 ` [PATCH 29/33] rust: re-export qemu_macros internal helper in "bits" Paolo Bonzini
                   ` (6 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

The crate purpose is only to provide integration tests at this point,
that can't easily be moved to a specific crate.

It's also often a good practice to have a single integration test crate
(see for ex https://github.com/rust-lang/cargo/issues/4867)

Drop README.md, use docs/devel/rust.rst instead.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-20-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS                                   |  2 +-
 rust/qemu-api/wrapper.h                       | 53 -------------
 rust/Cargo.lock                               | 30 ++++----
 rust/Cargo.toml                               |  2 +-
 rust/meson.build                              |  4 +-
 rust/qemu-api/.gitignore                      |  2 -
 rust/qemu-api/README.md                       | 19 -----
 rust/qemu-api/build.rs                        |  1 -
 rust/qemu-api/meson.build                     | 75 -------------------
 rust/qemu-api/src/bindings.rs                 | 33 --------
 rust/qemu-api/src/lib.rs                      | 18 -----
 rust/qemu-api/src/prelude.rs                  |  5 --
 rust/{qemu-api => tests}/Cargo.toml           |  7 +-
 rust/tests/meson.build                        | 14 ++++
 .../tests/vmstate_tests.rs                    |  0
 15 files changed, 35 insertions(+), 230 deletions(-)
 delete mode 100644 rust/qemu-api/wrapper.h
 delete mode 100644 rust/qemu-api/.gitignore
 delete mode 100644 rust/qemu-api/README.md
 delete mode 120000 rust/qemu-api/build.rs
 delete mode 100644 rust/qemu-api/meson.build
 delete mode 100644 rust/qemu-api/src/bindings.rs
 delete mode 100644 rust/qemu-api/src/lib.rs
 delete mode 100644 rust/qemu-api/src/prelude.rs
 rename rust/{qemu-api => tests}/Cargo.toml (78%)
 create mode 100644 rust/tests/meson.build
 rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 92cf6929bd8..de4f858df68 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3518,11 +3518,11 @@ F: rust/chardev/
 F: rust/common/
 F: rust/hw/core/
 F: rust/migration/
-F: rust/qemu-api
 F: rust/qemu-macros/
 F: rust/qom/
 F: rust/rustfmt.toml
 F: rust/system/
+F: rust/tests/
 F: rust/util/
 F: scripts/get-wraps-from-cargo-registry.py
 
diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h
deleted file mode 100644
index 7c9c20b14fe..00000000000
--- a/rust/qemu-api/wrapper.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2024 Linaro Ltd.
- *
- * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-/*
- * This header file is meant to be used as input to the `bindgen` application
- * in order to generate C FFI compatible Rust bindings.
- */
-
-#ifndef __CLANG_STDATOMIC_H
-#define __CLANG_STDATOMIC_H
-/*
- * Fix potential missing stdatomic.h error in case bindgen does not insert the
- * correct libclang header paths on its own. We do not use stdatomic.h symbols
- * in QEMU code, so it's fine to declare dummy types instead.
- */
-typedef enum memory_order {
-  memory_order_relaxed,
-  memory_order_consume,
-  memory_order_acquire,
-  memory_order_release,
-  memory_order_acq_rel,
-  memory_order_seq_cst,
-} memory_order;
-#endif /* __CLANG_STDATOMIC_H */
-
-#include "qemu/osdep.h"
-#include "qemu-io.h"
-#include "exec/memattrs.h"
-#include "hw/char/pl011.h"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 2018d13fbf5..ac79c6a34a9 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -187,21 +187,6 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "qemu_api"
-version = "0.1.0"
-dependencies = [
- "bql",
- "chardev",
- "common",
- "hwcore",
- "migration",
- "qemu_macros",
- "qom",
- "system",
- "util",
-]
-
 [[package]]
 name = "qemu_macros"
 version = "0.1.0"
@@ -252,6 +237,21 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "tests"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "chardev",
+ "common",
+ "hwcore",
+ "migration",
+ "qemu_macros",
+ "qom",
+ "system",
+ "util",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index b2a5c230fa2..d8183c614d4 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -6,13 +6,13 @@ members = [
     "common",
     "migration",
     "qemu-macros",
-    "qemu-api",
     "qom",
     "system",
     "hw/core",
     "hw/char/pl011",
     "hw/timer/hpet",
     "util",
+    "tests",
 ]
 
 [workspace.package]
diff --git a/rust/meson.build b/rust/meson.build
index 9f6a0b161d2..bd9b9cb83ea 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -18,8 +18,6 @@ quote_rs_native = dependency('quote-1-rs', native: true)
 syn_rs_native = dependency('syn-2-rs', native: true)
 proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true)
 
-qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true)
-
 genrs = []
 
 subdir('common')
@@ -32,7 +30,7 @@ subdir('qom')
 subdir('system')
 subdir('chardev')
 subdir('hw/core')
-subdir('qemu-api')
+subdir('tests')
 
 subdir('hw')
 
diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore
deleted file mode 100644
index df6c2163e03..00000000000
--- a/rust/qemu-api/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Ignore generated bindings file overrides.
-/src/bindings.inc.rs
diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md
deleted file mode 100644
index ed1b7ab263d..00000000000
--- a/rust/qemu-api/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# QEMU bindings and API wrappers
-
-This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs.
-
-The C bindings can be generated with `bindgen`, using this build target:
-
-```console
-$ make bindings.inc.rs
-```
-
-## Generate Rust documentation
-
-Common Cargo tasks can be performed from the QEMU build directory
-
-```console
-$ make clippy
-$ make rustfmt
-$ make rustdoc
-```
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
deleted file mode 120000
index 71a3167885c..00000000000
--- a/rust/qemu-api/build.rs
+++ /dev/null
@@ -1 +0,0 @@
-../util/build.rs
\ No newline at end of file
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
deleted file mode 100644
index fe81f16d990..00000000000
--- a/rust/qemu-api/meson.build
+++ /dev/null
@@ -1,75 +0,0 @@
-_qemu_api_cfg = run_command(rustc_args,
-  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
-  capture: true, check: true).stdout().strip().splitlines()
-
-c_enums = [
-  'MemoryDeviceInfoKind',
-]
-_qemu_api_bindgen_args = []
-foreach enum : c_enums
-  _qemu_api_bindgen_args += ['--rustified-enum', enum]
-endforeach
-
-blocked_type = [
-  'Chardev',
-  'Error',
-  'MemTxAttrs',
-  'MemoryRegion',
-  'ObjectClass',
-  'VMStateDescription',
-  'device_endian',
-]
-foreach type: blocked_type
-  _qemu_api_bindgen_args += ['--blocklist-type', type]
-endforeach
-
-# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
-#
-# Rust bindings generation with `bindgen` might fail in some cases where the
-# detected `libclang` does not match the expected `clang` version/target. In
-# this case you must pass the path to `clang` and `libclang` to your build
-# command invocation using the environment variables CLANG_PATH and
-# LIBCLANG_PATH
-_qemu_api_bindings_inc_rs = rust.bindgen(
-  input: 'wrapper.h',
-  dependencies: common_ss.all_dependencies(),
-  output: 'bindings.inc.rs',
-  include_directories: bindings_incdir,
-  bindgen_version: ['>=0.60.0'],
-  args: bindgen_args_common + _qemu_api_bindgen_args,
-  )
-
-_qemu_api_rs = static_library(
-  'qemu_api',
-  structured_sources(
-    [
-      'src/lib.rs',
-      'src/bindings.rs',
-      'src/prelude.rs',
-    ],
-    {'.' : _qemu_api_bindings_inc_rs},
-  ),
-  override_options: ['rust_std=2021', 'build.rust_std=2021'],
-  rust_abi: 'rust',
-  rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_macros,
-                 qom_rs, system_rs, util_rs, hwcore],
-)
-
-qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
-  dependencies: [qemu_macros, qom, hwcore, chardev, migration])
-
-test('rust-qemu-api-integration',
-    executable(
-        'rust-qemu-api-integration',
-        files('tests/vmstate_tests.rs'),
-        override_options: ['rust_std=2021', 'build.rust_std=2021'],
-        rust_args: ['--test'],
-        install: false,
-        dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api_rs]),
-    args: [
-        '--test', '--test-threads', '1',
-        '--format', 'pretty',
-    ],
-    protocol: 'rust',
-    suite: ['unit', 'rust'])
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
deleted file mode 100644
index 9c863e9b5b4..00000000000
--- a/rust/qemu-api/src/bindings.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-#![allow(
-    dead_code,
-    improper_ctypes_definitions,
-    improper_ctypes,
-    non_camel_case_types,
-    non_snake_case,
-    non_upper_case_globals,
-    unnecessary_transmutes,
-    unsafe_op_in_unsafe_fn,
-    clippy::pedantic,
-    clippy::restriction,
-    clippy::style,
-    clippy::missing_const_for_fn,
-    clippy::ptr_offset_with_cast,
-    clippy::useless_transmute,
-    clippy::missing_safety_doc,
-    clippy::too_many_arguments
-)]
-
-//! `bindgen`-generated declarations.
-
-use chardev::bindings::Chardev;
-use migration::bindings::VMStateDescription;
-use qom::bindings::ObjectClass;
-use system::bindings::{device_endian, MemTxAttrs, MemoryRegion};
-use util::bindings::Error;
-
-#[cfg(MESON)]
-include!("bindings.inc.rs");
-
-#[cfg(not(MESON))]
-include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
deleted file mode 100644
index 21b886035f3..00000000000
--- a/rust/qemu-api/src/lib.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
-#![deny(clippy::missing_const_for_fn)]
-
-#[rustfmt::skip]
-pub mod bindings;
-
-// preserve one-item-per-"use" syntax, it is clearer
-// for prelude-like modules
-#[rustfmt::skip]
-pub mod prelude;
-
-// Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
-// crate).
-extern crate self as qemu_api;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
deleted file mode 100644
index 8db56f9f817..00000000000
--- a/rust/qemu-api/src/prelude.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2024 Red Hat, Inc.
-// Author(s): Paolo Bonzini <pbonzini@redhat.com>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-//! Commonly used traits and types for QEMU.
diff --git a/rust/qemu-api/Cargo.toml b/rust/tests/Cargo.toml
similarity index 78%
rename from rust/qemu-api/Cargo.toml
rename to rust/tests/Cargo.toml
index 9abb88aa1f8..8d106d896d7 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/tests/Cargo.toml
@@ -1,12 +1,11 @@
 [package]
-name = "qemu_api"
+name = "tests"
 version = "0.1.0"
-authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
-description = "Rust bindings for QEMU"
-readme = "README.md"
+description = "Rust integration tests for QEMU"
 resolver = "2"
 publish = false
 
+authors.workspace = true
 edition.workspace = true
 homepage.workspace = true
 license.workspace = true
diff --git a/rust/tests/meson.build b/rust/tests/meson.build
new file mode 100644
index 00000000000..00688c66fb1
--- /dev/null
+++ b/rust/tests/meson.build
@@ -0,0 +1,14 @@
+test('rust-integration',
+    executable(
+        'rust-integration',
+        files('tests/vmstate_tests.rs'),
+        override_options: ['rust_std=2021', 'build.rust_std=2021'],
+        rust_args: ['--test'],
+        install: false,
+        dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs]),
+    args: [
+        '--test', '--test-threads', '1',
+        '--format', 'pretty',
+    ],
+    protocol: 'rust',
+    suite: ['unit', 'rust'])
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/tests/tests/vmstate_tests.rs
similarity index 100%
rename from rust/qemu-api/tests/vmstate_tests.rs
rename to rust/tests/tests/vmstate_tests.rs
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 29/33] rust: re-export qemu_macros internal helper in "bits"
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (26 preceding siblings ...)
  2025-09-08 10:50 ` [PATCH 28/33] rust: repurpose qemu_api -> tests Paolo Bonzini
@ 2025-09-08 10:50 ` Paolo Bonzini
  2025-09-12  9:01   ` Zhao Liu
  2025-09-08 10:50 ` [PATCH 30/33] rust: re-export qemu macros from common/qom/hwcore Paolo Bonzini
                   ` (5 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Avoid the need to import "qemu_macros".

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-21-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/bits/src/lib.rs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs
index 1bc882fde18..d1141f7c882 100644
--- a/rust/bits/src/lib.rs
+++ b/rust/bits/src/lib.rs
@@ -380,14 +380,17 @@ fn from(x: $type) -> Self {
     };
 
     { $type:ty: $expr:expr } => {
-        ::qemu_macros::bits_const_internal! { $type @ ($expr) }
+        $crate::bits_const_internal! { $type @ ($expr) }
     };
 
     { $type:ty as $int_type:ty: $expr:expr } => {
-        (::qemu_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
+        ($crate::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
     };
 }
 
+#[doc(hidden)]
+pub use qemu_macros::bits_const_internal;
+
 #[cfg(test)]
 mod test {
     bits! {
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 30/33] rust: re-export qemu macros from common/qom/hwcore
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (27 preceding siblings ...)
  2025-09-08 10:50 ` [PATCH 29/33] rust: re-export qemu_macros internal helper in "bits" Paolo Bonzini
@ 2025-09-08 10:50 ` Paolo Bonzini
  2025-09-12  9:03   ` Zhao Liu
  2025-09-08 10:50 ` [PATCH 31/33] docs: update rust.rst Paolo Bonzini
                   ` (4 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This is just a bit nicer.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-22-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/devel/rust.rst                 | 2 +-
 rust/Cargo.lock                     | 8 +-------
 rust/chardev/Cargo.toml             | 1 -
 rust/chardev/meson.build            | 2 +-
 rust/chardev/src/chardev.rs         | 2 +-
 rust/common/Cargo.toml              | 1 +
 rust/common/meson.build             | 2 +-
 rust/common/src/lib.rs              | 2 ++
 rust/common/src/opaque.rs           | 4 +---
 rust/hw/char/pl011/Cargo.toml       | 1 -
 rust/hw/char/pl011/meson.build      | 1 -
 rust/hw/char/pl011/src/device.rs    | 4 ++--
 rust/hw/char/pl011/src/registers.rs | 2 +-
 rust/hw/core/Cargo.toml             | 2 +-
 rust/hw/core/meson.build            | 2 +-
 rust/hw/core/src/irq.rs             | 2 +-
 rust/hw/core/src/lib.rs             | 1 +
 rust/hw/core/src/qdev.rs            | 4 ++--
 rust/hw/core/src/sysbus.rs          | 2 +-
 rust/hw/core/tests/tests.rs         | 4 ++--
 rust/hw/timer/hpet/Cargo.toml       | 1 -
 rust/hw/timer/hpet/meson.build      | 1 -
 rust/hw/timer/hpet/src/device.rs    | 6 +++---
 rust/meson.build                    | 3 ++-
 rust/migration/Cargo.toml           | 1 -
 rust/qom/src/lib.rs                 | 2 ++
 rust/qom/src/qom.rs                 | 4 ++--
 rust/system/Cargo.toml              | 1 -
 rust/system/meson.build             | 2 +-
 rust/system/src/memory.rs           | 2 +-
 rust/tests/Cargo.toml               | 1 -
 rust/util/Cargo.toml                | 1 -
 rust/util/meson.build               | 2 +-
 rust/util/src/timer.rs              | 4 ++--
 34 files changed, 35 insertions(+), 45 deletions(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 20d15347def..29eb48af35b 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -278,7 +278,7 @@ a raw pointer, for use in calls to C functions.  It can be used for
 example as follows::
 
     #[repr(transparent)]
-    #[derive(Debug, qemu_api_macros::Wrapper)]
+    #[derive(Debug, common::Wrapper)]
     pub struct Object(Opaque<bindings::Object>);
 
 where the special ``derive`` macro provides useful methods such as
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index ac79c6a34a9..eea928621a7 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -58,7 +58,6 @@ dependencies = [
  "bql",
  "common",
  "migration",
- "qemu_macros",
  "qom",
  "util",
 ]
@@ -68,6 +67,7 @@ name = "common"
 version = "0.1.0"
 dependencies = [
  "libc",
+ "qemu_macros",
 ]
 
 [[package]]
@@ -93,7 +93,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_macros",
  "qom",
  "system",
  "util",
@@ -133,7 +132,6 @@ name = "migration"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_macros",
  "util",
 ]
 
@@ -149,7 +147,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_macros",
  "qom",
  "system",
  "util",
@@ -232,7 +229,6 @@ name = "system"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_macros",
  "qom",
  "util",
 ]
@@ -246,7 +242,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_macros",
  "qom",
  "system",
  "util",
@@ -266,7 +261,6 @@ dependencies = [
  "common",
  "foreign",
  "libc",
- "qemu_macros",
 ]
 
 [[package]]
diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml
index c1391773078..3e77972546e 100644
--- a/rust/chardev/Cargo.toml
+++ b/rust/chardev/Cargo.toml
@@ -18,7 +18,6 @@ bql = { path = "../bql" }
 migration = { path = "../migration" }
 qom = { path = "../qom" }
 util = { path = "../util" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build
index a2fa3268d21..370895c111f 100644
--- a/rust/chardev/meson.build
+++ b/rust/chardev/meson.build
@@ -38,4 +38,4 @@ _chardev_rs = static_library(
   dependencies: [common_rs, qemu_macros],
 )
 
-chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev, qemuutil])
+chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil])
diff --git a/rust/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs
index cb6f99398eb..2014479674f 100644
--- a/rust/chardev/src/chardev.rs
+++ b/rust/chardev/src/chardev.rs
@@ -26,7 +26,7 @@
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
-#[derive(qemu_macros::Wrapper)]
+#[derive(common::Wrapper)]
 pub struct Chardev(Opaque<bindings::Chardev>);
 
 pub type ChardevClass = bindings::ChardevClass;
diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml
index 5e106427e80..0e1b4fc5050 100644
--- a/rust/common/Cargo.toml
+++ b/rust/common/Cargo.toml
@@ -14,6 +14,7 @@ rust-version.workspace = true
 
 [dependencies]
 libc.workspace = true
+qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/common/meson.build b/rust/common/meson.build
index 230a967760d..b805e0faf57 100644
--- a/rust/common/meson.build
+++ b/rust/common/meson.build
@@ -19,7 +19,7 @@ _common_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _common_cfg,
-  dependencies: [libc_rs],
+  dependencies: [libc_rs, qemu_macros],
 )
 
 common_rs = declare_dependency(link_with: [_common_rs])
diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs
index 25216503aab..8311bf945da 100644
--- a/rust/common/src/lib.rs
+++ b/rust/common/src/lib.rs
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+pub use qemu_macros::{TryInto, Wrapper};
+
 pub mod assertions;
 
 pub mod bitops;
diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs
index 3b3263acaa3..c941fb45462 100644
--- a/rust/common/src/opaque.rs
+++ b/rust/common/src/opaque.rs
@@ -192,7 +192,7 @@ pub unsafe fn new() -> Self {
 
 /// Annotates [`Self`] as a transparent wrapper for another type.
 ///
-/// Usually defined via the [`qemu_macros::Wrapper`] derive macro.
+/// Usually defined via the [`crate::Wrapper`] derive macro.
 ///
 /// # Examples
 ///
@@ -227,8 +227,6 @@ pub unsafe fn new() -> Self {
 /// ```
 ///
 /// They are not defined here to allow them to be `const`.
-///
-/// [`qemu_macros::Wrapper`]: ../../qemu_macros/derive.Wrapper.html
 pub unsafe trait Wrapper {
     type Wrapped;
 }
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 285d25c2178..b2418abc4bf 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -24,7 +24,6 @@ qom = { path = "../../../qom" }
 chardev = { path = "../../../chardev" }
 system = { path = "../../../system" }
 hwcore = { path = "../../../hw/core" }
-qemu_macros = { path = "../../../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index a14993f6921..628a5238702 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -35,7 +35,6 @@ _libpl011_rs = static_library(
     util_rs,
     migration_rs,
     bql_rs,
-    qemu_macros,
     qom_rs,
     chardev_rs,
     system_rs,
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 85626a969d4..1b4587d5f60 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -97,7 +97,7 @@ pub struct PL011Registers {
 }
 
 #[repr(C)]
-#[derive(qemu_macros::Object, qemu_macros::Device)]
+#[derive(qom::Object, hwcore::Device)]
 /// PL011 Device Model in QEMU
 pub struct PL011State {
     pub parent_obj: ParentField<SysBusDevice>,
@@ -683,7 +683,7 @@ pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError>
 }
 
 #[repr(C)]
-#[derive(qemu_macros::Object, qemu_macros::Device)]
+#[derive(qom::Object, hwcore::Device)]
 /// PL011 Luminary device model.
 pub struct PL011Luminary {
     parent_obj: ParentField<PL011State>,
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index a1c41347ed5..0c3a4d7d214 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -16,7 +16,7 @@
 #[doc(alias = "offset")]
 #[allow(non_camel_case_types)]
 #[repr(u64)]
-#[derive(Debug, Eq, PartialEq, qemu_macros::TryInto)]
+#[derive(Debug, Eq, PartialEq, common::TryInto)]
 pub enum RegisterOffset {
     /// Data Register
     ///
diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml
index 0eb9ffee263..9a9aa517082 100644
--- a/rust/hw/core/Cargo.toml
+++ b/rust/hw/core/Cargo.toml
@@ -13,6 +13,7 @@ repository.workspace = true
 rust-version.workspace = true
 
 [dependencies]
+qemu_macros = { path = "../../qemu-macros" }
 common = { path = "../../common" }
 bql = { path = "../../bql" }
 qom = { path = "../../qom" }
@@ -20,7 +21,6 @@ chardev = { path = "../../chardev" }
 migration = { path = "../../migration" }
 system = { path = "../../system" }
 util = { path = "../../util" }
-qemu_macros = { path = "../../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
index 67eacf854fe..81d8c77f9ad 100644
--- a/rust/hw/core/meson.build
+++ b/rust/hw/core/meson.build
@@ -71,7 +71,7 @@ test('rust-hwcore-rs-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_macros, util_rs]),
+        dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs
index d8d964cad27..e0d7784d97b 100644
--- a/rust/hw/core/src/irq.rs
+++ b/rust/hw/core/src/irq.rs
@@ -18,7 +18,7 @@
 
 /// An opaque wrapper around [`bindings::IRQState`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct IRQState(Opaque<bindings::IRQState>);
 
 /// Interrupt sources are used by devices to pass changes to a value (typically
diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs
index c5588d9bc27..b40801eb843 100644
--- a/rust/hw/core/src/lib.rs
+++ b/rust/hw/core/src/lib.rs
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+pub use qemu_macros::Device;
 pub use qom;
 
 pub mod bindings;
diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs
index c9faf44a71d..71b9ef141c3 100644
--- a/rust/hw/core/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -23,7 +23,7 @@
 
 /// A safe wrapper around [`bindings::Clock`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct Clock(Opaque<bindings::Clock>);
 
 unsafe impl Send for Clock {}
@@ -31,7 +31,7 @@ unsafe impl Sync for Clock {}
 
 /// A safe wrapper around [`bindings::DeviceState`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct DeviceState(Opaque<bindings::DeviceState>);
 
 unsafe impl Send for DeviceState {}
diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
index 92c7449b802..282315fce99 100644
--- a/rust/hw/core/src/sysbus.rs
+++ b/rust/hw/core/src/sysbus.rs
@@ -19,7 +19,7 @@
 
 /// A safe wrapper around [`bindings::SysBusDevice`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct SysBusDevice(Opaque<bindings::SysBusDevice>);
 
 unsafe impl Send for SysBusDevice {}
diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs
index 2f08b8f3bfe..247d812866d 100644
--- a/rust/hw/core/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -17,7 +17,7 @@
     .build();
 
 #[repr(C)]
-#[derive(qemu_macros::Object, qemu_macros::Device)]
+#[derive(qom::Object, hwcore::Device)]
 pub struct DummyState {
     parent: ParentField<DeviceState>,
     #[property(rename = "migrate-clk", default = true)]
@@ -54,7 +54,7 @@ impl DeviceImpl for DummyState {
 }
 
 #[repr(C)]
-#[derive(qemu_macros::Object, qemu_macros::Device)]
+#[derive(qom::Object, hwcore::Device)]
 pub struct DummyChildState {
     parent: ParentField<DummyState>,
 }
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 08bf97af553..f781b28d8b3 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -17,7 +17,6 @@ migration = { path = "../../../migration" }
 bql = { path = "../../../bql" }
 qom = { path = "../../../qom" }
 system = { path = "../../../system" }
-qemu_macros = { path = "../../../qemu-macros" }
 hwcore = { path = "../../../hw/core" }
 
 [lints]
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index 8ab26630d95..b6bb9477f0c 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -8,7 +8,6 @@ _libhpet_rs = static_library(
     util_rs,
     migration_rs,
     bql_rs,
-    qemu_macros,
     qom_rs,
     system_rs,
     hwcore_rs,
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 07e0f639fc4..3cfbe9c32bb 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -97,7 +97,7 @@
 /// Timer N Interrupt Routing Capability (bits 32:63)
 const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32;
 
-#[derive(qemu_macros::TryInto)]
+#[derive(common::TryInto)]
 #[repr(u64)]
 #[allow(non_camel_case_types)]
 /// Timer registers, masked by 0x18
@@ -110,7 +110,7 @@ enum TimerRegister {
     ROUTE = 16,
 }
 
-#[derive(qemu_macros::TryInto)]
+#[derive(common::TryInto)]
 #[repr(u64)]
 #[allow(non_camel_case_types)]
 /// Global registers
@@ -520,7 +520,7 @@ fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) {
 
 /// HPET Event Timer Block Abstraction
 #[repr(C)]
-#[derive(qemu_macros::Object)]
+#[derive(qom::Object)]
 pub struct HPETState {
     parent_obj: ParentField<SysBusDevice>,
     iomem: MemoryRegion,
diff --git a/rust/meson.build b/rust/meson.build
index bd9b9cb83ea..c7bd6aba45f 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -20,8 +20,9 @@ proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true)
 
 genrs = []
 
-subdir('common')
 subdir('qemu-macros')
+
+subdir('common')
 subdir('bits')
 subdir('util')
 subdir('migration')
diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml
index 66af81e0a35..708bfaaa682 100644
--- a/rust/migration/Cargo.toml
+++ b/rust/migration/Cargo.toml
@@ -15,7 +15,6 @@ rust-version.workspace = true
 [dependencies]
 common = { path = "../common" }
 util = { path = "../util" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs
index 204c6fea2ff..24c44fc2afb 100644
--- a/rust/qom/src/lib.rs
+++ b/rust/qom/src/lib.rs
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+pub use qemu_macros::Object;
+
 pub mod bindings;
 
 // preserve one-item-per-"use" syntax, it is clearer
diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs
index 2cd1d850112..5808051cd77 100644
--- a/rust/qom/src/qom.rs
+++ b/rust/qom/src/qom.rs
@@ -112,7 +112,7 @@
 
 /// A safe wrapper around [`bindings::Object`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct Object(Opaque<bindings::Object>);
 
 unsafe impl Send for Object {}
@@ -173,7 +173,7 @@ fn as_ref(&self) -> &$parent {
 ///
 /// ```ignore
 /// #[repr(C)]
-/// #[derive(qemu_macros::Object)]
+/// #[derive(qom::Object)]
 /// pub struct MyDevice {
 ///     parent: ParentField<DeviceState>,
 ///     ...
diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
index d8338c8348d..7fd369b9e32 100644
--- a/rust/system/Cargo.toml
+++ b/rust/system/Cargo.toml
@@ -16,7 +16,6 @@ rust-version.workspace = true
 common = { path = "../common" }
 qom = { path = "../qom" }
 util = { path = "../util" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/system/meson.build b/rust/system/meson.build
index 9f88166f3d3..3ec140de018 100644
--- a/rust/system/meson.build
+++ b/rust/system/meson.build
@@ -39,4 +39,4 @@ _system_rs = static_library(
 )
 
 system_rs = declare_dependency(link_with: [_system_rs],
-  dependencies: [qemu_macros, hwcore])
+  dependencies: [hwcore])
diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs
index 7312f809f51..02aa3af7b13 100644
--- a/rust/system/src/memory.rs
+++ b/rust/system/src/memory.rs
@@ -129,7 +129,7 @@ fn default() -> Self {
 
 /// A safe wrapper around [`bindings::MemoryRegion`].
 #[repr(transparent)]
-#[derive(qemu_macros::Wrapper)]
+#[derive(common::Wrapper)]
 pub struct MemoryRegion(Opaque<bindings::MemoryRegion>);
 
 unsafe impl Send for MemoryRegion {}
diff --git a/rust/tests/Cargo.toml b/rust/tests/Cargo.toml
index 8d106d896d7..d47dc3314d8 100644
--- a/rust/tests/Cargo.toml
+++ b/rust/tests/Cargo.toml
@@ -19,7 +19,6 @@ hwcore = { path = "../hw/core" }
 migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
-qemu_macros = { path = "../qemu-macros" }
 qom = { path = "../qom" }
 system = { path = "../system" }
 
diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml
index 18e6619ca06..1f6767ed9d1 100644
--- a/rust/util/Cargo.toml
+++ b/rust/util/Cargo.toml
@@ -17,7 +17,6 @@ anyhow = { workspace = true }
 foreign = { workspace = true }
 libc = { workspace = true }
 common = { path = "../common" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/util/meson.build b/rust/util/meson.build
index 197872c9b23..87a893673d2 100644
--- a/rust/util/meson.build
+++ b/rust/util/meson.build
@@ -39,7 +39,7 @@ _util_rs = static_library(
   ),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
-  dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_macros, qom, qemuutil],
+  dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qom, qemuutil],
 )
 
 util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom])
diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs
index 622b6ee3097..c6b3e4088ec 100644
--- a/rust/util/src/timer.rs
+++ b/rust/util/src/timer.rs
@@ -15,14 +15,14 @@
 
 /// A safe wrapper around [`bindings::QEMUTimer`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct Timer(Opaque<bindings::QEMUTimer>);
 
 unsafe impl Send for Timer {}
 unsafe impl Sync for Timer {}
 
 #[repr(transparent)]
-#[derive(qemu_macros::Wrapper)]
+#[derive(common::Wrapper)]
 pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);
 
 unsafe impl Send for TimerListGroup {}
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 31/33] docs: update rust.rst
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (28 preceding siblings ...)
  2025-09-08 10:50 ` [PATCH 30/33] rust: re-export qemu macros from common/qom/hwcore Paolo Bonzini
@ 2025-09-08 10:50 ` Paolo Bonzini
  2025-09-12  9:13   ` Zhao Liu
  2025-09-08 10:50 ` [PATCH 32/33] rust: meson: remove unnecessary complication in device crates Paolo Bonzini
                   ` (3 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-23-marcandre.lureau@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/devel/rust.rst | 61 ++++++++++++++++++++++++---------------------
 1 file changed, 32 insertions(+), 29 deletions(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 29eb48af35b..13a20e86a16 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -103,15 +103,18 @@ anymore.
 Writing Rust code in QEMU
 -------------------------
 
-QEMU includes four crates:
+QEMU includes several crates:
 
-* ``qemu_api`` for bindings to C code and useful functionality
+* ``common`` provides Rust-only utilities
 
-* ``qemu_api_macros`` defines several procedural macros that are useful when
+* ``bql``, ``chardev``, ``hw/core``, ``migration``, ``qom``, ``system``,
+  ``util`` for bindings to respective QEMU C library APIs
+
+* ``qemu_macros`` defines several procedural macros that are useful when
   writing C code
 
 * ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``)
-  are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are
+  are sample devices that demonstrate Rust binding usage and ``qemu_macros``, and are
   used to further develop them.  These two crates are functional\ [#issues]_ replacements
   for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files.
 
@@ -124,7 +127,7 @@ This section explains how to work with them.
 Status
 ''''''
 
-Modules of ``qemu_api`` can be defined as:
+The stability of the modules can be defined as:
 
 - *complete*: ready for use in new devices; if applicable, the API supports the
   full functionality available in C
@@ -140,26 +143,26 @@ Modules of ``qemu_api`` can be defined as:
 
 The status of the modules is as follows:
 
-================ ======================
-module           status
-================ ======================
-``assertions``   stable
-``bitops``       complete
-``callbacks``    complete
-``cell``         stable
-``errno``        complete
-``error``        stable
-``irq``          complete
-``log``          proof of concept
-``memory``       stable
-``module``       complete
-``qdev``         stable
-``qom``          stable
-``sysbus``       stable
-``timer``        stable
-``vmstate``      stable
-``zeroable``     stable
-================ ======================
+========================== ======================
+module                     status
+========================== ======================
+``bql::cell``              stable
+``common::assertions``     stable
+``common::bitops``         complete
+``common::callbacks``      complete
+``common::errno``          complete
+``common::zeroable``       stable
+``hwcore::irq``            complete
+``hwcore::qdev``           stable
+``hwcore::sysbus``         stable
+``migration::vmstate``     stable
+``qom``                    stable
+``system::memory``         stable
+``util::error``            stable
+``util::log``              proof of concept
+``util::module``           complete
+``util::timer``            stable
+========================== ======================
 
 .. note::
   API stability is not a promise, if anything because the C APIs are not a stable
@@ -260,7 +263,7 @@ to go from a shared reference to a ``&mut``.
 
 Whenever C code provides you with an opaque ``void *``, avoid converting it
 to a Rust mutable reference, and use a shared reference instead.  The
-``qemu_api::cell`` module provides wrappers that can be used to tell the
+``bql::cell`` module provides wrappers that can be used to tell the
 Rust compiler about interior mutability, and optionally to enforce locking
 rules for the "Big QEMU Lock".  In the future, similar cell types might
 also be provided for ``AioContext``-based locking as well.
@@ -292,7 +295,7 @@ the wrapper to be declared thread-safe::
 Writing bindings to C code
 ''''''''''''''''''''''''''
 
-Here are some things to keep in mind when working on the ``qemu_api`` crate.
+Here are some things to keep in mind when working on the QEMU Rust crate.
 
 **Look at existing code**
   Very often, similar idioms in C code correspond to similar tricks in
@@ -355,7 +358,7 @@ from the type after ``as`` in the invocation of ``parse_macro_input!``::
             .into()
     }
 
-The ``qemu_api_macros`` crate has utility functions to examine a
+The ``qemu_macros`` crate has utility functions to examine a
 ``DeriveInput`` and perform common checks (e.g. looking for a struct
 with named fields).  These functions return ``Result<..., syn::Error>``
 and can be used easily in the procedural macro function::
@@ -396,7 +399,7 @@ Right now, only the nightly version of ``rustfmt`` is supported.  This
 might change in the future.  While CI checks for correct formatting via
 ``cargo fmt --check``, maintainers can fix this for you when applying patches.
 
-It is expected that ``qemu_api`` provides full ``rustdoc`` documentation for
+It is expected that QEMU Rust crates provides full ``rustdoc`` documentation for
 bindings that are in their final shape or close.
 
 Adding dependencies
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 32/33] rust: meson: remove unnecessary complication in device crates
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (29 preceding siblings ...)
  2025-09-08 10:50 ` [PATCH 31/33] docs: update rust.rst Paolo Bonzini
@ 2025-09-08 10:50 ` Paolo Bonzini
  2025-09-12  9:20   ` Zhao Liu
  2025-09-08 10:50 ` [PATCH 33/33] rust: do not inline do_init_io Paolo Bonzini
                   ` (2 subsequent siblings)
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:50 UTC (permalink / raw)
  To: qemu-devel

It is not necessary anymore to explicitly list procedural macro crates
when doing the final link using rustc.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/hw/char/pl011/meson.build | 3 ---
 rust/hw/timer/hpet/meson.build | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 628a5238702..ffdc8af53f1 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -44,8 +44,5 @@ _libpl011_rs = static_library(
 
 rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
   link_whole: [_libpl011_rs],
-  # Putting proc macro crates in `dependencies` is necessary for Meson to find
-  # them when compiling the root per-target static rust lib.
-  dependencies: [bilge_impl_rs, qemu_macros],
   variables: {'crate': 'pl011'},
 )])
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index b6bb9477f0c..bb64b96672e 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -16,8 +16,5 @@ _libhpet_rs = static_library(
 
 rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency(
   link_whole: [_libhpet_rs],
-  # Putting proc macro crates in `dependencies` is necessary for Meson to find
-  # them when compiling the root per-target static rust lib.
-  dependencies: [qemu_macros],
   variables: {'crate': 'hpet'},
 )])
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 33/33] rust: do not inline do_init_io
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (30 preceding siblings ...)
  2025-09-08 10:50 ` [PATCH 32/33] rust: meson: remove unnecessary complication in device crates Paolo Bonzini
@ 2025-09-08 10:50 ` Paolo Bonzini
  2025-09-12  9:21   ` Zhao Liu
       [not found] ` <20250908105005.2119297-3-pbonzini@redhat.com>
  2025-09-11 10:04 ` [PATCH 00/33] First Rust update for QEMU 10.2 Peter Maydell
  33 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-08 10:50 UTC (permalink / raw)
  To: qemu-devel

This is now possible since the hwcore integration tests do not
link the system crate anymore.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/system/src/memory.rs | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs
index 02aa3af7b13..4b3316bf767 100644
--- a/rust/system/src/memory.rs
+++ b/rust/system/src/memory.rs
@@ -136,11 +136,6 @@ unsafe impl Send for MemoryRegion {}
 unsafe impl Sync for MemoryRegion {}
 
 impl MemoryRegion {
-    // inline to ensure that it is not included in tests, which only
-    // link to hwcore and qom.  FIXME: inlining is actually the opposite
-    // of what we want, since this is the type-erased version of the
-    // init_io function below.  Look into splitting the qemu_api crate.
-    #[inline(always)]
     unsafe fn do_init_io(
         slot: *mut bindings::MemoryRegion,
         owner: *mut bindings::Object,
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu
  2025-09-08 10:49 ` [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu Paolo Bonzini
@ 2025-09-11  3:14   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11  3:14 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:49:33PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:33 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu
> X-Mailer: git-send-email 2.51.0
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  .gitlab-ci.d/buildtest.yml | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 02/33] configure: bump Meson to 1.9.0 for use with Rust
       [not found] ` <20250908105005.2119297-3-pbonzini@redhat.com>
@ 2025-09-11  3:17   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11  3:17 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:49:34PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:34 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 02/33] configure: bump Meson to 1.9.0 for use with Rust
> X-Mailer: git-send-email 2.51.0
> 
> Meson 1.9.0 provides mixed linking of Rust and C objects.  As a side effect,
> this also allows adding dependencies with "sources: ..." files to Rust crates
> that use structured_sources().
> 
> It can also clean up up the meson.build files for Rust noticeably, but due
> to an issue with doctests (see https://github.com/mesonbuild/meson/pull/14973)
> that will have to wait for 1.9.1.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  configure                                  |   4 ++--
>  python/scripts/vendor.py                   |   4 ++--
>  python/wheels/meson-1.8.1-py3-none-any.whl | Bin 1013001 -> 0 bytes
>  python/wheels/meson-1.9.0-py3-none-any.whl | Bin 0 -> 1029634 bytes
>  pythondeps.toml                            |   4 ++--
>  5 files changed, 6 insertions(+), 6 deletions(-)
>  delete mode 100644 python/wheels/meson-1.8.1-py3-none-any.whl
>  create mode 100644 python/wheels/meson-1.9.0-py3-none-any.whl

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 03/33] meson, cargo: require Rust 1.83.0
  2025-09-08 10:49 ` [PATCH 03/33] meson, cargo: require Rust 1.83.0 Paolo Bonzini
@ 2025-09-11  3:19   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11  3:19 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:49:35PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:35 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 03/33] meson, cargo: require Rust 1.83.0
> X-Mailer: git-send-email 2.51.0
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/devel/rust.rst | 14 +++++---------
>  meson.build         |  6 +++---
>  clippy.toml         |  2 +-
>  rust/Cargo.toml     |  2 +-
>  4 files changed, 10 insertions(+), 14 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 04/33] rust: add missing const markers for MSRV==1.83.0
  2025-09-08 10:49 ` [PATCH 04/33] rust: add missing const markers for MSRV==1.83.0 Paolo Bonzini
@ 2025-09-11  3:20   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11  3:20 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:49:36PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:36 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 04/33] rust: add missing const markers for MSRV==1.83.0
> X-Mailer: git-send-email 2.51.0
> 
> Rust 1.83 allows more functions to be marked const.
> Fix clippy with bumped minimum supported Rust version.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/bits/src/lib.rs            | 6 +++---
>  rust/qemu-api/src/assertions.rs | 4 ++--
>  rust/qemu-api/src/cell.rs       | 6 ++----
>  rust/qemu-api/src/qom.rs        | 6 +++---
>  rust/qemu-api/src/timer.rs      | 2 +-
>  rust/qemu-api/src/uninit.rs     | 6 +++---
>  6 files changed, 14 insertions(+), 16 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 05/33] rust: use inline const expressions
  2025-09-08 10:49 ` [PATCH 05/33] rust: use inline const expressions Paolo Bonzini
@ 2025-09-11  6:15   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11  6:15 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:49:37PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:37 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 05/33] rust: use inline const expressions
> X-Mailer: git-send-email 2.51.0
> 
> They were stabilized in Rust 1.79.0.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/devel/rust.rst            |  9 +++------
>  rust/qemu-api/src/callbacks.rs | 27 +--------------------------
>  rust/qemu-api/src/chardev.rs   |  2 +-
>  rust/qemu-api/src/qdev.rs      |  2 +-
>  rust/qemu-api/src/timer.rs     |  2 +-
>  rust/qemu-api/src/vmstate.rs   |  2 +-
>  6 files changed, 8 insertions(+), 36 deletions(-)
> 
> diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
> index e0ee4a9837d..98e3a33a3ce 100644
> --- a/docs/devel/rust.rst
> +++ b/docs/devel/rust.rst

...

> @@ -97,9 +94,9 @@ patches are welcome:
>    before QEMU can use them.  For now, there is special code in
>    ``util/error.c`` to support non-NUL-terminated file names.
>  
> -* associated const equality would be nice to have for some users of
> -  ``callbacks::FnCall``, but is still experimental.  ``ASSERT_IS_SOME``
> -  replaces it.
> +Associated const equality would be nice to have for some users of
> +``callbacks::FnCall``, but is still experimental.  Const assertions
> +are used instead.

nit: this format seems broken - this sentence should be after bullet.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 06/33] rust: add qdev Device derive macro
  2025-09-08 10:49 ` [PATCH 06/33] rust: add qdev Device derive macro Paolo Bonzini
@ 2025-09-11  6:58   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11  6:58 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Manos Pitsidianakis

On Mon, Sep 08, 2025 at 12:49:38PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:38 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 06/33] rust: add qdev Device derive macro
> X-Mailer: git-send-email 2.51.0
> 
> From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> 
> Add derive macro for declaring qdev properties directly above the field
> definitions. To do this, we split DeviceImpl::properties method on a
> separate trait so we can implement only that part in the derive macro
> expansion (we cannot partially implement the DeviceImpl trait).
> 
> Adding a `property` attribute above the field declaration will generate
> a `qemu_api::bindings::Property` array member in the device's property
> list.
> 
> Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Link: https://lore.kernel.org/r/20250711-rust-qdev-properties-v3-1-e198624416fb@linaro.org
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/hw/char/pl011/src/device.rs  |  31 +-----
>  rust/hw/timer/hpet/src/device.rs  |   6 +-
>  rust/qemu-api-macros/src/lib.rs   | 152 +++++++++++++++++++++++++++++-
>  rust/qemu-api-macros/src/tests.rs | 111 ++++++++++++++++++++++
>  rust/qemu-api/src/qdev.rs         |  70 ++++++++++++--
>  rust/qemu-api/tests/tests.rs      |  24 +----
>  6 files changed, 336 insertions(+), 58 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 00/33] First Rust update for QEMU 10.2
  2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
                   ` (32 preceding siblings ...)
       [not found] ` <20250908105005.2119297-3-pbonzini@redhat.com>
@ 2025-09-11 10:04 ` Peter Maydell
  2025-09-11 11:18   ` Marc-André Lureau
  2025-09-15 14:27   ` Paolo Bonzini
  33 siblings, 2 replies; 70+ messages in thread
From: Peter Maydell @ 2025-09-11 10:04 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, 8 Sept 2025 at 11:53, Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> This includes:
> - bumping MSRV to 1.83.0 to support const_refs_to_static
> - Zhao's safe, builder-based implementation of migration callbacks
> - Manos's qdev properties macro.  While bit-based properties are
>   not yet supported, that's a small change overall.
> - the Rust crate split from Marc-André
> - adding proc macro aliases in individual crates, also from Marc-André
>
> I'm still not convinced about having "bql" depend on "migration",
> but I am convinced by the crate split between "util" and "bql",
> so we can move the implementation of VMState from "bql" to
> "migration" later if needed.
>
> For the purpose of getting this in as an easy-to-use base for future
> development, I'm disabling CI from Debian and Ubuntu.  The plan is:
> - that Debian will require trixie to enable Rust usage
> - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions
>   (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318)
> - that Marc-André or someone else will add Rust to other CI jobs

How far into the future does moving to 1.83.0 push our
"we can enable rust and make it mandatory" point? I was
hoping we would be able to do that sometime soon but this
sounds like we're going to be still a long way out from that :-(

-- PMM


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 08/33] rust: vmstate: use const_refs_to_static
  2025-09-08 10:49 ` [PATCH 08/33] rust: vmstate: use const_refs_to_static Paolo Bonzini
@ 2025-09-11 10:07   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:07 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:49:40PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:40 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 08/33] rust: vmstate: use const_refs_to_static
> X-Mailer: git-send-email 2.51.0
> 
> The VMStateDescriptionBuilder already needs const_refs_static, so
> use it to remove the need for vmstate_clock! and vmstate_struct!,
> as well as to simplify the implementation for scalars.
> 
> If the consts in the VMState trait can reference to static
> VMStateDescription, scalars do not need the info_enum_to_ref!
> indirection and structs can implement the VMState trait themselves.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/devel/rust.rst                  |   5 -
>  rust/hw/char/pl011/src/device.rs     |  19 ++-
>  rust/hw/timer/hpet/src/device.rs     |   8 +-
>  rust/qemu-api/src/assertions.rs      |   4 -
>  rust/qemu-api/src/vmstate.rs         | 229 +++++++--------------------
>  rust/qemu-api/tests/vmstate_tests.rs |  65 +++++---
>  6 files changed, 113 insertions(+), 217 deletions(-)

Good improvement!

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 09/33] rust: qdev: const_refs_to_static
  2025-09-08 10:49 ` [PATCH 09/33] rust: qdev: const_refs_to_static Paolo Bonzini
@ 2025-09-11 10:20   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:20 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:49:41PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:41 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 09/33] rust: qdev: const_refs_to_static
> X-Mailer: git-send-email 2.51.0
> 
> Now that const_refs_static can be assumed, convert the members of
> the DeviceImpl trait from functions to constants.  This lets the
> compiler know that they have a 'static lifetime, and removes the
> need for the weird "Box::leak()".
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/hw/char/pl011/src/device.rs  |  4 +--
>  rust/hw/timer/hpet/src/device.rs  |  9 ++-----
>  rust/qemu-api-macros/src/lib.rs   |  8 +++---
>  rust/qemu-api-macros/src/tests.rs | 44 ++++++++++++++-----------------
>  rust/qemu-api/src/qdev.rs         | 19 +++++--------
>  rust/qemu-api/tests/tests.rs      |  4 +--
>  6 files changed, 33 insertions(+), 55 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 10/33] docs/rust: update msrv
  2025-09-08 10:49 ` [PATCH 10/33] docs/rust: update msrv Paolo Bonzini
@ 2025-09-11 10:21   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:21 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:42PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:42 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 10/33] docs/rust: update msrv
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-2-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/devel/rust.rst | 2 +-
>  rust/Cargo.toml     | 1 +
>  2 files changed, 2 insertions(+), 1 deletion(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 11/33] rust: remove unused global qemu "allocator"
  2025-09-08 10:49 ` [PATCH 11/33] rust: remove unused global qemu "allocator" Paolo Bonzini
@ 2025-09-11 10:21   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:21 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:43PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:43 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 11/33] rust: remove unused global qemu "allocator"
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> The global allocator has always been disabled. There is no clear reason
> Rust and C should use the same allocator. Allocations made from Rust
> must be freed by Rust, and same for C, otherwise we head into troubles.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-3-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  meson.build               |   4 --
>  rust/Cargo.toml           |   2 +-
>  rust/qemu-api/Cargo.toml  |   1 -
>  rust/qemu-api/meson.build |   1 -
>  rust/qemu-api/src/lib.rs  | 135 --------------------------------------
>  5 files changed, 1 insertion(+), 142 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 12/33] rust: add workspace authors
  2025-09-08 10:49 ` [PATCH 12/33] rust: add workspace authors Paolo Bonzini
@ 2025-09-11 10:23   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:23 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:44PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:44 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 12/33] rust: add workspace authors
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-4-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/Cargo.toml | 1 +
>  1 file changed, 1 insertion(+)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 13/33] rust: move vmstate_clock!() to qdev module
  2025-09-08 10:49 ` [PATCH 13/33] rust: move vmstate_clock!() to qdev module Paolo Bonzini
@ 2025-09-11 10:25   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:25 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:45PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:45 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 13/33] rust: move vmstate_clock!() to qdev module
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> This will allow to split vmstate to a standalone crate next.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-8-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/src/qdev.rs    |  3 +++
>  rust/qemu-api/src/vmstate.rs | 14 ++++++--------
>  2 files changed, 9 insertions(+), 8 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 14/33] rust: move VMState handling to QOM module
  2025-09-08 10:49 ` [PATCH 14/33] rust: move VMState handling to QOM module Paolo Bonzini
@ 2025-09-11 10:26   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:26 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:46PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:46 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 14/33] rust: move VMState handling to QOM module
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> This will allow to split vmstate to a standalone crate next.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-9-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/src/qom.rs     |  3 +++
>  rust/qemu-api/src/vmstate.rs | 11 +++++------
>  2 files changed, 8 insertions(+), 6 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 15/33] rust: move Cell vmstate impl
  2025-09-08 10:49 ` [PATCH 15/33] rust: move Cell vmstate impl Paolo Bonzini
@ 2025-09-11 10:31   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:31 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:47PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:47 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 15/33] rust: move Cell vmstate impl
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> This will allow to split vmstate to a standalone crate next.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-10-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/src/cell.rs    |  6 +++++-
>  rust/qemu-api/src/vmstate.rs | 14 +++++++-------
>  2 files changed, 12 insertions(+), 8 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 16/33] rust: split Rust-only "common" crate
  2025-09-08 10:49 ` [PATCH 16/33] rust: split Rust-only "common" crate Paolo Bonzini
@ 2025-09-11 10:44   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-11 10:44 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:48PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:48 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 16/33] rust: split Rust-only "common" crate
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-6-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                                 |   1 +
>  rust/Cargo.lock                             |  10 +
>  rust/Cargo.toml                             |   1 +
>  rust/common/Cargo.toml                      |  19 ++
>  rust/common/meson.build                     |  34 +++
>  rust/{qemu-api => common}/src/assertions.rs |  16 +-
>  rust/{qemu-api => common}/src/bitops.rs     |   1 -
>  rust/{qemu-api => common}/src/callbacks.rs  |  10 +-
>  rust/{qemu-api => common}/src/errno.rs      |   8 +-
>  rust/common/src/lib.rs                      |  20 ++
>  rust/common/src/opaque.rs                   | 238 ++++++++++++++++++++
>  rust/{qemu-api => common}/src/uninit.rs     |   2 +-
>  rust/common/src/zeroable.rs                 |  18 ++
>  rust/hw/char/pl011/Cargo.toml               |   1 +
>  rust/hw/char/pl011/meson.build              |   3 +-
>  rust/hw/char/pl011/src/device.rs            |   3 +-
>  rust/hw/timer/hpet/Cargo.toml               |   1 +
>  rust/hw/timer/hpet/meson.build              |   3 +-
>  rust/hw/timer/hpet/src/device.rs            |   2 +-
>  rust/hw/timer/hpet/src/fw_cfg.rs            |   3 +-
>  rust/meson.build                            |   1 +
>  rust/qemu-api-macros/src/lib.rs             |  16 +-
>  rust/qemu-api-macros/src/tests.rs           |   6 +-
>  rust/qemu-api/Cargo.toml                    |   1 +
>  rust/qemu-api/meson.build                   |  14 +-
>  rust/qemu-api/src/bindings.rs               |  21 ++
>  rust/qemu-api/src/cell.rs                   | 235 +------------------
>  rust/qemu-api/src/chardev.rs                |   5 +-
>  rust/qemu-api/src/error.rs                  |   3 +-
>  rust/qemu-api/src/irq.rs                    |   3 +-
>  rust/qemu-api/src/lib.rs                    |   6 -
>  rust/qemu-api/src/log.rs                    |   4 +-
>  rust/qemu-api/src/memory.rs                 |   5 +-
>  rust/qemu-api/src/prelude.rs                |   6 +-
>  rust/qemu-api/src/qdev.rs                   |  10 +-
>  rust/qemu-api/src/qom.rs                    |   3 +-
>  rust/qemu-api/src/sysbus.rs                 |   3 +-
>  rust/qemu-api/src/timer.rs                  |   8 +-
>  rust/qemu-api/src/vmstate.rs                |  23 +-
>  rust/qemu-api/src/zeroable.rs               |  37 ---
>  rust/qemu-api/tests/vmstate_tests.rs        |   3 +-
>  41 files changed, 448 insertions(+), 359 deletions(-)
>  create mode 100644 rust/common/Cargo.toml
>  create mode 100644 rust/common/meson.build
>  rename rust/{qemu-api => common}/src/assertions.rs (92%)
>  rename rust/{qemu-api => common}/src/bitops.rs (98%)
>  rename rust/{qemu-api => common}/src/callbacks.rs (97%)
>  rename rust/{qemu-api => common}/src/errno.rs (98%)
>  create mode 100644 rust/common/src/lib.rs
>  create mode 100644 rust/common/src/opaque.rs
>  rename rust/{qemu-api => common}/src/uninit.rs (98%)
>  create mode 100644 rust/common/src/zeroable.rs
>  delete mode 100644 rust/qemu-api/src/zeroable.rs

...

> +/// Stores an opaque value that is shared with C code.
> +///
> +/// Often, C structs can changed when calling a C function even if they are

nit typo (found in kvm forum :) ): s/can changed/can be changed/

> +/// behind a shared Rust reference, or they can be initialized lazily and have
> +/// invalid bit patterns (e.g. `3` for a [`bool`]).  This goes against Rust's
> +/// strict aliasing rules, which normally prevent mutation through shared
> +/// references.
> +///
> +/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
> +/// assume the usual constraints that Rust structs require, and allows using
> +/// shared references on the Rust side.
> +///
> +/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
> +/// of `T`.
> +#[repr(transparent)]
> +pub struct Opaque<T> {
> +    value: UnsafeCell<MaybeUninit<T>>,
> +    // PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
> +    // one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
> +    // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
> +    _pin: PhantomPinned,
> +}

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 00/33] First Rust update for QEMU 10.2
  2025-09-11 10:04 ` [PATCH 00/33] First Rust update for QEMU 10.2 Peter Maydell
@ 2025-09-11 11:18   ` Marc-André Lureau
  2025-09-12  7:42     ` Zhao Liu
  2025-09-15 14:27   ` Paolo Bonzini
  1 sibling, 1 reply; 70+ messages in thread
From: Marc-André Lureau @ 2025-09-11 11:18 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Paolo Bonzini, qemu-devel

Hi

On Thu, Sep 11, 2025 at 2:05 PM Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Mon, 8 Sept 2025 at 11:53, Paolo Bonzini <pbonzini@redhat.com> wrote:
> >
> > This includes:
> > - bumping MSRV to 1.83.0 to support const_refs_to_static
> > - Zhao's safe, builder-based implementation of migration callbacks
> > - Manos's qdev properties macro.  While bit-based properties are
> >   not yet supported, that's a small change overall.
> > - the Rust crate split from Marc-André
> > - adding proc macro aliases in individual crates, also from Marc-André
> >
> > I'm still not convinced about having "bql" depend on "migration",
> > but I am convinced by the crate split between "util" and "bql",
> > so we can move the implementation of VMState from "bql" to
> > "migration" later if needed.
> >
> > For the purpose of getting this in as an easy-to-use base for future
> > development, I'm disabling CI from Debian and Ubuntu.  The plan is:
> > - that Debian will require trixie to enable Rust usage
> > - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions
> >   (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318)
> > - that Marc-André or someone else will add Rust to other CI jobs
>
> How far into the future does moving to 1.83.0 push our
> "we can enable rust and make it mandatory" point? I was
> hoping we would be able to do that sometime soon but this
> sounds like we're going to be still a long way out from that :-(

I suppose most distro will want to backport a newer rust in their
stable distro, like ubuntu is doing or debian with rustc-web. With
that, it shouldn't be too long before we could make Rust required.

fwiw, I am working on a series to enable Rust on our various
CI/docker/vm. I am mostly left with issues from windows and macos ..
(btw, if someone can help me investigate the linking issue on mac
https://gitlab.com/marcandre.lureau/qemu/-/jobs/11301499123, perhaps a
temporary ssh somewhere?)


--
Marc-André Lureau


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 00/33] First Rust update for QEMU 10.2
  2025-09-11 11:18   ` Marc-André Lureau
@ 2025-09-12  7:42     ` Zhao Liu
  2025-09-12  8:47       ` Marc-André Lureau
  0 siblings, 1 reply; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  7:42 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Peter Maydell, Paolo Bonzini, qemu-devel

On Thu, Sep 11, 2025 at 03:18:44PM +0400, Marc-André Lureau wrote:
> Date: Thu, 11 Sep 2025 15:18:44 +0400
> From: Marc-André Lureau <marcandre.lureau@gmail.com>
> Subject: Re: [PATCH 00/33] First Rust update for QEMU 10.2
> 
> Hi
> 
> On Thu, Sep 11, 2025 at 2:05 PM Peter Maydell <peter.maydell@linaro.org> wrote:
> >
> > On Mon, 8 Sept 2025 at 11:53, Paolo Bonzini <pbonzini@redhat.com> wrote:
> > >
> > > This includes:
> > > - bumping MSRV to 1.83.0 to support const_refs_to_static
> > > - Zhao's safe, builder-based implementation of migration callbacks
> > > - Manos's qdev properties macro.  While bit-based properties are
> > >   not yet supported, that's a small change overall.
> > > - the Rust crate split from Marc-André
> > > - adding proc macro aliases in individual crates, also from Marc-André
> > >
> > > I'm still not convinced about having "bql" depend on "migration",
> > > but I am convinced by the crate split between "util" and "bql",
> > > so we can move the implementation of VMState from "bql" to
> > > "migration" later if needed.
> > >
> > > For the purpose of getting this in as an easy-to-use base for future
> > > development, I'm disabling CI from Debian and Ubuntu.  The plan is:
> > > - that Debian will require trixie to enable Rust usage
> > > - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions
> > >   (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318)
> > > - that Marc-André or someone else will add Rust to other CI jobs
> >
> > How far into the future does moving to 1.83.0 push our
> > "we can enable rust and make it mandatory" point? I was
> > hoping we would be able to do that sometime soon but this
> > sounds like we're going to be still a long way out from that :-(
> 
> I suppose most distro will want to backport a newer rust in their
> stable distro, like ubuntu is doing or debian with rustc-web. With
> that, it shouldn't be too long before we could make Rust required.
> 
> fwiw, I am working on a series to enable Rust on our various
> CI/docker/vm. I am mostly left with issues from windows and macos ..
> (btw, if someone can help me investigate the linking issue on mac
> https://gitlab.com/marcandre.lureau/qemu/-/jobs/11301499123, perhaps a
> temporary ssh somewhere?)

Just a guess...

It seems "-llibqemuutil.a" may tell the linker to search for
liblibqemuutil.a.dylib or liblibqemuutil.a.a [*] :-(

[*]: https://manp.gs/mac/1/ld#l

-Zhao



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 17/33] rust: make build.rs generic over various ./rust/projects
  2025-09-08 10:49 ` [PATCH 17/33] rust: make build.rs generic over various ./rust/projects Paolo Bonzini
@ 2025-09-12  7:43   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  7:43 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:49PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:49 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 17/33] rust: make build.rs generic over various
>  ./rust/projects
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Guess the name of the subdir from the manifest directory, instead of
> hard-coding it. In the following commits, other crates can then link to
> this file, instead of maintaining their own copy.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-5-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/build.rs | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 18/33] rust: split "util" crate
  2025-09-08 10:49 ` [PATCH 18/33] rust: split "util" crate Paolo Bonzini
@ 2025-09-12  8:00   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:00 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:50PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:50 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 18/33] rust: split "util" crate
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-7-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                           |  1 +
>  rust/qemu-api/wrapper.h               |  6 ---
>  rust/util/wrapper.h                   | 32 ++++++++++++++++
>  rust/Cargo.lock                       | 17 +++++++--
>  rust/Cargo.toml                       |  5 +--
>  rust/hw/char/pl011/Cargo.toml         |  1 +
>  rust/hw/char/pl011/meson.build        |  1 +
>  rust/hw/char/pl011/src/device.rs      |  7 ++--
>  rust/hw/timer/hpet/Cargo.toml         |  1 +
>  rust/hw/timer/hpet/meson.build        |  1 +
>  rust/hw/timer/hpet/src/device.rs      |  6 +--
>  rust/meson.build                      |  1 +
>  rust/qemu-api-macros/src/lib.rs       |  2 +-
>  rust/qemu-api-macros/src/tests.rs     |  2 +-
>  rust/qemu-api/Cargo.toml              |  4 +-
>  rust/qemu-api/build.rs                | 50 +-----------------------
>  rust/qemu-api/meson.build             | 14 ++-----
>  rust/qemu-api/src/bindings.rs         |  1 +
>  rust/qemu-api/src/lib.rs              |  6 ---
>  rust/qemu-api/src/prelude.rs          |  2 -
>  rust/qemu-api/src/qdev.rs             |  4 +-
>  rust/qemu-api/src/sysbus.rs           |  2 +-
>  rust/qemu-api/src/vmstate.rs          |  2 +-
>  rust/qemu-api/tests/tests.rs          |  2 +-
>  rust/util/Cargo.toml                  | 23 +++++++++++
>  rust/util/build.rs                    | 49 ++++++++++++++++++++++++
>  rust/util/meson.build                 | 55 +++++++++++++++++++++++++++
>  rust/util/src/bindings.rs             | 25 ++++++++++++
>  rust/{qemu-api => util}/src/error.rs  |  5 +--
>  rust/util/src/lib.rs                  |  9 +++++
>  rust/{qemu-api => util}/src/log.rs    | 12 +++---
>  rust/{qemu-api => util}/src/module.rs |  2 +-
>  rust/{qemu-api => util}/src/timer.rs  |  0
>  33 files changed, 243 insertions(+), 107 deletions(-)
>  create mode 100644 rust/util/wrapper.h
>  mode change 100644 => 120000 rust/qemu-api/build.rs
>  create mode 100644 rust/util/Cargo.toml
>  create mode 100644 rust/util/build.rs
>  create mode 100644 rust/util/meson.build
>  create mode 100644 rust/util/src/bindings.rs
>  rename rust/{qemu-api => util}/src/error.rs (98%)
>  create mode 100644 rust/util/src/lib.rs
>  rename rust/{qemu-api => util}/src/log.rs (93%)
>  rename rust/{qemu-api => util}/src/module.rs (97%)
>  rename rust/{qemu-api => util}/src/timer.rs (100%)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 19/33] rust: split "migration" crate
  2025-09-08 10:49 ` [PATCH 19/33] rust: split "migration" crate Paolo Bonzini
@ 2025-09-12  8:09   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:09 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:51PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:51 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 19/33] rust: split "migration" crate
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-11-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                                 |  1 +
>  rust/migration/wrapper.h                    | 51 ++++++++++++++++++++
>  rust/qemu-api/wrapper.h                     |  1 -
>  rust/Cargo.lock                             | 12 +++++
>  rust/Cargo.toml                             |  1 +
>  rust/hw/char/pl011/Cargo.toml               |  1 +
>  rust/hw/char/pl011/meson.build              |  1 +
>  rust/hw/char/pl011/src/device.rs            | 13 ++---
>  rust/hw/char/pl011/src/registers.rs         |  2 +-
>  rust/hw/timer/hpet/Cargo.toml               |  1 +
>  rust/hw/timer/hpet/meson.build              |  1 +
>  rust/hw/timer/hpet/src/device.rs            | 11 +++--
>  rust/meson.build                            |  1 +
>  rust/migration/Cargo.toml                   | 21 ++++++++
>  rust/migration/build.rs                     |  1 +
>  rust/migration/meson.build                  | 53 +++++++++++++++++++++
>  rust/migration/src/bindings.rs              | 48 +++++++++++++++++++
>  rust/migration/src/lib.rs                   |  6 +++
>  rust/{qemu-api => migration}/src/vmstate.rs | 42 +++++++++-------
>  rust/qemu-api/Cargo.toml                    |  1 +
>  rust/qemu-api/meson.build                   | 12 ++---
>  rust/qemu-api/src/bindings.rs               | 21 +-------
>  rust/qemu-api/src/cell.rs                   |  4 +-
>  rust/qemu-api/src/lib.rs                    |  1 -
>  rust/qemu-api/src/prelude.rs                |  2 -
>  rust/qemu-api/src/qdev.rs                   |  3 +-
>  rust/qemu-api/src/qom.rs                    |  2 +-
>  rust/qemu-api/tests/tests.rs                |  2 +-
>  rust/qemu-api/tests/vmstate_tests.rs        |  4 +-
>  29 files changed, 252 insertions(+), 68 deletions(-)
>  create mode 100644 rust/migration/wrapper.h
>  create mode 100644 rust/migration/Cargo.toml
>  create mode 120000 rust/migration/build.rs
>  create mode 100644 rust/migration/meson.build
>  create mode 100644 rust/migration/src/bindings.rs
>  create mode 100644 rust/migration/src/lib.rs
>  rename rust/{qemu-api => migration}/src/vmstate.rs (95%)

...

> diff --git a/rust/migration/wrapper.h b/rust/migration/wrapper.h
> new file mode 100644
> index 00000000000..daf316aed41
> --- /dev/null
> +++ b/rust/migration/wrapper.h
> @@ -0,0 +1,51 @@

nit: this file misses SPDX-License-Identifier

> +/*
> + * QEMU System Emulator
> + *
> + * Copyright (c) 2024 Linaro Ltd.
> + *
> + * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 20/33] rust: split "bql" crate
  2025-09-08 10:49 ` [PATCH 20/33] rust: split "bql" crate Paolo Bonzini
@ 2025-09-12  8:31   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:31 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:52PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:52 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 20/33] rust: split "bql" crate
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Unfortunately, an example had to be compile-time disabled, since it
> relies on higher level crates (qdev, irq etc). The alternative is
> probably to move that code to an example in qemu-api or elsewere and
> make a link to it, or include_str.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-12-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                          |  1 +
>  rust/bql/wrapper.h                   | 27 ++++++++
>  rust/Cargo.lock                      | 10 +++
>  rust/Cargo.toml                      |  1 +
>  rust/bql/Cargo.toml                  | 23 +++++++
>  rust/bql/build.rs                    |  1 +
>  rust/bql/meson.build                 | 52 ++++++++++++++++
>  rust/bql/src/bindings.rs             | 25 ++++++++
>  rust/{qemu-api => bql}/src/cell.rs   | 92 ++++++++++------------------
>  rust/bql/src/lib.rs                  | 29 +++++++++
>  rust/common/src/opaque.rs            |  4 +-
>  rust/hw/char/pl011/Cargo.toml        |  1 +
>  rust/hw/char/pl011/meson.build       |  1 +
>  rust/hw/char/pl011/src/device.rs     |  1 +
>  rust/hw/timer/hpet/Cargo.toml        |  1 +
>  rust/hw/timer/hpet/meson.build       |  1 +
>  rust/hw/timer/hpet/src/device.rs     |  2 +-
>  rust/hw/timer/hpet/src/fw_cfg.rs     |  5 +-
>  rust/meson.build                     |  1 +
>  rust/migration/src/vmstate.rs        |  4 +-
>  rust/qemu-api/Cargo.toml             |  5 +-
>  rust/qemu-api/meson.build            | 18 +-----
>  rust/qemu-api/src/chardev.rs         | 17 +++--
>  rust/qemu-api/src/irq.rs             |  1 +
>  rust/qemu-api/src/lib.rs             |  1 -
>  rust/qemu-api/src/prelude.rs         |  3 -
>  rust/qemu-api/src/qdev.rs            |  5 +-
>  rust/qemu-api/src/qom.rs             | 13 ++--
>  rust/qemu-api/src/sysbus.rs          | 13 ++--
>  rust/qemu-api/tests/tests.rs         |  4 +-
>  rust/qemu-api/tests/vmstate_tests.rs |  2 +-
>  31 files changed, 244 insertions(+), 120 deletions(-)
>  create mode 100644 rust/bql/wrapper.h
>  create mode 100644 rust/bql/Cargo.toml
>  create mode 120000 rust/bql/build.rs
>  create mode 100644 rust/bql/meson.build
>  create mode 100644 rust/bql/src/bindings.rs
>  rename rust/{qemu-api => bql}/src/cell.rs (92%)
>  create mode 100644 rust/bql/src/lib.rs

...

> diff --git a/rust/Cargo.lock b/rust/Cargo.lock
> index 048dd74757f..73ca9582a56 100644
> --- a/rust/Cargo.lock
> +++ b/rust/Cargo.lock
> @@ -44,6 +44,13 @@ dependencies = [
>   "qemu_api_macros",
>  ]
>  
> +[[package]]
> +name = "bql"
> +version = "0.1.0"
> +dependencies = [
> + "migration",
> +]

Yes, I also think this dependency is not much proper.

I also agree to move vmstate related things back to migration crate and
then we can make migration depend on bql (in fact, migration itself
indeed requires bql).

Current bql is good for now, so

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 22/33] rust: split "chardev" crate
  2025-09-08 10:49 ` [PATCH 22/33] rust: split "chardev" crate Paolo Bonzini
@ 2025-09-12  8:35   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:35 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:54PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:54 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 22/33] rust: split "chardev" crate
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-14-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                               |  1 +
>  rust/chardev/wrapper.h                    | 28 ++++++++++++++++
>  rust/qemu-api/wrapper.h                   |  2 --
>  rust/Cargo.lock                           | 14 ++++++++
>  rust/chardev/Cargo.toml                   | 24 +++++++++++++
>  rust/chardev/build.rs                     |  1 +
>  rust/chardev/meson.build                  | 41 +++++++++++++++++++++++
>  rust/chardev/src/bindings.rs              | 36 ++++++++++++++++++++
>  rust/{qemu-api => chardev}/src/chardev.rs |  0
>  rust/chardev/src/lib.rs                   |  6 ++++
>  rust/hw/char/pl011/Cargo.toml             |  1 +
>  rust/hw/char/pl011/meson.build            |  1 +
>  rust/hw/char/pl011/src/device.rs          |  2 +-
>  rust/meson.build                          |  1 +
>  rust/qemu-api/Cargo.toml                  |  1 +
>  rust/qemu-api/meson.build                 |  9 +++--
>  rust/qemu-api/src/bindings.rs             |  9 +----
>  rust/qemu-api/src/lib.rs                  |  1 -
>  rust/qemu-api/src/qdev.rs                 |  6 ++--
>  19 files changed, 164 insertions(+), 20 deletions(-)
>  create mode 100644 rust/chardev/wrapper.h
>  create mode 100644 rust/chardev/Cargo.toml
>  create mode 120000 rust/chardev/build.rs
>  create mode 100644 rust/chardev/meson.build
>  create mode 100644 rust/chardev/src/bindings.rs
>  rename rust/{qemu-api => chardev}/src/chardev.rs (100%)
>  create mode 100644 rust/chardev/src/lib.rs

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 23/33] rust: split "system" crate
  2025-09-08 10:49 ` [PATCH 23/33] rust: split "system" crate Paolo Bonzini
@ 2025-09-12  8:38   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:38 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:55PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:55 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 23/33] rust: split "system" crate
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-15-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                             |  1 +
>  rust/qemu-api/wrapper.h                 |  3 --
>  rust/system/wrapper.h                   | 29 +++++++++++++++++
>  rust/Cargo.lock                         | 13 ++++++++
>  rust/Cargo.toml                         |  1 +
>  rust/bql/src/cell.rs                    |  5 ++-
>  rust/hw/char/pl011/Cargo.toml           |  1 +
>  rust/hw/char/pl011/meson.build          |  1 +
>  rust/hw/char/pl011/src/device.rs        |  2 +-
>  rust/hw/timer/hpet/Cargo.toml           |  1 +
>  rust/hw/timer/hpet/meson.build          |  1 +
>  rust/hw/timer/hpet/src/device.rs        | 12 +++----
>  rust/meson.build                        |  1 +
>  rust/qemu-api/Cargo.toml                |  1 +
>  rust/qemu-api/meson.build               |  7 +++--
>  rust/qemu-api/src/bindings.rs           | 14 +--------
>  rust/qemu-api/src/lib.rs                |  1 -
>  rust/qemu-api/src/sysbus.rs             |  2 +-
>  rust/system/Cargo.toml                  | 22 +++++++++++++
>  rust/system/build.rs                    |  1 +
>  rust/system/meson.build                 | 42 +++++++++++++++++++++++++
>  rust/system/src/bindings.rs             | 41 ++++++++++++++++++++++++
>  rust/system/src/lib.rs                  |  6 ++++
>  rust/{qemu-api => system}/src/memory.rs |  2 +-
>  24 files changed, 177 insertions(+), 33 deletions(-)
>  create mode 100644 rust/system/wrapper.h
>  create mode 100644 rust/system/Cargo.toml
>  create mode 120000 rust/system/build.rs
>  create mode 100644 rust/system/meson.build
>  create mode 100644 rust/system/src/bindings.rs
>  create mode 100644 rust/system/src/lib.rs
>  rename rust/{qemu-api => system}/src/memory.rs (99%)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 24/33] rust: split "hwcore" crate
  2025-09-08 10:49 ` [PATCH 24/33] rust: split "hwcore" crate Paolo Bonzini
@ 2025-09-12  8:41   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:41 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:56PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:56 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 24/33] rust: split "hwcore" crate
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-16-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                               |  1 +
>  rust/hw/core/wrapper.h                    | 32 +++++++++
>  rust/qemu-api/wrapper.h                   |  6 --
>  rust/Cargo.lock                           | 17 +++++
>  rust/Cargo.toml                           |  1 +
>  rust/bindings/src/lib.rs                  | 64 ++++++++++++++++++
>  rust/hw/char/pl011/Cargo.toml             |  1 +
>  rust/hw/char/pl011/meson.build            |  1 +
>  rust/hw/char/pl011/src/device.rs          | 10 ++-
>  rust/hw/core/Cargo.toml                   | 26 ++++++++
>  rust/hw/core/build.rs                     |  1 +
>  rust/hw/core/meson.build                  | 80 +++++++++++++++++++++++
>  rust/hw/core/src/bindings.rs              | 41 ++++++++++++
>  rust/{qemu-api => hw/core}/src/irq.rs     |  0
>  rust/hw/core/src/lib.rs                   | 14 ++++
>  rust/{qemu-api => hw/core}/src/qdev.rs    |  2 +-
>  rust/{qemu-api => hw/core}/src/sysbus.rs  |  0
>  rust/{qemu-api => hw/core}/tests/tests.rs |  7 +-
>  rust/hw/timer/hpet/Cargo.toml             |  1 +
>  rust/hw/timer/hpet/meson.build            |  1 +
>  rust/hw/timer/hpet/src/device.rs          | 26 ++++----
>  rust/meson.build                          |  1 +
>  rust/qemu-api-macros/src/lib.rs           | 10 +--
>  rust/qemu-api-macros/src/tests.rs         | 20 +++---
>  rust/qemu-api/Cargo.toml                  |  1 +
>  rust/qemu-api/meson.build                 | 17 +----
>  rust/qemu-api/src/bindings.rs             | 10 ---
>  rust/qemu-api/src/lib.rs                  |  4 --
>  rust/qemu-api/src/prelude.rs              |  4 --
>  29 files changed, 318 insertions(+), 81 deletions(-)
>  create mode 100644 rust/hw/core/wrapper.h
>  create mode 100644 rust/bindings/src/lib.rs
>  create mode 100644 rust/hw/core/Cargo.toml
>  create mode 120000 rust/hw/core/build.rs
>  create mode 100644 rust/hw/core/meson.build
>  create mode 100644 rust/hw/core/src/bindings.rs
>  rename rust/{qemu-api => hw/core}/src/irq.rs (100%)
>  create mode 100644 rust/hw/core/src/lib.rs
>  rename rust/{qemu-api => hw/core}/src/qdev.rs (99%)
>  rename rust/{qemu-api => hw/core}/src/sysbus.rs (100%)
>  rename rust/{qemu-api => hw/core}/tests/tests.rs (97%)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 25/33] rust: rename qemu_api_macros -> qemu_macros
  2025-09-08 10:49 ` [PATCH 25/33] rust: rename qemu_api_macros -> qemu_macros Paolo Bonzini
@ 2025-09-12  8:41   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:41 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:57PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:57 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 25/33] rust: rename qemu_api_macros -> qemu_macros
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Since "qemu_api" is no longer the unique crate to provide APIs.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-17-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                                   |  2 +-
>  rust/Cargo.lock                               | 22 +++++++++----------
>  rust/Cargo.toml                               |  2 +-
>  rust/bits/Cargo.toml                          |  2 +-
>  rust/bits/meson.build                         |  2 +-
>  rust/bits/src/lib.rs                          |  4 ++--
>  rust/chardev/Cargo.toml                       |  2 +-
>  rust/chardev/meson.build                      |  4 ++--
>  rust/chardev/src/chardev.rs                   |  2 +-
>  rust/common/src/opaque.rs                     |  4 ++--
>  rust/hw/char/pl011/Cargo.toml                 |  2 +-
>  rust/hw/char/pl011/meson.build                |  4 ++--
>  rust/hw/char/pl011/src/device.rs              |  4 ++--
>  rust/hw/char/pl011/src/registers.rs           |  2 +-
>  rust/hw/core/Cargo.toml                       |  2 +-
>  rust/hw/core/meson.build                      |  4 ++--
>  rust/hw/core/src/irq.rs                       |  2 +-
>  rust/hw/core/src/qdev.rs                      |  6 ++---
>  rust/hw/core/src/sysbus.rs                    |  2 +-
>  rust/hw/core/tests/tests.rs                   |  4 ++--
>  rust/hw/timer/hpet/Cargo.toml                 |  2 +-
>  rust/hw/timer/hpet/meson.build                |  4 ++--
>  rust/hw/timer/hpet/src/device.rs              |  6 ++---
>  rust/meson.build                              |  2 +-
>  rust/migration/Cargo.toml                     |  2 +-
>  rust/qemu-api/Cargo.toml                      |  2 +-
>  rust/qemu-api/meson.build                     |  4 ++--
>  .../Cargo.toml                                |  2 +-
>  .../meson.build                               | 10 ++++-----
>  .../src/bits.rs                               |  0
>  .../src/lib.rs                                |  0
>  .../src/tests.rs                              |  0
>  rust/qom/Cargo.toml                           |  2 +-
>  rust/qom/meson.build                          |  4 ++--
>  rust/qom/src/qom.rs                           |  4 ++--
>  rust/system/Cargo.toml                        |  2 +-
>  rust/system/meson.build                       |  4 ++--
>  rust/system/src/memory.rs                     |  2 +-
>  rust/util/Cargo.toml                          |  2 +-
>  rust/util/meson.build                         |  2 +-
>  rust/util/src/timer.rs                        |  4 ++--
>  41 files changed, 69 insertions(+), 69 deletions(-)
>  rename rust/{qemu-api-macros => qemu-macros}/Cargo.toml (94%)
>  rename rust/{qemu-api-macros => qemu-macros}/meson.build (63%)
>  rename rust/{qemu-api-macros => qemu-macros}/src/bits.rs (100%)
>  rename rust/{qemu-api-macros => qemu-macros}/src/lib.rs (100%)
>  rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (100%)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 26/33] rust/hpet: drop now unneeded qemu_api dep
  2025-09-08 10:49 ` [PATCH 26/33] rust/hpet: drop now unneeded qemu_api dep Paolo Bonzini
@ 2025-09-12  8:42   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:42 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:58PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:58 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 26/33] rust/hpet: drop now unneeded qemu_api dep
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-18-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/Cargo.lock                | 1 -
>  rust/hw/timer/hpet/Cargo.toml  | 1 -
>  rust/hw/timer/hpet/meson.build | 1 -
>  3 files changed, 3 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 27/33] rust/pl011: drop dependency on qemu_api
  2025-09-08 10:49 ` [PATCH 27/33] rust/pl011: drop dependency on qemu_api Paolo Bonzini
@ 2025-09-12  8:45   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:45 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:49:59PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:49:59 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 27/33] rust/pl011: drop dependency on qemu_api
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-19-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/hw/char/pl011/wrapper.h       | 51 ++++++++++++++++++++++++++++++
>  rust/Cargo.lock                    |  1 -
>  rust/hw/char/pl011/Cargo.toml      |  1 -
>  rust/hw/char/pl011/build.rs        |  1 +
>  rust/hw/char/pl011/meson.build     | 27 ++++++++++++++--
>  rust/hw/char/pl011/src/bindings.rs | 27 ++++++++++++++++
>  rust/hw/char/pl011/src/device.rs   |  2 +-
>  rust/hw/char/pl011/src/lib.rs      |  1 +
>  8 files changed, 106 insertions(+), 5 deletions(-)
>  create mode 100644 rust/hw/char/pl011/wrapper.h
>  create mode 120000 rust/hw/char/pl011/build.rs
>  create mode 100644 rust/hw/char/pl011/src/bindings.rs
> 
> diff --git a/rust/hw/char/pl011/wrapper.h b/rust/hw/char/pl011/wrapper.h
> new file mode 100644
> index 00000000000..87a5a589c8e
> --- /dev/null
> +++ b/rust/hw/char/pl011/wrapper.h
> @@ -0,0 +1,51 @@
> +/*
> + * QEMU System Emulator
> + *
> + * Copyright (c) 2024 Linaro Ltd.
> + *
> + * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */

nit: missing SPDX-License-Identifier.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 00/33] First Rust update for QEMU 10.2
  2025-09-12  7:42     ` Zhao Liu
@ 2025-09-12  8:47       ` Marc-André Lureau
  0 siblings, 0 replies; 70+ messages in thread
From: Marc-André Lureau @ 2025-09-12  8:47 UTC (permalink / raw)
  To: Zhao Liu; +Cc: Peter Maydell, Paolo Bonzini, qemu-devel

Hi Zhao

On Fri, Sep 12, 2025 at 11:20 AM Zhao Liu <zhao1.liu@intel.com> wrote:
>
> On Thu, Sep 11, 2025 at 03:18:44PM +0400, Marc-André Lureau wrote:
> > Date: Thu, 11 Sep 2025 15:18:44 +0400
> > From: Marc-André Lureau <marcandre.lureau@gmail.com>
> > Subject: Re: [PATCH 00/33] First Rust update for QEMU 10.2
> >
> > Hi
> >
> > On Thu, Sep 11, 2025 at 2:05 PM Peter Maydell <peter.maydell@linaro.org> wrote:
> > >
> > > On Mon, 8 Sept 2025 at 11:53, Paolo Bonzini <pbonzini@redhat.com> wrote:
> > > >
> > > > This includes:
> > > > - bumping MSRV to 1.83.0 to support const_refs_to_static
> > > > - Zhao's safe, builder-based implementation of migration callbacks
> > > > - Manos's qdev properties macro.  While bit-based properties are
> > > >   not yet supported, that's a small change overall.
> > > > - the Rust crate split from Marc-André
> > > > - adding proc macro aliases in individual crates, also from Marc-André
> > > >
> > > > I'm still not convinced about having "bql" depend on "migration",
> > > > but I am convinced by the crate split between "util" and "bql",
> > > > so we can move the implementation of VMState from "bql" to
> > > > "migration" later if needed.
> > > >
> > > > For the purpose of getting this in as an easy-to-use base for future
> > > > development, I'm disabling CI from Debian and Ubuntu.  The plan is:
> > > > - that Debian will require trixie to enable Rust usage
> > > > - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions
> > > >   (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318)
> > > > - that Marc-André or someone else will add Rust to other CI jobs
> > >
> > > How far into the future does moving to 1.83.0 push our
> > > "we can enable rust and make it mandatory" point? I was
> > > hoping we would be able to do that sometime soon but this
> > > sounds like we're going to be still a long way out from that :-(
> >
> > I suppose most distro will want to backport a newer rust in their
> > stable distro, like ubuntu is doing or debian with rustc-web. With
> > that, it shouldn't be too long before we could make Rust required.
> >
> > fwiw, I am working on a series to enable Rust on our various
> > CI/docker/vm. I am mostly left with issues from windows and macos ..
> > (btw, if someone can help me investigate the linking issue on mac
> > https://gitlab.com/marcandre.lureau/qemu/-/jobs/11301499123, perhaps a
> > temporary ssh somewhere?)
>
> Just a guess...
>
> It seems "-llibqemuutil.a" may tell the linker to search for
> liblibqemuutil.a.dylib or liblibqemuutil.a.a [*] :-(
>
> [*]: https://manp.gs/mac/1/ld#l

Good guess, the trouble seems to be the usage of -lstatic:+verbatim in meson:

https://github.com/mesonbuild/meson/issues/15020

-- 
Marc-André Lureau


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 28/33] rust: repurpose qemu_api -> tests
  2025-09-08 10:50 ` [PATCH 28/33] rust: repurpose qemu_api -> tests Paolo Bonzini
@ 2025-09-12  8:57   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  8:57 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:50:00PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:50:00 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 28/33] rust: repurpose qemu_api -> tests
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> The crate purpose is only to provide integration tests at this point,
> that can't easily be moved to a specific crate.
> 
> It's also often a good practice to have a single integration test crate
> (see for ex https://github.com/rust-lang/cargo/issues/4867)

Good reference material!

> Drop README.md, use docs/devel/rust.rst instead.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-20-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  MAINTAINERS                                   |  2 +-
>  rust/qemu-api/wrapper.h                       | 53 -------------
>  rust/Cargo.lock                               | 30 ++++----
>  rust/Cargo.toml                               |  2 +-
>  rust/meson.build                              |  4 +-
>  rust/qemu-api/.gitignore                      |  2 -
>  rust/qemu-api/README.md                       | 19 -----
>  rust/qemu-api/build.rs                        |  1 -
>  rust/qemu-api/meson.build                     | 75 -------------------
>  rust/qemu-api/src/bindings.rs                 | 33 --------
>  rust/qemu-api/src/lib.rs                      | 18 -----
>  rust/qemu-api/src/prelude.rs                  |  5 --
>  rust/{qemu-api => tests}/Cargo.toml           |  7 +-
>  rust/tests/meson.build                        | 14 ++++
>  .../tests/vmstate_tests.rs                    |  0
>  15 files changed, 35 insertions(+), 230 deletions(-)
>  delete mode 100644 rust/qemu-api/wrapper.h
>  delete mode 100644 rust/qemu-api/.gitignore
>  delete mode 100644 rust/qemu-api/README.md
>  delete mode 120000 rust/qemu-api/build.rs
>  delete mode 100644 rust/qemu-api/meson.build
>  delete mode 100644 rust/qemu-api/src/bindings.rs
>  delete mode 100644 rust/qemu-api/src/lib.rs
>  delete mode 100644 rust/qemu-api/src/prelude.rs
>  rename rust/{qemu-api => tests}/Cargo.toml (78%)
>  create mode 100644 rust/tests/meson.build
>  rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (100%)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 29/33] rust: re-export qemu_macros internal helper in "bits"
  2025-09-08 10:50 ` [PATCH 29/33] rust: re-export qemu_macros internal helper in "bits" Paolo Bonzini
@ 2025-09-12  9:01   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  9:01 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:50:01PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:50:01 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 29/33] rust: re-export qemu_macros internal helper in "bits"
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Avoid the need to import "qemu_macros".
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-21-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/bits/src/lib.rs | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 30/33] rust: re-export qemu macros from common/qom/hwcore
  2025-09-08 10:50 ` [PATCH 30/33] rust: re-export qemu macros from common/qom/hwcore Paolo Bonzini
@ 2025-09-12  9:03   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  9:03 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:50:02PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:50:02 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 30/33] rust: re-export qemu macros from common/qom/hwcore
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> This is just a bit nicer.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-22-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/devel/rust.rst                 | 2 +-
>  rust/Cargo.lock                     | 8 +-------
>  rust/chardev/Cargo.toml             | 1 -
>  rust/chardev/meson.build            | 2 +-
>  rust/chardev/src/chardev.rs         | 2 +-
>  rust/common/Cargo.toml              | 1 +
>  rust/common/meson.build             | 2 +-
>  rust/common/src/lib.rs              | 2 ++
>  rust/common/src/opaque.rs           | 4 +---
>  rust/hw/char/pl011/Cargo.toml       | 1 -
>  rust/hw/char/pl011/meson.build      | 1 -
>  rust/hw/char/pl011/src/device.rs    | 4 ++--
>  rust/hw/char/pl011/src/registers.rs | 2 +-
>  rust/hw/core/Cargo.toml             | 2 +-
>  rust/hw/core/meson.build            | 2 +-
>  rust/hw/core/src/irq.rs             | 2 +-
>  rust/hw/core/src/lib.rs             | 1 +
>  rust/hw/core/src/qdev.rs            | 4 ++--
>  rust/hw/core/src/sysbus.rs          | 2 +-
>  rust/hw/core/tests/tests.rs         | 4 ++--
>  rust/hw/timer/hpet/Cargo.toml       | 1 -
>  rust/hw/timer/hpet/meson.build      | 1 -
>  rust/hw/timer/hpet/src/device.rs    | 6 +++---
>  rust/meson.build                    | 3 ++-
>  rust/migration/Cargo.toml           | 1 -
>  rust/qom/src/lib.rs                 | 2 ++
>  rust/qom/src/qom.rs                 | 4 ++--
>  rust/system/Cargo.toml              | 1 -
>  rust/system/meson.build             | 2 +-
>  rust/system/src/memory.rs           | 2 +-
>  rust/tests/Cargo.toml               | 1 -
>  rust/util/Cargo.toml                | 1 -
>  rust/util/meson.build               | 2 +-
>  rust/util/src/timer.rs              | 4 ++--
>  34 files changed, 35 insertions(+), 45 deletions(-)

Good cleanup!

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 31/33] docs: update rust.rst
  2025-09-08 10:50 ` [PATCH 31/33] docs: update rust.rst Paolo Bonzini
@ 2025-09-12  9:13   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  9:13 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel, Marc-André Lureau

On Mon, Sep 08, 2025 at 12:50:03PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:50:03 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 31/33] docs: update rust.rst
> X-Mailer: git-send-email 2.51.0
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20250827104147.717203-23-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/devel/rust.rst | 61 ++++++++++++++++++++++++---------------------
>  1 file changed, 32 insertions(+), 29 deletions(-)

...

>  The status of the modules is as follows:
>  
> -================ ======================
> -module           status
> -================ ======================
> -``assertions``   stable
> -``bitops``       complete
> -``callbacks``    complete
> -``cell``         stable
> -``errno``        complete
> -``error``        stable
> -``irq``          complete
> -``log``          proof of concept
> -``memory``       stable
> -``module``       complete
> -``qdev``         stable
> -``qom``          stable
> -``sysbus``       stable
> -``timer``        stable
> -``vmstate``      stable
> -``zeroable``     stable
> -================ ======================
> +========================== ======================
> +module                     status
> +========================== ======================

Missing bits (complete)?

> +``bql::cell``              stable

Missing chardev (stable)?

> +``common::assertions``     stable
> +``common::bitops``         complete
> +``common::callbacks``      complete
> +``common::errno``          complete

Missing opaque (complete) & unint (complete)?

> +``common::zeroable``       stable
> +``hwcore::irq``            complete
> +``hwcore::qdev``           stable
> +``hwcore::sysbus``         stable

Maybe hw::core::...? Because this "modele" seems to reflect the path.

> +``migration::vmstate``     stable
> +``qom``                    stable
> +``system::memory``         stable
> +``util::error``            stable
> +``util::log``              proof of concept
> +``util::module``           complete
> +``util::timer``            stable
> +========================== ======================

Others LGTM,

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 32/33] rust: meson: remove unnecessary complication in device crates
  2025-09-08 10:50 ` [PATCH 32/33] rust: meson: remove unnecessary complication in device crates Paolo Bonzini
@ 2025-09-12  9:20   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  9:20 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:50:04PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:50:04 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 32/33] rust: meson: remove unnecessary complication in
>  device crates
> X-Mailer: git-send-email 2.51.0
> 
> It is not necessary anymore to explicitly list procedural macro crates
> when doing the final link using rustc.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/hw/char/pl011/meson.build | 3 ---
>  rust/hw/timer/hpet/meson.build | 3 ---
>  2 files changed, 6 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 33/33] rust: do not inline do_init_io
  2025-09-08 10:50 ` [PATCH 33/33] rust: do not inline do_init_io Paolo Bonzini
@ 2025-09-12  9:21   ` Zhao Liu
  0 siblings, 0 replies; 70+ messages in thread
From: Zhao Liu @ 2025-09-12  9:21 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: qemu-devel

On Mon, Sep 08, 2025 at 12:50:05PM +0200, Paolo Bonzini wrote:
> Date: Mon,  8 Sep 2025 12:50:05 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 33/33] rust: do not inline do_init_io
> X-Mailer: git-send-email 2.51.0
> 
> This is now possible since the hwcore integration tests do not
> link the system crate anymore.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/system/src/memory.rs | 5 -----
>  1 file changed, 5 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 00/33] First Rust update for QEMU 10.2
  2025-09-11 10:04 ` [PATCH 00/33] First Rust update for QEMU 10.2 Peter Maydell
  2025-09-11 11:18   ` Marc-André Lureau
@ 2025-09-15 14:27   ` Paolo Bonzini
  2025-09-15 17:51     ` Daniel P. Berrangé
  1 sibling, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2025-09-15 14:27 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

On 9/11/25 12:04, Peter Maydell wrote:
> On Mon, 8 Sept 2025 at 11:53, Paolo Bonzini <pbonzini@redhat.com> wrote:
>>
>> This includes:
>> - bumping MSRV to 1.83.0 to support const_refs_to_static
>> - Zhao's safe, builder-based implementation of migration callbacks
>> - Manos's qdev properties macro.  While bit-based properties are
>>    not yet supported, that's a small change overall.
>> - the Rust crate split from Marc-André
>> - adding proc macro aliases in individual crates, also from Marc-André
>>
>> I'm still not convinced about having "bql" depend on "migration",
>> but I am convinced by the crate split between "util" and "bql",
>> so we can move the implementation of VMState from "bql" to
>> "migration" later if needed.
>>
>> For the purpose of getting this in as an easy-to-use base for future
>> development, I'm disabling CI from Debian and Ubuntu.  The plan is:
>> - that Debian will require trixie to enable Rust usage
>> - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions
>>    (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318)
>> - that Marc-André or someone else will add Rust to other CI jobs
> 
> How far into the future does moving to 1.83.0 push our
> "we can enable rust and make it mandatory" point? I was
> hoping we would be able to do that sometime soon but this
> sounds like we're going to be still a long way out from that :-(
Sorry for not seeing the question, the good news is that it doesn't push 
it by much, if at all.  Debian bookworm has even updated rustc-web last 
month to 1.85.0 (say thanks to Firefox), so the only remaining straggler 
is Ubuntu and they're working on it.

As far as technical blockers go, Marc-André has a couple fixes pending 
in Meson, and of course tracing support is still in flight.  But we 
could enable it for 10.2 in CI and 11.0 in configure.

The bad news is that enabling Rust by default is a bit like a point of 
no return and, in that respect, other factors may matter more than 
distro support:

* Community support: it's a lot of new code to deal with, and we're not 
Linux.

* What's the killer app: DMA support may take a bit longer, so right now 
Rust is limited to very simple devices for which memory safety is not a 
primary issue.  Could it be BQL-free interrupts, where even simpler 
devices like interrupt controllers could benefit from a more picky compiler?

* Dependency on Meson work: this is something that Zhao and I didn't 
have time to go through, but right now adding a Rust device is *a lot* 
more verbose than adding the corresponding Rust device.  For day to day 
work it's not a huge deal, as the verbosity is a minor issue while we 
have a handful of Rust devices.  Furthermore, I have plans to improve 
Meson in that respect, so that it understands more of the Rust 
conventions, and we've already structured a lot of the Rust code with an 
eye towards those future versions of Meson.  The problem is that for the 
most part of 2026 we'll be bumping the minimum supported *Meson* version 
relatively quickly.  Right now we only bump it for --enable-rust, but 
the picture changes if Rust is enabled by default or even hard-required.

Let's talk about it on the community call, since we didn't make it at 
QEMU Summit.

Paolo



^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 00/33] First Rust update for QEMU 10.2
  2025-09-15 14:27   ` Paolo Bonzini
@ 2025-09-15 17:51     ` Daniel P. Berrangé
  0 siblings, 0 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2025-09-15 17:51 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Peter Maydell, qemu-devel

On Mon, Sep 15, 2025 at 04:27:47PM +0200, Paolo Bonzini wrote:
> On 9/11/25 12:04, Peter Maydell wrote:
> > On Mon, 8 Sept 2025 at 11:53, Paolo Bonzini <pbonzini@redhat.com> wrote:
> > > 
> > > This includes:
> > > - bumping MSRV to 1.83.0 to support const_refs_to_static
> > > - Zhao's safe, builder-based implementation of migration callbacks
> > > - Manos's qdev properties macro.  While bit-based properties are
> > >    not yet supported, that's a small change overall.
> > > - the Rust crate split from Marc-André
> > > - adding proc macro aliases in individual crates, also from Marc-André
> > > 
> > > I'm still not convinced about having "bql" depend on "migration",
> > > but I am convinced by the crate split between "util" and "bql",
> > > so we can move the implementation of VMState from "bql" to
> > > "migration" later if needed.
> > > 
> > > For the purpose of getting this in as an easy-to-use base for future
> > > development, I'm disabling CI from Debian and Ubuntu.  The plan is:
> > > - that Debian will require trixie to enable Rust usage
> > > - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions
> > >    (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318)
> > > - that Marc-André or someone else will add Rust to other CI jobs
> > 
> > How far into the future does moving to 1.83.0 push our
> > "we can enable rust and make it mandatory" point? I was
> > hoping we would be able to do that sometime soon but this
> > sounds like we're going to be still a long way out from that :-(
> Sorry for not seeing the question, the good news is that it doesn't push it
> by much, if at all.  Debian bookworm has even updated rustc-web last month
> to 1.85.0 (say thanks to Firefox), so the only remaining straggler is Ubuntu
> and they're working on it.
> 
> As far as technical blockers go, Marc-André has a couple fixes pending in
> Meson, and of course tracing support is still in flight.  But we could
> enable it for 10.2 in CI and 11.0 in configure.
> 
> The bad news is that enabling Rust by default is a bit like a point of no
> return and, in that respect, other factors may matter more than distro
> support:
> 
> * Community support: it's a lot of new code to deal with, and we're not
> Linux.

That is true, but we're already seeing alot of stuff that is directly
adjacent to QEMU that is written in Rust, with some overlaps amongst
contributors. Coconut SVSM, IGVM, Rust VMM, virtiofsd, libbkio, and
more besides.

So if we consider community as "open source virtualization devs" it
looks like there is a reasonable pool of talent with experience that
can cross-pollinate with QEMU.

> * What's the killer app: DMA support may take a bit longer, so right now
> Rust is limited to very simple devices for which memory safety is not a
> primary issue.  Could it be BQL-free interrupts, where even simpler devices
> like interrupt controllers could benefit from a more picky compiler?

I suggest we don't try to over-think this too much, as it'll become
a bit of a chicken and egg problem.

IMHO initial ideas for Rust usage will inevitable be fairly simple,
as that's part of the learning process for all of us, avoiding trying
to bite off too much too quickly. As a result, the initial work will
not look very compelling The more worthwhile and substantial things
will only arrive once use of Rust in QEMU has had time to marinate,
and at least some of them will be things we can't thing of ahead of
time.

   "if you build it, they will come"

Even if QEMU doesn't mandate Rust directly, Rust is already a part of
QEMU indirectly, so on the distro front the point of no return is
pretty much already here unless you want to cut out an increasing
number of interesting features.

eg we see ourselves integrating with libblkio, and igvm, both of which
we do via C shims around the Rust code. There are more of such things
to come - a Rust impl of the EDK vars storage is probable, there are
virtee crates for dealing with low level SEV pieces that have been
proposed before and seem interesting.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2025-09-15 17:53 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-08 10:49 [PATCH 00/33] First Rust update for QEMU 10.2 Paolo Bonzini
2025-09-08 10:49 ` [PATCH 01/33] ci: temporarily remove rust from Debian and Ubuntu Paolo Bonzini
2025-09-11  3:14   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 03/33] meson, cargo: require Rust 1.83.0 Paolo Bonzini
2025-09-11  3:19   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 04/33] rust: add missing const markers for MSRV==1.83.0 Paolo Bonzini
2025-09-11  3:20   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 05/33] rust: use inline const expressions Paolo Bonzini
2025-09-11  6:15   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 06/33] rust: add qdev Device derive macro Paolo Bonzini
2025-09-11  6:58   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 07/33] rust: vmstate: convert to use builder pattern Paolo Bonzini
2025-09-08 10:49 ` [PATCH 08/33] rust: vmstate: use const_refs_to_static Paolo Bonzini
2025-09-11 10:07   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 09/33] rust: qdev: const_refs_to_static Paolo Bonzini
2025-09-11 10:20   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 10/33] docs/rust: update msrv Paolo Bonzini
2025-09-11 10:21   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 11/33] rust: remove unused global qemu "allocator" Paolo Bonzini
2025-09-11 10:21   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 12/33] rust: add workspace authors Paolo Bonzini
2025-09-11 10:23   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 13/33] rust: move vmstate_clock!() to qdev module Paolo Bonzini
2025-09-11 10:25   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 14/33] rust: move VMState handling to QOM module Paolo Bonzini
2025-09-11 10:26   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 15/33] rust: move Cell vmstate impl Paolo Bonzini
2025-09-11 10:31   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 16/33] rust: split Rust-only "common" crate Paolo Bonzini
2025-09-11 10:44   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 17/33] rust: make build.rs generic over various ./rust/projects Paolo Bonzini
2025-09-12  7:43   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 18/33] rust: split "util" crate Paolo Bonzini
2025-09-12  8:00   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 19/33] rust: split "migration" crate Paolo Bonzini
2025-09-12  8:09   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 20/33] rust: split "bql" crate Paolo Bonzini
2025-09-12  8:31   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 21/33] rust: split "qom" crate Paolo Bonzini
2025-09-08 10:49 ` [PATCH 22/33] rust: split "chardev" crate Paolo Bonzini
2025-09-12  8:35   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 23/33] rust: split "system" crate Paolo Bonzini
2025-09-12  8:38   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 24/33] rust: split "hwcore" crate Paolo Bonzini
2025-09-12  8:41   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 25/33] rust: rename qemu_api_macros -> qemu_macros Paolo Bonzini
2025-09-12  8:41   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 26/33] rust/hpet: drop now unneeded qemu_api dep Paolo Bonzini
2025-09-12  8:42   ` Zhao Liu
2025-09-08 10:49 ` [PATCH 27/33] rust/pl011: drop dependency on qemu_api Paolo Bonzini
2025-09-12  8:45   ` Zhao Liu
2025-09-08 10:50 ` [PATCH 28/33] rust: repurpose qemu_api -> tests Paolo Bonzini
2025-09-12  8:57   ` Zhao Liu
2025-09-08 10:50 ` [PATCH 29/33] rust: re-export qemu_macros internal helper in "bits" Paolo Bonzini
2025-09-12  9:01   ` Zhao Liu
2025-09-08 10:50 ` [PATCH 30/33] rust: re-export qemu macros from common/qom/hwcore Paolo Bonzini
2025-09-12  9:03   ` Zhao Liu
2025-09-08 10:50 ` [PATCH 31/33] docs: update rust.rst Paolo Bonzini
2025-09-12  9:13   ` Zhao Liu
2025-09-08 10:50 ` [PATCH 32/33] rust: meson: remove unnecessary complication in device crates Paolo Bonzini
2025-09-12  9:20   ` Zhao Liu
2025-09-08 10:50 ` [PATCH 33/33] rust: do not inline do_init_io Paolo Bonzini
2025-09-12  9:21   ` Zhao Liu
     [not found] ` <20250908105005.2119297-3-pbonzini@redhat.com>
2025-09-11  3:17   ` [PATCH 02/33] configure: bump Meson to 1.9.0 for use with Rust Zhao Liu
2025-09-11 10:04 ` [PATCH 00/33] First Rust update for QEMU 10.2 Peter Maydell
2025-09-11 11:18   ` Marc-André Lureau
2025-09-12  7:42     ` Zhao Liu
2025-09-12  8:47       ` Marc-André Lureau
2025-09-15 14:27   ` Paolo Bonzini
2025-09-15 17:51     ` Daniel P. Berrangé

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).