* [RFC 00/18] rust: split qemu-api
@ 2025-08-26 14:04 marcandre.lureau
  2025-08-26 14:04 ` [RFC 01/18] rust: remove unused global qemu "allocator" marcandre.lureau
                   ` (18 more replies)
  0 siblings, 19 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Hi,
qemu-api is providing bindings for various internal libraries. Instead, the
bindings requirements should match the various libraries and use the minimal set
of dependencies.
An initial Rust-only "common" crate is introduced, then "util" (for libqemuutil,
without bql), "migration" (so it doesn't depend on bql), "bql", "qom" (arguably,
bql shouldn't be required?), and "chardev", "system", "hwcore". Finally the
qemu-api crates are renamed and repurposed.
This involves a lot of code churn, so hopefully it can be reviewed and merged
early and iterated upon :)
thanks
Marc-André Lureau (18):
  rust: remove unused global qemu "allocator"
  rust: add workspace authors
  rust: split Rust-only "common" crate
  rust: split "util" crate
  rust: move vmstate_clock!() to qdev module
  rust: move VMState handling to QOM module
  rust: move Cell vmstate impl
  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
  docs: update rust.rst
 MAINTAINERS                                   |  12 +-
 docs/devel/rust.rst                           |  51 +--
 meson.build                                   |   4 -
 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 ++
 rust/Cargo.lock                               | 127 ++++++-
 rust/Cargo.toml                               |  16 +-
 rust/bits/Cargo.toml                          |   2 +-
 rust/bits/meson.build                         |   2 +-
 rust/bits/src/lib.rs                          |   4 +-
 rust/{qemu-api => bql}/Cargo.toml             |  13 +-
 rust/{qemu-api => bql}/build.rs               |   2 +-
 rust/bql/meson.build                          |  52 +++
 rust/bql/src/bindings.rs                      |  25 ++
 rust/{qemu-api => bql}/src/cell.rs            | 333 +++---------------
 rust/bql/src/lib.rs                           |  29 ++
 rust/chardev/Cargo.toml                       |  24 ++
 rust/chardev/build.rs                         |  43 +++
 rust/chardev/meson.build                      |  54 +++
 rust/chardev/src/bindings.rs                  |  36 ++
 rust/{qemu-api => chardev}/src/chardev.rs     |  35 +-
 rust/chardev/src/lib.rs                       |   4 +
 rust/common/Cargo.toml                        |  16 +
 rust/common/meson.build                       |  32 ++
 rust/{qemu-api => common}/src/assertions.rs   |  16 +-
 rust/{qemu-api => common}/src/bitops.rs       |   1 -
 rust/{qemu-api => common}/src/callbacks.rs    |  12 +-
 rust/common/src/lib.rs                        |  17 +
 rust/common/src/opaque.rs                     | 240 +++++++++++++
 rust/{qemu-api => common}/src/uninit.rs       |   2 +-
 rust/common/src/zeroable.rs                   |  18 +
 rust/hw/char/pl011/Cargo.toml                 |  11 +-
 rust/hw/char/pl011/build.rs                   |  43 +++
 rust/hw/char/pl011/meson.build                |  39 +-
 rust/hw/char/pl011/src/bindings.rs            |  27 ++
 rust/hw/char/pl011/src/device.rs              |  49 +--
 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                         |  43 +++
 rust/{qemu-api => hw/core}/meson.build        |  86 ++---
 rust/hw/core/src/bindings.rs                  |  41 +++
 rust/{qemu-api => hw/core}/src/irq.rs         |  18 +-
 rust/hw/core/src/lib.rs                       |  12 +
 rust/{qemu-api => hw/core}/src/qdev.rs        |  81 +++--
 rust/{qemu-api => hw/core}/src/sysbus.rs      |  28 +-
 rust/{qemu-api => hw/core}/tests/tests.rs     |  29 +-
 rust/hw/timer/hpet/Cargo.toml                 |  10 +-
 rust/hw/timer/hpet/meson.build                |  12 +-
 rust/hw/timer/hpet/src/device.rs              |  56 ++-
 rust/hw/timer/hpet/src/fw_cfg.rs              |   6 +-
 rust/meson.build                              |  12 +-
 rust/migration/Cargo.toml                     |  21 ++
 rust/migration/build.rs                       |  43 +++
 rust/migration/meson.build                    |  57 +++
 rust/migration/src/bindings.rs                |  48 +++
 rust/migration/src/lib.rs                     |   4 +
 rust/{qemu-api => migration}/src/vmstate.rs   | 166 ++++-----
 rust/qemu-api/.gitignore                      |   2 -
 rust/qemu-api/README.md                       |  19 -
 rust/qemu-api/src/lib.rs                      | 170 ---------
 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                                |  20 +-
 .../src/tests.rs                              |   8 +-
 rust/qom/Cargo.toml                           |  23 ++
 rust/qom/build.rs                             |  43 +++
 rust/qom/meson.build                          |  61 ++++
 rust/qom/src/bindings.rs                      |  25 ++
 rust/qom/src/lib.rs                           |   4 +
 rust/{qemu-api => qom}/src/qom.rs             |  27 +-
 rust/qom/tests/tests.rs                       |  47 +++
 rust/system/Cargo.toml                        |  22 ++
 rust/system/build.rs                          |  43 +++
 rust/system/meson.build                       |  57 +++
 rust/{qemu-api => system}/src/bindings.rs     |  33 +-
 rust/system/src/lib.rs                        |   4 +
 rust/{qemu-api => system}/src/memory.rs       |  20 +-
 rust/tests/Cargo.toml                         |  30 ++
 rust/tests/meson.build                        |  14 +
 .../tests/vmstate_tests.rs                    |  18 +-
 rust/util/Cargo.toml                          |  23 ++
 rust/util/build.rs                            |  43 +++
 rust/util/meson.build                         |  61 ++++
 rust/util/src/bindings.rs                     |  25 ++
 rust/{qemu-api => util}/src/errno.rs          |  11 +-
 rust/{qemu-api => util}/src/error.rs          |   6 +-
 rust/util/src/lib.rs                          |  10 +
 rust/{qemu-api => util}/src/log.rs            |  12 +-
 rust/{qemu-api => util}/src/module.rs         |   2 +-
 rust/{qemu-api => util}/src/timer.rs          |  12 +-
 100 files changed, 2372 insertions(+), 1044 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
 rename rust/{qemu-api => bql}/Cargo.toml (52%)
 rename rust/{qemu-api => bql}/build.rs (96%)
 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 100644 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 (91%)
 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 (92%)
 rename rust/{qemu-api => common}/src/bitops.rs (98%)
 rename rust/{qemu-api => common}/src/callbacks.rs (97%)
 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
 create mode 100644 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 100644 rust/hw/core/build.rs
 rename rust/{qemu-api => hw/core}/meson.build (52%)
 create mode 100644 rust/hw/core/src/bindings.rs
 rename rust/{qemu-api => hw/core}/src/irq.rs (92%)
 create mode 100644 rust/hw/core/src/lib.rs
 rename rust/{qemu-api => hw/core}/src/qdev.rs (86%)
 rename rust/{qemu-api => hw/core}/src/sysbus.rs (87%)
 rename rust/{qemu-api => hw/core}/tests/tests.rs (88%)
 create mode 100644 rust/migration/Cargo.toml
 create mode 100644 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 (80%)
 delete mode 100644 rust/qemu-api/.gitignore
 delete mode 100644 rust/qemu-api/README.md
 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 (91%)
 rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (93%)
 create mode 100644 rust/qom/Cargo.toml
 create mode 100644 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
 rename rust/{qemu-api => qom}/src/qom.rs (98%)
 create mode 100644 rust/qom/tests/tests.rs
 create mode 100644 rust/system/Cargo.toml
 create mode 100644 rust/system/build.rs
 create mode 100644 rust/system/meson.build
 rename rust/{qemu-api => system}/src/bindings.rs (56%)
 create mode 100644 rust/system/src/lib.rs
 rename rust/{qemu-api => system}/src/memory.rs (95%)
 create mode 100644 rust/tests/Cargo.toml
 create mode 100644 rust/tests/meson.build
 rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (96%)
 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/errno.rs (98%)
 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 (93%)
-- 
2.50.1
^ permalink raw reply	[flat|nested] 32+ messages in thread
* [RFC 01/18] rust: remove unused global qemu "allocator"
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 02/18] rust: add workspace authors marcandre.lureau
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
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>
---
 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 50c774a195..ebbe9f8785 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 0868e1b426..1a24859cd7 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -17,7 +17,7 @@ rust-version = "1.77.0"
 
 [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 db7000dee4..603327945f 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -21,7 +21,6 @@ foreign = "~0.3.1"
 
 [features]
 default = ["debug_cell"]
-allocator = []
 debug_cell = []
 
 [lints]
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index a090297c45..062009f161 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 86dcd8ef17..0f9231f398 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -32,139 +32,4 @@
 pub mod vmstate;
 pub mod zeroable;
 
-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.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 02/18] rust: add workspace authors
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
  2025-08-26 14:04 ` [RFC 01/18] rust: remove unused global qemu "allocator" marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 03/18] rust: split Rust-only "common" crate marcandre.lureau
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 rust/Cargo.toml | 1 +
 1 file changed, 1 insertion(+)
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 1a24859cd7..5881bd034b 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -14,6 +14,7 @@ homepage = "https://www.qemu.org"
 license = "GPL-2.0-or-later"
 repository = "https://gitlab.com/qemu-project/qemu/"
 rust-version = "1.77.0"
+authors = ["The QEMU Project Developers <qemu-devel@nongnu.org>"]
 
 [workspace.lints.rust]
 unexpected_cfgs = { level = "deny", check-cfg = [
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 03/18] rust: split Rust-only "common" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
  2025-08-26 14:04 ` [RFC 01/18] rust: remove unused global qemu "allocator" marcandre.lureau
  2025-08-26 14:04 ` [RFC 02/18] rust: add workspace authors marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 04/18] rust: split "util" crate marcandre.lureau
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 MAINTAINERS                                 |   1 +
 rust/Cargo.lock                             |   7 +
 rust/Cargo.toml                             |   1 +
 rust/common/Cargo.toml                      |  16 ++
 rust/common/meson.build                     |  32 +++
 rust/{qemu-api => common}/src/assertions.rs |  16 +-
 rust/{qemu-api => common}/src/bitops.rs     |   1 -
 rust/{qemu-api => common}/src/callbacks.rs  |  12 +-
 rust/common/src/lib.rs                      |  17 ++
 rust/common/src/opaque.rs                   | 240 ++++++++++++++++++++
 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              |   1 +
 rust/hw/char/pl011/src/device.rs            |   4 +-
 rust/hw/timer/hpet/Cargo.toml               |   1 +
 rust/hw/timer/hpet/meson.build              |   1 +
 rust/hw/timer/hpet/src/device.rs            |   3 +-
 rust/hw/timer/hpet/src/fw_cfg.rs            |   3 +-
 rust/meson.build                            |   1 +
 rust/qemu-api-macros/src/lib.rs             |  14 +-
 rust/qemu-api-macros/src/tests.rs           |   2 +-
 rust/qemu-api/Cargo.toml                    |   1 +
 rust/qemu-api/meson.build                   |   9 +-
 rust/qemu-api/src/bindings.rs               |  21 ++
 rust/qemu-api/src/cell.rs                   | 237 +------------------
 rust/qemu-api/src/chardev.rs                |   5 +-
 rust/qemu-api/src/errno.rs                  |   3 +-
 rust/qemu-api/src/error.rs                  |   3 +-
 rust/qemu-api/src/irq.rs                    |   3 +-
 rust/qemu-api/src/lib.rs                    |   5 -
 rust/qemu-api/src/memory.rs                 |   5 +-
 rust/qemu-api/src/prelude.rs                |   4 +-
 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                |  24 +-
 rust/qemu-api/src/zeroable.rs               |  37 ---
 rust/qemu-api/tests/tests.rs                |   2 +-
 rust/qemu-api/tests/vmstate_tests.rs        |   4 +-
 41 files changed, 429 insertions(+), 352 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%)
 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 a07086ed76..80c0f80657 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3510,6 +3510,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 b785c718f3..ac105bd499 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -44,6 +44,10 @@ dependencies = [
  "qemu_api_macros",
 ]
 
+[[package]]
+name = "common"
+version = "0.1.0"
+
 [[package]]
 name = "either"
 version = "1.12.0"
@@ -63,6 +67,7 @@ dependencies = [
 name = "hpet"
 version = "0.1.0"
 dependencies = [
+ "common",
  "qemu_api",
  "qemu_api_macros",
 ]
@@ -89,6 +94,7 @@ dependencies = [
  "bilge",
  "bilge-impl",
  "bits",
+ "common",
  "qemu_api",
  "qemu_api_macros",
 ]
@@ -130,6 +136,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 5881bd034b..bb27db7931 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 0000000000..d7e5580339
--- /dev/null
+++ b/rust/common/Cargo.toml
@@ -0,0 +1,16 @@
+[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
+
+[lints]
+workspace = true
diff --git a/rust/common/meson.build b/rust/common/meson.build
new file mode 100644
index 0000000000..d644a4f012
--- /dev/null
+++ b/rust/common/meson.build
@@ -0,0 +1,32 @@
+_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/opaque.rs',
+      'src/uninit.rs',
+      'src/zeroable.rs',
+    ],
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  rust_args: _common_cfg,
+)
+
+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 a2d38c877d..32a3ad0be6 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);
 /// ```
@@ -107,7 +107,7 @@ 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);
@@ -136,12 +136,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 b1e3a530ab..06c78c3b8a 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 9642a16eb8..676508a4b2 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,))
 /// # }
@@ -125,7 +125,7 @@ pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
     /// # Examples
     ///
     /// ```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 {
     ///     let _: () = F::ASSERT_IS_SOME;
     ///     F::call((s,))
@@ -145,7 +145,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/common/src/lib.rs b/rust/common/src/lib.rs
new file mode 100644
index 0000000000..a956ebbd0b
--- /dev/null
+++ b/rust/common/src/lib.rs
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod assertions;
+
+pub mod bitops;
+
+pub mod callbacks;
+pub use callbacks::FnCall;
+
+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 0000000000..f96b91e25a
--- /dev/null
+++ b/rust/common/src/opaque.rs
@@ -0,0 +1,240 @@
+// 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.
+    #[allow(clippy::missing_const_for_fn)]
+    pub 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.
+    #[allow(clippy::missing_const_for_fn)]
+    pub 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 04123b4ae9..1a1adfbb0d 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 0000000000..fd056deb1f
--- /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 88ef110507..6d15f107df 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 2a1be329ab..67bd295c3d 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -7,6 +7,7 @@ _libpl011_rs = static_library(
     bilge_rs,
     bilge_impl_rs,
     bits_rs,
+    common_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index ceb71dd99b..0c27c42c31 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -8,6 +8,7 @@
     ptr::NonNull,
 };
 
+use common::{static_assert, uninit_field_mut, Zeroable};
 use qemu_api::{
     bindings::{qdev_prop_bool, qdev_prop_chr},
     chardev::{CharBackend, Chardev, Event},
@@ -19,12 +20,9 @@
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
     qom::{ObjectImpl, Owned, ParentField, ParentInit},
-    static_assert,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    uninit_field_mut,
     vmstate::VMStateDescription,
     vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
-    zeroable::Zeroable,
 };
 
 use crate::registers::{self, Interrupt, RegisterOffset};
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index ac5df23c1d..ba7354f07e 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 c2d7c0532c..05f8bd240a 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -4,6 +4,7 @@ _libhpet_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   dependencies: [
+    common_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index acf7251029..f3c324f243 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, Zeroable};
 use qemu_api::{
     bindings::{
         address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
@@ -26,10 +27,8 @@
     qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
     timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
-    uninit_field_mut,
     vmstate::VMStateDescription,
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
-    zeroable::Zeroable,
 };
 
 use crate::fw_cfg::HPETFwConfig;
diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs
index 619d662ee1..0605225fbb 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 331f11b7e7..402f8d6600 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 b525d89c09..850e3b1596 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -88,7 +88,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
     let parent = &get_fields(&input, "#[derive(Object)]")?[0].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! {
@@ -118,20 +118,20 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
     // TODO: how to add "::qemu_api"?  For now, this is only used in the
     // qemu_api crate so it's not a problem.
     Ok(quote! {
-        unsafe impl crate::cell::Wrapper for #name {
-            type Wrapped = <#typ as crate::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 crate::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 crate::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 crate::cell::Wrapper>::Wrapped {
+            pub const fn as_ptr(&self) -> *const <Self as ::common::opaque::Wrapper>::Wrapped {
                 self.0.as_ptr()
             }
 
@@ -139,7 +139,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 crate::cell::Wrapper>::Wrapped {
+            pub const fn raw_get(slot: *mut Self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
                 slot.cast()
             }
         }
diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs
index d6dcd62fcf..42d5fa50bd 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -58,7 +58,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 603327945f..12774c356a 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 = "~1.0"
 libc = "0.2.162"
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 062009f161..6177e26819 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -52,10 +52,7 @@ _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',
@@ -69,16 +66,14 @@ _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],
 )
 
@@ -104,7 +99,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]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index b8104dea8b..3acdd903b5 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 27063b049d..98d720caf9 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,
 };
@@ -935,167 +866,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.
-    #[allow(clippy::missing_const_for_fn)]
-    pub 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.
-    #[allow(clippy::missing_const_for_fn)]
-    pub 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 6e0590d758..e2cb5f4379 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -18,10 +18,11 @@
     slice,
 };
 
+use common::{callbacks::FnCall, Opaque};
+
 use crate::{
     bindings,
-    callbacks::FnCall,
-    cell::{BqlRefMut, Opaque},
+    cell::{BqlRefCell, BqlRefMut},
     prelude::*,
 };
 
diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs
index 18d101448b..e9478c4eb4 100644
--- a/rust/qemu-api/src/errno.rs
+++ b/rust/qemu-api/src/errno.rs
@@ -240,8 +240,9 @@ pub fn into_neg_errno<T: MergeErrno, E: Into<Errno>>(value: Result<T, E>) -> T::
 mod tests {
     use std::io::ErrorKind;
 
+    use common::assert_match;
+
     use super::*;
-    use crate::assert_match;
 
     #[test]
     pub fn test_from_u8() {
diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs
index e114fc4178..8bac3cbec8 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 1526e6f63a..ea6b32848c 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 0f9231f398..7a2b29f28f 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -13,9 +13,6 @@
 #[rustfmt::skip]
 pub mod prelude;
 
-pub mod assertions;
-pub mod bitops;
-pub mod callbacks;
 pub mod cell;
 pub mod chardev;
 pub mod errno;
@@ -28,8 +25,6 @@
 pub mod qom;
 pub mod sysbus;
 pub mod timer;
-pub mod uninit;
 pub mod vmstate;
-pub mod zeroable;
 
 pub use error::{Error, Result};
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
index e40fad6cf1..f790cb5fd2 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 8f9e23ee2c..ad157f9655 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -4,8 +4,6 @@
 
 //! Commonly used traits and types for QEMU.
 
-pub use crate::bitops::IntegerExt;
-
 pub use crate::cell::BqlCell;
 pub use crate::cell::BqlRefCell;
 
@@ -19,8 +17,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 36f02fb57d..bf17558cdb 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},
     irq::InterruptSource,
@@ -198,7 +198,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$(,)*) => {
@@ -209,7 +209,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$(,)*) => {
@@ -219,7 +219,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 e20ee014cb..c2f9a682bb 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,
 };
 
 /// A safe wrapper around [`bindings::Object`].
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index e92502a8fe..4a5b4cbbf6 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 0a2d111d49..441072a949 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 812f390d78..6a9cc0b975 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -27,10 +27,10 @@
 use core::{marker::PhantomData, mem, ptr::NonNull};
 use std::ffi::{c_int, c_void};
 
+use common::{callbacks::FnCall, Zeroable};
+
 pub use crate::bindings::{VMStateDescription, VMStateField};
-use crate::{
-    bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable,
-};
+use crate::{bindings::VMStateFlags, prelude::*, qom::Owned};
 
 /// This macro is used to call a function with a generic argument bound
 /// to the type of a field.  The function must take a
@@ -343,7 +343,7 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
 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);
+impl_vmstate_transparent!(::common::Opaque<T> where T: VMState);
 
 #[macro_export]
 macro_rules! impl_vmstate_bitsized {
@@ -431,7 +431,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::Zeroable::ZERO
         }
     }};
 }
@@ -454,7 +454,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 {
             let _: () = F::ASSERT_IS_SOME;
@@ -485,14 +485,14 @@ macro_rules! vmstate_struct {
                 .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)?);
+                ::common::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,
             $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
-            ..$crate::zeroable::Zeroable::ZERO
+            ..::common::zeroable::Zeroable::ZERO
          } $(.with_varray_flag_unchecked(
                   $crate::call_func_with_field!(
                       $crate::vmstate::vmstate_varray_flag,
@@ -513,7 +513,7 @@ macro_rules! vmstate_clock {
                 .as_bytes()
                 .as_ptr() as *const ::std::os::raw::c_char,
             offset: {
-                $crate::assert_field_type!(
+                ::common::assert_field_type!(
                     $struct_name,
                     $field_name,
                     $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
@@ -526,7 +526,7 @@ macro_rules! vmstate_clock {
                     | $crate::bindings::VMStateFlags::VMS_POINTER.0,
             ),
             vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
-            ..$crate::zeroable::Zeroable::ZERO
+            ..::common::zeroable::Zeroable::ZERO
          } $(.with_varray_flag_unchecked(
                   $crate::call_func_with_field!(
                       $crate::vmstate::vmstate_varray_flag,
@@ -548,7 +548,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()
@@ -567,7 +567,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
         }
     };
 }
diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs
deleted file mode 100644
index d8239d0856..0000000000
--- 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/tests.rs b/rust/qemu-api/tests/tests.rs
index a658a49fcf..2594e5465d 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -4,6 +4,7 @@
 
 use std::{ffi::CStr, ptr::addr_of};
 
+use common::Zeroable;
 use qemu_api::{
     bindings::{module_call_init, module_init_type, qdev_prop_bool},
     cell::{self, BqlCell},
@@ -13,7 +14,6 @@
     qom::{ObjectImpl, ParentField},
     sysbus::SysBusDevice,
     vmstate::VMStateDescription,
-    zeroable::Zeroable,
 };
 
 mod vmstate_tests;
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs
index bded836eb6..1ec80c483e 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -9,16 +9,16 @@
     slice,
 };
 
+use common::{Opaque, Zeroable};
 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,
     vmstate::{VMStateDescription, VMStateField},
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
-    zeroable::Zeroable,
 };
 
 const FOO_ARRAY_MAX: usize = 3;
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 04/18] rust: split "util" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (2 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 03/18] rust: split Rust-only "common" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 05/18] rust: move vmstate_clock!() to qdev module marcandre.lureau
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 MAINTAINERS                           |  1 +
 rust/qemu-api/wrapper.h               |  6 ---
 rust/util/wrapper.h                   | 32 ++++++++++++++
 rust/Cargo.lock                       | 14 ++++++
 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              |  1 +
 rust/qemu-api/meson.build             | 12 ++----
 rust/qemu-api/src/bindings.rs         |  1 +
 rust/qemu-api/src/chardev.rs          | 10 ++---
 rust/qemu-api/src/lib.rs              |  7 ---
 rust/qemu-api/src/prelude.rs          |  4 --
 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          |  3 +-
 rust/util/Cargo.toml                  | 23 ++++++++++
 rust/util/build.rs                    | 43 +++++++++++++++++++
 rust/util/meson.build                 | 61 +++++++++++++++++++++++++++
 rust/util/src/bindings.rs             | 25 +++++++++++
 rust/{qemu-api => util}/src/errno.rs  | 10 ++---
 rust/{qemu-api => util}/src/error.rs  |  7 ++-
 rust/util/src/lib.rs                  | 10 +++++
 rust/{qemu-api => util}/src/log.rs    | 12 +++---
 rust/{qemu-api => util}/src/module.rs |  2 +-
 rust/{qemu-api => util}/src/timer.rs  |  0
 34 files changed, 255 insertions(+), 64 deletions(-)
 create mode 100644 rust/util/wrapper.h
 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/errno.rs (98%)
 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 80c0f80657..22d7847804 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3514,6 +3514,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 15a1b19847..cc7112406b 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 0000000000..b9ed68a01d
--- /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 ac105bd499..5cc3306b21 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -70,6 +70,7 @@ dependencies = [
  "common",
  "qemu_api",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -97,6 +98,7 @@ dependencies = [
  "common",
  "qemu_api",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -140,6 +142,7 @@ dependencies = [
  "foreign",
  "libc",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -177,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 bb27db7931..04dac19c32 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]
@@ -18,9 +19,7 @@ rust-version = "1.77.0"
 authors = ["The QEMU Project Developers <qemu-devel@nongnu.org>"]
 
 [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 6d15f107df..0cf9943fe8 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 67bd295c3d..41cf46ead1 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -8,6 +8,7 @@ _libpl011_rs = static_library(
     bilge_impl_rs,
     bits_rs,
     common_rs,
+    util_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 0c27c42c31..eb94cf6ba7 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -14,8 +14,6 @@
     chardev::{CharBackend, Chardev, Event},
     impl_vmstate_forward,
     irq::{IRQState, InterruptSource},
-    log::Log,
-    log_mask_ln,
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
@@ -24,6 +22,7 @@
     vmstate::VMStateDescription,
     vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
 };
+use util::{log::Log, log_mask_ln};
 
 use crate::registers::{self, Interrupt, RegisterOffset};
 
@@ -180,7 +179,7 @@ fn properties() -> &'static [Property] {
     fn vmsd() -> Option<&'static VMStateDescription> {
         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 {
@@ -627,7 +626,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 ba7354f07e..dd9a5ed3d4 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 05f8bd240a..f413893aa5 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -5,6 +5,7 @@ _libhpet_rs = static_library(
   rust_abi: 'rust',
   dependencies: [
     common_rs,
+    util_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index f3c324f243..672c88c46c 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -26,10 +26,10 @@
     qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
     qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
     vmstate::VMStateDescription,
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
 };
+use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
 use crate::fw_cfg::HPETFwConfig;
 
@@ -723,7 +723,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}"
@@ -1039,7 +1039,7 @@ fn vmsd() -> Option<&'static VMStateDescription> {
         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 402f8d6600..a9d715e6e9 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 850e3b1596..b5f77f06f5 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -91,7 +91,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 42d5fa50bd..52683e46d5 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -63,7 +63,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 12774c356a..28e5969e7a 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" }
+util = { path = "../util" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 anyhow = "~1.0"
 libc = "0.2.162"
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 6177e26819..8a2950dfe4 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,17 +54,12 @@ _qemu_api_rs = static_library(
       'src/bindings.rs',
       'src/cell.rs',
       'src/chardev.rs',
-      'src/errno.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},
@@ -73,7 +67,7 @@ _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, qemuutil_rs, util_rs,
                  qom, hwcore, chardev, migration],
 )
 
@@ -99,7 +93,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]),
+        dependencies: [common_rs, util_rs, qemu_api]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 3acdd903b5..aedf42b652 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/chardev.rs b/rust/qemu-api/src/chardev.rs
index e2cb5f4379..c2bea23e82 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -54,7 +54,7 @@ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
 
         let len = buf.len().try_into().unwrap();
         let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) };
-        errno::into_io_result(r).map(|cnt| cnt as usize)
+        ::util::errno::into_io_result(r).map(|cnt| cnt as usize)
     }
 
     fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
@@ -62,7 +62,7 @@ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
 
         let len = buf.len().try_into().unwrap();
         let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) };
-        errno::into_io_result(r).and_then(|cnt| {
+        ::util::errno::into_io_result(r).and_then(|cnt| {
             if cnt as usize == buf.len() {
                 Ok(())
             } else {
@@ -215,7 +215,7 @@ pub fn send_break(&self, long: bool) -> io::Result<()> {
             )
         };
 
-        errno::into_io_result(r).map(|_| ())
+        ::util::errno::into_io_result(r).map(|_| ())
     }
 
     /// Write data to a character backend from the front end.  This function
@@ -229,7 +229,7 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         let len = buf.len().try_into().unwrap();
         // SAFETY: qemu_chr_fe_write is thread-safe
         let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) };
-        errno::into_io_result(r).map(|cnt| cnt as usize)
+        ::util::errno::into_io_result(r).map(|cnt| cnt as usize)
     }
 
     /// Write data to a character backend from the front end.  This function
@@ -243,7 +243,7 @@ pub fn write_all(&self, buf: &[u8]) -> io::Result<()> {
         let len = buf.len().try_into().unwrap();
         // SAFETY: qemu_chr_fe_write_all is thread-safe
         let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) };
-        errno::into_io_result(r).and_then(|cnt| {
+        ::util::errno::into_io_result(r).and_then(|cnt| {
             if cnt as usize == buf.len() {
                 Ok(())
             } else {
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 7a2b29f28f..db81841a8f 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -15,16 +15,9 @@
 
 pub mod cell;
 pub mod chardev;
-pub mod errno;
-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;
-
-pub use error::{Error, Result};
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index ad157f9655..3d771481e4 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -7,10 +7,6 @@
 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;
 
 pub use crate::qom::InterfaceType;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index bf17558cdb..02e9d55e6a 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};
+pub 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},
     irq::InterruptSource,
     prelude::*,
     qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
@@ -135,7 +135,7 @@ fn vmsd() -> Option<&'static VMStateDescription> {
 /// 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 4a5b4cbbf6..2dbfc31dbd 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 6a9cc0b975..4e2b7e2db0 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -390,7 +390,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);
 
 // Pointer types using the underlying type's VMState plus VMS_POINTER
 // Note that references are not supported, though references to cells
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 2594e5465d..1dd4e29754 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -6,7 +6,7 @@
 
 use common::Zeroable;
 use qemu_api::{
-    bindings::{module_call_init, module_init_type, qdev_prop_bool},
+    bindings::qdev_prop_bool,
     cell::{self, BqlCell},
     declare_properties, define_property,
     prelude::*,
@@ -15,6 +15,7 @@
     sysbus::SysBusDevice,
     vmstate::VMStateDescription,
 };
+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 0000000000..aa10f03384
--- /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 = "~1.0"
+libc = "0.2.162"
+foreign = "~0.3.1"
+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 0000000000..cac57cc5b9
--- /dev/null
+++ b/rust/util/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/util/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"))
+    };
+
+    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(())
+}
diff --git a/rust/util/meson.build b/rust/util/meson.build
new file mode 100644
index 0000000000..3fe8e3975f
--- /dev/null
+++ b/rust/util/meson.build
@@ -0,0 +1,61 @@
+_util_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+_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/errno.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',
+  rust_args: _util_cfg,
+  dependencies: [anyhow_rs, libc_rs, foreign_rs, qemuutil_rs, common_rs, qemu_api_macros],
+)
+
+util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil_rs, 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 0000000000..9ffff12cde
--- /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/errno.rs b/rust/util/src/errno.rs
similarity index 98%
rename from rust/qemu-api/src/errno.rs
rename to rust/util/src/errno.rs
index e9478c4eb4..f3d27ccf65 100644
--- a/rust/qemu-api/src/errno.rs
+++ b/rust/util/src/errno.rs
@@ -176,7 +176,7 @@ fn map_ok(self) -> i32 {
 /// are interpreted as negated `errno` and turned into an `Err`.
 ///
 /// ```
-/// # use qemu_api::errno::into_io_result;
+/// # use util::errno::into_io_result;
 /// # use std::io::ErrorKind;
 /// let ok = into_io_result(1i32).unwrap();
 /// assert_eq!(ok, 1u32);
@@ -192,7 +192,7 @@ fn map_ok(self) -> i32 {
 /// likely overflows and will panic:
 ///
 /// ```should_panic
-/// # use qemu_api::errno::into_io_result;
+/// # use util::errno::into_io_result;
 /// # #[allow(dead_code)]
 /// let err = into_io_result(-0x1234_5678i32); // panic
 /// ```
@@ -204,7 +204,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 util::errno::into_neg_errno;
 /// # use std::io::{self, ErrorKind};
 /// let ok: io::Result<()> = Ok(());
 /// assert_eq!(into_neg_errno(ok), 0);
@@ -223,7 +223,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 util::errno::into_neg_errno;
 /// # use std::io;
 /// let err: io::Result<u32> = Ok(0x8899_AABB);
 /// into_neg_errno(err) // panic
@@ -240,7 +240,7 @@ pub fn into_neg_errno<T: MergeErrno, E: Into<Errno>>(value: Result<T, E>) -> T::
 mod tests {
     use std::io::ErrorKind;
 
-    use common::assert_match;
+    use bindings::assert_match;
 
     use super::*;
 
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 8bac3cbec8..a1b11a97ca 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.
 
@@ -316,11 +316,10 @@ mod tests {
     use std::ffi::CStr;
 
     use anyhow::anyhow;
-    use common::assert_match;
+    use bindings::assert_match;
     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 0000000000..3463d937b4
--- /dev/null
+++ b/rust/util/src/lib.rs
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+pub mod errno;
+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 a441b8c1f2..2e2c1cf3f8 100644
--- a/rust/qemu-api/src/log.rs
+++ b/rust/util/src/log.rs
@@ -47,7 +47,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");
@@ -114,7 +114,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");
@@ -124,7 +124,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!(
@@ -137,12 +137,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 fa5cea3598..06c45fc142 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.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 05/18] rust: move vmstate_clock!() to qdev module
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (3 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 04/18] rust: split "util" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 06/18] rust: move VMState handling to QOM module marcandre.lureau
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
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>
---
 rust/qemu-api/src/qdev.rs    | 34 ++++++++++++++++++++++++++++++++++
 rust/qemu-api/src/vmstate.rs | 34 ----------------------------------
 2 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 02e9d55e6a..5469eef22a 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -408,3 +408,37 @@ unsafe impl ObjectType for Clock {
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
 }
 qom_isa!(Clock: Object);
+
+#[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: {
+                ::common::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) },
+            ..::common::zeroable::Zeroable::ZERO
+         } $(.with_varray_flag_unchecked(
+                  $crate::call_func_with_field!(
+                      $crate::vmstate::vmstate_varray_flag,
+                      $struct_name,
+                      $num
+                  )
+              )
+           $(.with_varray_multiply($factor))?)?
+    }};
+}
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 4e2b7e2db0..ff64d2c6a2 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -504,40 +504,6 @@ macro_rules! vmstate_struct {
     };
 }
 
-#[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: {
-                ::common::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) },
-            ..::common::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.
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 06/18] rust: move VMState handling to QOM module
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (4 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 05/18] rust: move vmstate_clock!() to qdev module marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 07/18] rust: move Cell vmstate impl marcandre.lureau
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
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>
---
 rust/qemu-api/src/qom.rs     |  3 +++
 rust/qemu-api/src/vmstate.rs | 10 +++++-----
 2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index c2f9a682bb..56feff3e17 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -110,6 +110,7 @@
         object_get_typename, object_new, object_ref, object_unref, TypeInfo,
     },
     cell::bql_locked,
+    impl_vmstate_pointer,
 };
 
 /// A safe wrapper around [`bindings::Object`].
@@ -949,3 +950,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 ff64d2c6a2..c1e2b06390 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -29,8 +29,8 @@
 
 use common::{callbacks::FnCall, Zeroable};
 
+use crate::bindings::VMStateFlags;
 pub use crate::bindings::{VMStateDescription, VMStateField};
-use crate::{bindings::VMStateFlags, prelude::*, qom::Owned};
 
 /// This macro is used to call a function with a generic argument bound
 /// to the type of a field.  The function must take a
@@ -396,11 +396,12 @@ 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 SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
-            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 SCALAR_TYPE: $crate::vmstate::VMStateFieldType = <T as $crate::vmstate::VMState>::SCALAR_TYPE;
+            const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag();
         }
     };
 }
@@ -412,7 +413,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.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 07/18] rust: move Cell vmstate impl
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (5 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 06/18] rust: move VMState handling to QOM module marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 18:28   ` Paolo Bonzini
  2025-08-26 14:04 ` [RFC 08/18] rust: split "migration" crate marcandre.lureau
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
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>
---
 rust/qemu-api/src/cell.rs    |  5 ++++-
 rust/qemu-api/src/vmstate.rs | 16 +++++++---------
 2 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index 98d720caf9..4bce526e45 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -152,7 +152,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() {
@@ -866,3 +866,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         (**self).fmt(f)
     }
 }
+
+impl_vmstate_transparent!(BqlCell<T> where T: VMState);
+impl_vmstate_transparent!(BqlRefCell<T> where T: VMState);
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index c1e2b06390..9d33997c57 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -29,8 +29,7 @@
 
 use common::{callbacks::FnCall, Zeroable};
 
-use crate::bindings::VMStateFlags;
-pub use crate::bindings::{VMStateDescription, VMStateField};
+pub use crate::bindings::{VMStateDescription, VMStateField, VMStateFlags};
 
 /// This macro is used to call a function with a generic argument bound
 /// to the type of a field.  The function must take a
@@ -325,15 +324,16 @@ 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 SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE;
-            const BASE: VMStateField = VMStateField {
+        unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
+            const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = <$base as $crate::vmstate::VMState>::SCALAR_TYPE;
+            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::vmstate::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG;
         }
     };
 }
@@ -341,8 +341,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!(::common::Opaque<T> where T: VMState);
 
 #[macro_export]
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 08/18] rust: split "migration" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (6 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 07/18] rust: move Cell vmstate impl marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 09/18] rust: split "bql" crate marcandre.lureau
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@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            |   8 +-
 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            |   8 +-
 rust/meson.build                            |   1 +
 rust/migration/Cargo.toml                   |  21 ++++
 rust/migration/build.rs                     |  43 ++++++++
 rust/migration/meson.build                  |  57 +++++++++++
 rust/migration/src/bindings.rs              |  48 +++++++++
 rust/migration/src/lib.rs                   |   4 +
 rust/{qemu-api => migration}/src/vmstate.rs | 104 +++++++++++---------
 rust/qemu-api/Cargo.toml                    |   1 +
 rust/qemu-api/meson.build                   |  10 +-
 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                   |  10 +-
 rust/qemu-api/src/qom.rs                    |   2 +-
 rust/qemu-api/tests/tests.rs                |   2 +-
 rust/qemu-api/tests/vmstate_tests.rs        |  16 +--
 29 files changed, 338 insertions(+), 97 deletions(-)
 create mode 100644 rust/migration/wrapper.h
 create mode 100644 rust/migration/Cargo.toml
 create mode 100644 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 (86%)
diff --git a/MAINTAINERS b/MAINTAINERS
index 22d7847804..bb4f5f011f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3511,6 +3511,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 0000000000..daf316aed4
--- /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 cc7112406b..b99df9f568 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 5cc3306b21..58bfbaf545 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -68,6 +68,7 @@ name = "hpet"
 version = "0.1.0"
 dependencies = [
  "common",
+ "migration",
  "qemu_api",
  "qemu_api_macros",
  "util",
@@ -88,6 +89,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"
@@ -96,6 +106,7 @@ dependencies = [
  "bilge-impl",
  "bits",
  "common",
+ "migration",
  "qemu_api",
  "qemu_api_macros",
  "util",
@@ -141,6 +152,7 @@ dependencies = [
  "common",
  "foreign",
  "libc",
+ "migration",
  "qemu_api_macros",
  "util",
 ]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 04dac19c32..df6a385d64 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 0cf9943fe8..7fd7531823 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 41cf46ead1..e3ddd17351 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,
     util_rs,
+    migration_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index eb94cf6ba7..904faa80a9 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -9,18 +9,20 @@
 };
 
 use common::{static_assert, uninit_field_mut, Zeroable};
+use migration::{
+    impl_vmstate_forward, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections,
+    vmstate_unused, VMStateDescription,
+};
 use qemu_api::{
     bindings::{qdev_prop_bool, qdev_prop_chr},
     chardev::{CharBackend, Chardev, Event},
-    impl_vmstate_forward,
     irq::{IRQState, InterruptSource},
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
     qom::{ObjectImpl, Owned, ParentField, ParentInit},
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    vmstate::VMStateDescription,
-    vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
+    vmstate_clock,
 };
 use util::{log::Log, log_mask_ln};
 
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index 7ececd39f8..2bfbd81095 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 dd9a5ed3d4..70acdf03d6 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 f413893aa5..2c605dcf9d 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,
     util_rs,
+    migration_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 672c88c46c..35b968cca7 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -11,6 +11,10 @@
 };
 
 use common::{bitops::IntegerExt, uninit_field_mut, Zeroable};
+use migration::{
+    vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
+    VMStateDescription, VMStateFieldHelper,
+};
 use qemu_api::{
     bindings::{
         address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
@@ -26,8 +30,6 @@
     qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
     qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
-    vmstate::VMStateDescription,
-    vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
 };
 use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
@@ -1021,7 +1023,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),
+        VMStateFieldHelper(vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers)).with_version_id(0).0,
     },
     subsections: vmstate_subsections! {
         VMSTATE_HPET_RTC_IRQ_LEVEL,
diff --git a/rust/meson.build b/rust/meson.build
index a9d715e6e9..826949b2e6 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 0000000000..98e6df2109
--- /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 100644
index 0000000000..50be521b16
--- /dev/null
+++ b/rust/migration/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/migration/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"))
+    };
+
+    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(())
+}
diff --git a/rust/migration/meson.build b/rust/migration/meson.build
new file mode 100644
index 0000000000..d6d6a7c98e
--- /dev/null
+++ b/rust/migration/meson.build
@@ -0,0 +1,57 @@
+_migration_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+_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',
+  rust_args: _migration_cfg,
+  dependencies: [qemuutil_rs, common_rs, util_rs, migration],
+)
+
+migration_rs = declare_dependency(link_with: [_migration_rs],
+  dependencies: [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-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 0000000000..8ce13a9000
--- /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 0000000000..f2a6bb9f27
--- /dev/null
+++ b/rust/migration/src/lib.rs
@@ -0,0 +1,4 @@
+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 86%
rename from rust/qemu-api/src/vmstate.rs
rename to rust/migration/src/vmstate.rs
index 9d33997c57..4f95ab0d49 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -27,7 +27,7 @@
 use core::{marker::PhantomData, mem, ptr::NonNull};
 use std::ffi::{c_int, c_void};
 
-use common::{callbacks::FnCall, Zeroable};
+use common::callbacks::FnCall;
 
 pub use crate::bindings::{VMStateDescription, VMStateField, VMStateFlags};
 
@@ -39,7 +39,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>()
@@ -197,6 +197,10 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 /// for them.  The macros
 /// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized)
 /// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) 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)? $(,)?) => {
@@ -214,20 +218,25 @@ macro_rules! vmstate_of {
                 $struct_name,
                 $field_name
             )),
-            ..$crate::call_func_with_field!(
+            ..$crate::vmstate::VMStateFieldHelper($crate::call_func_with_field!(
                 $crate::vmstate::vmstate_base,
                 $struct_name,
                 $field_name
-            )$(.with_varray_flag($crate::call_func_with_field!(
+            ))$(.with_varray_flag($crate::call_func_with_field!(
                     $crate::vmstate::vmstate_varray_flag,
                     $struct_name,
                     $num))
                $(.with_varray_multiply($factor))?)?
+            .0
         }
     };
 }
 
-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
@@ -236,62 +245,66 @@ impl VMStateFlags {
     );
 }
 
+// using extension traits would be nicer, unfortunately it doesn't allow const
+// fn yet
+pub struct VMStateFieldHelper(pub VMStateField);
+
 // Add a couple builder-style methods to VMStateField, allowing
 // easy derivation of VMStateField constants from other types.
-impl VMStateField {
+impl VMStateFieldHelper {
     #[must_use]
     pub const fn with_version_id(mut self, version_id: i32) -> Self {
         assert!(version_id >= 0);
-        self.version_id = version_id;
+        self.0.version_id = version_id;
         self
     }
 
     #[must_use]
     pub const fn with_array_flag(mut self, num: usize) -> Self {
         assert!(num <= 0x7FFF_FFFFusize);
-        assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0);
-        assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0);
-        if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 {
-            self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0);
-            self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0);
+        assert!((self.0.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0);
+        assert!((self.0.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0);
+        if (self.0.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 {
+            self.0.flags = VMStateFlags(self.0.flags.0 & !VMStateFlags::VMS_POINTER.0);
+            self.0.flags = VMStateFlags(self.0.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0);
             // VMS_ARRAY_OF_POINTER flag stores the size of pointer.
             // FIXME: *const, *mut, NonNull and Box<> have the same size as usize.
             //        Resize if more smart pointers are supported.
-            self.size = std::mem::size_of::<usize>();
+            self.0.size = std::mem::size_of::<usize>();
         }
-        self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0);
-        self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0);
-        self.num = num as i32;
+        self.0.flags = VMStateFlags(self.0.flags.0 & !VMStateFlags::VMS_SINGLE.0);
+        self.0.flags = VMStateFlags(self.0.flags.0 | VMStateFlags::VMS_ARRAY.0);
+        self.0.num = num as i32;
         self
     }
 
     #[must_use]
     pub const fn with_pointer_flag(mut self) -> Self {
-        assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0);
-        self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0);
+        assert!((self.0.flags.0 & VMStateFlags::VMS_POINTER.0) == 0);
+        self.0.flags = VMStateFlags(self.0.flags.0 | VMStateFlags::VMS_POINTER.0);
         self
     }
 
     #[must_use]
-    pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField {
-        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.
+    pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self {
+        self.0.flags = VMStateFlags(self.0.flags.0 & !VMStateFlags::VMS_ARRAY.0);
+        self.0.flags = VMStateFlags(self.0.flags.0 | flag.0);
+        self.0.num = 0; // varray uses num_offset instead of num.
         self
     }
 
     #[must_use]
     #[allow(unused_mut)]
-    pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField {
-        assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0);
+    pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self {
+        assert!((self.0.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;
+        self.0.flags = VMStateFlags(self.0.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0);
+        self.0.num = num as i32;
         self
     }
 }
@@ -303,7 +316,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);
 /// ```
@@ -341,7 +354,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 {
@@ -367,12 +380,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 SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info;
-            const BASE: VMStateField = VMStateField {
-                size: mem::size_of::<$type>(),
-                flags: VMStateFlags::VMS_SINGLE,
-                ..Zeroable::ZERO
+        unsafe impl $crate::vmstate::VMState for $type {
+            const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = $crate::vmstate::VMStateFieldType::$info;
+            const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField {
+                size: ::std::mem::size_of::<$type>(),
+                flags: $crate::vmstate::VMStateFlags::VMS_SINGLE,
+                ..::common::zeroable::Zeroable::ZERO
             };
             $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)?
         }
@@ -399,7 +412,7 @@ macro_rules! impl_vmstate_pointer {
     ($type:ty where $base:tt: VMState $($where:tt)*) => {
         unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
             const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = <T as $crate::vmstate::VMState>::SCALAR_TYPE;
-            const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag();
+            const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateFieldHelper(<$base as $crate::vmstate::VMState>::BASE).with_pointer_flag().0;
         }
     };
 }
@@ -417,7 +430,9 @@ unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmsta
 
 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);
+    const BASE: VMStateField = VMStateFieldHelper(<T as VMState>::BASE)
+        .with_array_flag(N)
+        .0;
 }
 
 #[doc(alias = "VMSTATE_UNUSED")]
@@ -429,7 +444,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,
-            ..::common::zeroable::Zeroable::ZERO
+            ..::common::Zeroable::ZERO
         }
     }};
 }
@@ -452,7 +467,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 {
             let _: () = F::ASSERT_IS_SOME;
@@ -477,7 +492,7 @@ const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
 #[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 {
+        $crate::vmstate::VMStateFieldHelper($crate::bindings::VMStateField {
             name: ::core::concat!(::core::stringify!($field_name), "\0")
                 .as_bytes()
                 .as_ptr() as *const ::std::os::raw::c_char,
@@ -490,8 +505,8 @@ macro_rules! vmstate_struct {
             flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
             vmsd: $vmsd,
             $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
-            ..::common::zeroable::Zeroable::ZERO
-         } $(.with_varray_flag_unchecked(
+            ..::common::Zeroable::ZERO
+         }) $(.with_varray_flag_unchecked(
                   $crate::call_func_with_field!(
                       $crate::vmstate::vmstate_varray_flag,
                       $struct_name,
@@ -499,6 +514,7 @@ macro_rules! vmstate_struct {
                   )
               )
            $(.with_varray_multiply($factor))?)?
+         .0
     };
 }
 
@@ -512,7 +528,7 @@ macro_rules! vmstate_fields {
             $($field),*,
             $crate::bindings::VMStateField {
                 flags: $crate::bindings::VMStateFlags::VMS_END,
-                ..::common::zeroable::Zeroable::ZERO
+                ..::common::Zeroable::ZERO
             }
         ];
         _FIELDS.as_ptr()
@@ -531,7 +547,7 @@ macro_rules! vmstate_validate {
                     | $crate::bindings::VMStateFlags::VMS_ARRAY.0,
             ),
             num: 0, // 0 elements: no data, only run test_fn callback
-            ..::common::zeroable::Zeroable::ZERO
+            ..common::Zeroable::ZERO
         }
     };
 }
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 28e5969e7a..e040b93224 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" }
 anyhow = "~1.0"
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 8a2950dfe4..ee6311cd3f 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,14 +59,13 @@ _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, qemuutil_rs, util_rs,
+  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs,
                  qom, hwcore, chardev, migration],
 )
 
@@ -93,7 +91,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]),
+        dependencies: [common_rs, util_rs, migration_rs, qemu_api]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index aedf42b652..ce00a6e0e4 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 4bce526e45..70cee2ca7a 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 db81841a8f..52e4d5889b 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -20,4 +20,3 @@
 pub mod qdev;
 pub mod qom;
 pub mod sysbus;
-pub mod vmstate;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 3d771481e4..c10c171158 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 5469eef22a..15e1fdff96 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -11,6 +11,7 @@
 
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use common::{callbacks::FnCall, Opaque};
+use migration::vmstate::VMStateDescription;
 pub use util::{Error, Result};
 
 use crate::{
@@ -20,7 +21,6 @@
     irq::InterruptSource,
     prelude::*,
     qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
-    vmstate::VMStateDescription,
 };
 
 /// A safe wrapper around [`bindings::Clock`].
@@ -413,7 +413,7 @@ unsafe impl ObjectType for Clock {
 #[macro_export]
 macro_rules! vmstate_clock {
     ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{
-        $crate::bindings::VMStateField {
+        ::migration::VMStateField {
             name: ::core::concat!(::core::stringify!($field_name), "\0")
                 .as_bytes()
                 .as_ptr() as *const ::std::os::raw::c_char,
@@ -426,9 +426,9 @@ macro_rules! vmstate_clock {
                 ::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,
+            flags: ::migration::VMStateFlags(
+                ::migration::VMStateFlags::VMS_STRUCT.0
+                    | ::migration::VMStateFlags::VMS_POINTER.0,
             ),
             vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
             ..::common::zeroable::Zeroable::ZERO
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 56feff3e17..901445013c 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 1dd4e29754..2ea4f88dd1 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -5,6 +5,7 @@
 use std::{ffi::CStr, ptr::addr_of};
 
 use common::Zeroable;
+use migration::VMStateDescription;
 use qemu_api::{
     bindings::qdev_prop_bool,
     cell::{self, BqlCell},
@@ -13,7 +14,6 @@
     qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
     qom::{ObjectImpl, ParentField},
     sysbus::SysBusDevice,
-    vmstate::VMStateDescription,
 };
 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 1ec80c483e..3f64eb46c0 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -10,16 +10,16 @@
 };
 
 use common::{Opaque, Zeroable};
-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,
-    vmstate::{VMStateDescription, VMStateField},
+    vmstate::{VMStateDescription, VMStateField, VMStateFieldHelper},
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
 };
+use qemu_api::cell::BqlCell;
 
 const FOO_ARRAY_MAX: usize = 3;
 
@@ -48,7 +48,7 @@ struct FooA {
     fields: vmstate_fields! {
         vmstate_of!(FooA, elem),
         vmstate_unused!(size_of::<i64>()),
-        vmstate_of!(FooA, arr[0 .. num]).with_version_id(0),
+        VMStateFieldHelper(vmstate_of!(FooA, arr[0 .. num])).with_version_id(0).0,
         vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]),
     },
     ..Zeroable::ZERO
@@ -176,10 +176,10 @@ fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
     version_id: 2,
     minimum_version_id: 1,
     fields: vmstate_fields! {
-        vmstate_of!(FooB, val).with_version_id(2),
+        VMStateFieldHelper(vmstate_of!(FooB, val)).with_version_id(2).0,
         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),
+        VMStateFieldHelper(vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA)).with_version_id(1).0,
+        VMStateFieldHelper(vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA)).with_version_id(2).0,
         vmstate_of!(FooB, arr_i64),
         vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
     },
@@ -340,7 +340,7 @@ struct FooC {
     version_id: 3,
     minimum_version_id: 1,
     fields: vmstate_fields! {
-        vmstate_of!(FooC, ptr).with_version_id(2),
+        VMStateFieldHelper(vmstate_of!(FooC, ptr)).with_version_id(2).0,
         // 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>>()),
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 09/18] rust: split "bql" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (7 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 08/18] rust: split "migration" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 10/18] rust: split "qom" crate marcandre.lureau
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
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>
---
 MAINTAINERS                          |  1 +
 rust/bql/wrapper.h                   | 27 ++++++++
 rust/Cargo.lock                      | 10 +++
 rust/Cargo.toml                      |  1 +
 rust/bql/Cargo.toml                  | 23 +++++++
 rust/bql/build.rs                    | 43 +++++++++++++
 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             |  1 +
 rust/qemu-api/meson.build            |  9 +--
 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, 286 insertions(+), 107 deletions(-)
 create mode 100644 rust/bql/wrapper.h
 create mode 100644 rust/bql/Cargo.toml
 create mode 100644 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 bb4f5f011f..0b5f327d4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3510,6 +3510,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 0000000000..2ef9a96e1d
--- /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 58bfbaf545..76ff79f81a 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"
@@ -67,6 +74,7 @@ dependencies = [
 name = "hpet"
 version = "0.1.0"
 dependencies = [
+ "bql",
  "common",
  "migration",
  "qemu_api",
@@ -105,6 +113,7 @@ dependencies = [
  "bilge",
  "bilge-impl",
  "bits",
+ "bql",
  "common",
  "migration",
  "qemu_api",
@@ -149,6 +158,7 @@ name = "qemu_api"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "bql",
  "common",
  "foreign",
  "libc",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index df6a385d64..3ce1977ee0 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 0000000000..1041bd4ea9
--- /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 100644
index 0000000000..5a68a1e64a
--- /dev/null
+++ b/rust/bql/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/bql/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"))
+    };
+
+    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(())
+}
diff --git a/rust/bql/meson.build b/rust/bql/meson.build
new file mode 100644
index 0000000000..465774a5b2
--- /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,
+  dependencies: [migration_rs, qemuutil_rs],
+)
+
+bql_rs = declare_dependency(link_with: [_bql_rs],
+  dependencies: [qemuutil_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-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 0000000000..9ffff12cde
--- /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 70cee2ca7a..43109130c5 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();
@@ -445,7 +421,7 @@ impl<T> BqlRefCell<T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlRefCell;
+    /// use bql::BqlRefCell;
     ///
     /// let c = BqlRefCell::new(5);
     /// ```
@@ -504,8 +480,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);
     ///
@@ -516,8 +492,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);
     ///
@@ -534,7 +510,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.
@@ -558,8 +534,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());
     ///
@@ -571,8 +547,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();
@@ -589,7 +565,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()) };
@@ -608,7 +584,7 @@ pub fn borrow_mut(&self) -> BqlRefMut<'_, T> {
     /// # Examples
     ///
     /// ```
-    /// use qemu_api::cell::BqlRefCell;
+    /// use bql::BqlRefCell;
     ///
     /// let c = BqlRefCell::new(5);
     ///
@@ -733,7 +709,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)
     }
 }
 
@@ -823,7 +799,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 0000000000..ef08221e9c
--- /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 f96b91e25a..e076964634 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 7fd7531823..1a1d4ba715 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 e3ddd17351..7062497b7c 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,
     util_rs,
     migration_rs,
+    bql_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 904faa80a9..7cffb894a8 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -8,6 +8,7 @@
     ptr::NonNull,
 };
 
+use bql::BqlRefCell;
 use common::{static_assert, uninit_field_mut, Zeroable};
 use migration::{
     impl_vmstate_forward, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections,
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 70acdf03d6..9fcec38bfa 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 2c605dcf9d..5e01e57210 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,
     util_rs,
     migration_rs,
+    bql_rs,
     qemu_api,
     qemu_api_macros,
   ],
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 35b968cca7..c0e52ce415 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, Zeroable};
 use migration::{
     vmstate_fields, vmstate_of, vmstate_struct, 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 0605225fbb..e569b57b93 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 826949b2e6..2ba1ea2280 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 4f95ab0d49..243f31baf6 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -198,8 +198,8 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
 /// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized)
 /// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) 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 e040b93224..511eb6cb89 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -17,6 +17,7 @@ rust-version.workspace = true
 common = { path = "../common" }
 migration = { path = "../migration" }
 util = { path = "../util" }
+bql = { path = "../bql" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 anyhow = "~1.0"
 libc = "0.2.162"
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index ee6311cd3f..dd829e3348 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,7 +60,7 @@ _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, util_rs, migration_rs,
+  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs,
                  qom, hwcore, chardev, migration],
 )
 
@@ -91,7 +86,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]),
+        dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs
index c2bea23e82..d07e263193 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, 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 ea6b32848c..3063fbe97a 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 52e4d5889b..37e6c21946 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 c10c171158..9da7313016 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 15e1fdff96..6875c32be7 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::*,
@@ -275,7 +274,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
@@ -346,7 +345,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 901445013c..e797958e4e 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 2dbfc31dbd..b21883246e 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 2ea4f88dd1..bc4bd320ce 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -4,11 +4,11 @@
 
 use std::{ffi::CStr, ptr::addr_of};
 
+use bql::BqlCell;
 use common::Zeroable;
 use migration::VMStateDescription;
 use qemu_api::{
     bindings::qdev_prop_bool,
-    cell::{self, BqlCell},
     declare_properties, define_property,
     prelude::*,
     qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
@@ -113,7 +113,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 3f64eb46c0..f808aed2c9 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, Zeroable};
 use migration::{
     bindings::{
@@ -19,7 +20,6 @@
     vmstate::{VMStateDescription, VMStateField, VMStateFieldHelper},
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
 };
-use qemu_api::cell::BqlCell;
 
 const FOO_ARRAY_MAX: usize = 3;
 
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 10/18] rust: split "qom" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (8 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 09/18] rust: split "bql" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-27  6:55   ` Zhao Liu
  2025-08-26 14:04 ` [RFC 11/18] rust: split "chardev" crate marcandre.lureau
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@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  |  5 ++-
 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      |  5 ++-
 rust/qemu-api/src/irq.rs          | 12 +++---
 rust/qemu-api/src/lib.rs          |  1 -
 rust/qemu-api/src/memory.rs       |  9 ++---
 rust/qemu-api/src/prelude.rs      | 11 ------
 rust/qemu-api/src/qdev.rs         | 22 +++++++----
 rust/qemu-api/src/sysbus.rs       |  8 ++--
 rust/qemu-api/tests/tests.rs      | 12 +++---
 rust/qom/Cargo.toml               | 23 ++++++++++++
 rust/qom/build.rs                 | 43 ++++++++++++++++++++++
 rust/qom/meson.build              | 61 +++++++++++++++++++++++++++++++
 rust/qom/src/bindings.rs          | 25 +++++++++++++
 rust/qom/src/lib.rs               |  4 ++
 rust/{qemu-api => qom}/src/qom.rs |  4 +-
 rust/qom/tests/tests.rs           | 47 ++++++++++++++++++++++++
 32 files changed, 313 insertions(+), 57 deletions(-)
 create mode 100644 rust/qom/wrapper.h
 create mode 100644 rust/qom/Cargo.toml
 create mode 100644 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
 rename rust/{qemu-api => qom}/src/qom.rs (99%)
 create mode 100644 rust/qom/tests/tests.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 0b5f327d4f..8054913502 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3515,6 +3515,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 0000000000..3b71bcd3f5
--- /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 76ff79f81a..0f1dcea6e4 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -79,6 +79,7 @@ dependencies = [
  "migration",
  "qemu_api",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -118,6 +119,7 @@ dependencies = [
  "migration",
  "qemu_api",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -164,6 +166,7 @@ dependencies = [
  "libc",
  "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 3ce1977ee0..fd7cf9b0e1 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 1a1d4ba715..da89f78727 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 7062497b7c..cd855408a5 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -13,6 +13,7 @@ _libpl011_rs = static_library(
     bql_rs,
     qemu_api,
     qemu_api_macros,
+    qom_rs,
   ],
 )
 
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 7cffb894a8..a3bcd1297a 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -21,10 +21,13 @@
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
-    qom::{ObjectImpl, Owned, ParentField, ParentInit},
     sysbus::{SysBusDevice, SysBusDeviceImpl},
     vmstate_clock,
 };
+use qom::{
+    qom_isa, IsA, Object, ObjectClassMethods, ObjectDeref, ObjectImpl, ObjectMethods, ObjectType,
+    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 9fcec38bfa..19456ec72b 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 5e01e57210..195dc48e1c 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -10,6 +10,7 @@ _libhpet_rs = static_library(
     bql_rs,
     qemu_api,
     qemu_api_macros,
+    qom_rs,
   ],
 )
 
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index c0e52ce415..b876b35a43 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::{qom_isa, Object, ObjectImpl, ObjectType, 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 2ba1ea2280..043603d416 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 243f31baf6..58a4396c30 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -200,7 +200,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 b5f77f06f5..5d52f22441 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -89,11 +89,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 52683e46d5..b6da07f24c 100644
--- a/rust/qemu-api-macros/src/tests.rs
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -61,11 +61,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 511eb6cb89..333cef1c5d 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -19,6 +19,7 @@ migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
 qemu_api_macros = { path = "../qemu-api-macros" }
+qom = { path = "../qom" }
 anyhow = "~1.0"
 libc = "0.2.162"
 foreign = "~0.3.1"
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index dd829e3348..c5038aa5bd 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},
@@ -60,7 +65,7 @@ _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, util_rs, migration_rs, bql_rs,
+  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs,
                  qom, hwcore, chardev, migration],
 )
 
@@ -86,7 +91,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]),
+        dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index ce00a6e0e4..525f136ae2 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 d07e263193..1a6795a938 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, Opaque};
+use qom::{Object, ObjectType};
 
-use crate::{bindings, prelude::*};
+use crate::bindings;
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
@@ -257,4 +258,4 @@ unsafe impl ObjectType for Chardev {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CHARDEV) };
 }
-qom_isa!(Chardev: Object);
+qom::qom_isa!(Chardev: Object);
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index 3063fbe97a..5a380923fe 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::{Object, ObjectClass, ObjectType};
 
-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);
+
+qom::qom_isa!(IRQState: Object);
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 37e6c21946..f9551c13a5 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -17,5 +17,4 @@
 pub mod irq;
 pub mod memory;
 pub mod qdev;
-pub mod qom;
 pub mod sysbus;
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
index f790cb5fd2..480cc6b0c5 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::{IsA, Object, ObjectType};
 
-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,7 +184,8 @@ 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);
+
+qom::qom_isa!(MemoryRegion: Object);
 
 /// A special `MemTxAttrs` constant, used to indicate that no memory
 /// attributes are specified.
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 9da7313016..9e9d1c8247 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 6875c32be7..a94af90bb1 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -12,14 +12,16 @@
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use common::{callbacks::FnCall, Opaque};
 use migration::vmstate::VMStateDescription;
+use qom::{
+    InterfaceType, IsA, Object, ObjectCast, ObjectClass, ObjectDeref, ObjectImpl, ObjectType,
+    Owned, ParentInit,
+};
 pub 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`].
@@ -164,10 +166,14 @@ pub fn class_init<T: ResettablePhasesImpl>(&mut self) {
     }
 }
 
-impl DeviceClass {
+pub trait DeviceClassExt {
+    fn class_init<T: DeviceImpl>(&mut self);
+}
+
+impl DeviceClassExt for DeviceClass {
     /// Fill in the virtual methods of `DeviceClass` based on the definitions in
     /// the `DeviceImpl` trait.
-    pub fn class_init<T: DeviceImpl>(&mut self) {
+    fn class_init<T: DeviceImpl>(&mut self) {
         if <T as DeviceImpl>::REALIZE.is_some() {
             self.realize = Some(rust_realize_fn::<T>);
         }
@@ -244,7 +250,8 @@ unsafe impl ObjectType for DeviceState {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
 }
-qom_isa!(DeviceState: Object);
+
+qom::qom_isa!(DeviceState: Object);
 
 /// Initialization methods take a [`ParentInit`] and can be called as
 /// associated functions.
@@ -406,7 +413,8 @@ unsafe impl ObjectType for Clock {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
 }
-qom_isa!(Clock: Object);
+
+qom::qom_isa!(Clock: Object);
 
 #[doc(alias = "VMSTATE_CLOCK")]
 #[macro_export]
@@ -420,7 +428,7 @@ macro_rules! vmstate_clock {
                 ::common::assert_field_type!(
                     $struct_name,
                     $field_name,
-                    $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
+                    ::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
                 );
                 ::std::mem::offset_of!($struct_name, $field_name)
             },
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index b21883246e..016e57935a 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::{IsA, Object, ObjectCast, ObjectDeref, ObjectType, Owned};
 
 use crate::{
     bindings,
     irq::{IRQState, InterruptSource},
     memory::MemoryRegion,
-    prelude::*,
-    qdev::{DeviceImpl, DeviceState},
-    qom::Owned,
+    qdev::{DeviceClassExt, DeviceImpl, DeviceState},
 };
 
 /// A safe wrapper around [`bindings::SysBusDevice`].
@@ -31,7 +30,8 @@ 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);
+
+qom::qom_isa!(SysBusDevice: DeviceState, Object);
 
 // TODO: add virtual methods
 pub trait SysBusDeviceImpl: DeviceImpl + IsA<SysBusDevice> {}
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index bc4bd320ce..821aabe831 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -10,11 +10,13 @@
 use qemu_api::{
     bindings::qdev_prop_bool,
     declare_properties, define_property,
-    prelude::*,
-    qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
-    qom::{ObjectImpl, ParentField},
+    qdev::{DeviceClassExt, DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
     sysbus::SysBusDevice,
 };
+use qom::{
+    Object, ObjectCast, ObjectClassMethods, ObjectDeref, ObjectImpl, ObjectMethods, ObjectType,
+    ParentField,
+};
 use util::bindings::{module_call_init, module_init_type};
 
 mod vmstate_tests;
@@ -33,7 +35,7 @@ pub struct DummyState {
     migrate_clock: bool,
 }
 
-qom_isa!(DummyState: Object, DeviceState);
+qom::qom_isa!(DummyState: Object, DeviceState);
 
 pub struct DummyClass {
     parent_class: <DeviceState as ObjectType>::Class,
@@ -84,7 +86,7 @@ pub struct DummyChildState {
     parent: ParentField<DummyState>,
 }
 
-qom_isa!(DummyChildState: Object, DeviceState, DummyState);
+qom::qom_isa!(DummyChildState: Object, DeviceState, DummyState);
 
 pub struct DummyChildClass {
     parent_class: <DummyState as ObjectType>::Class,
diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml
new file mode 100644
index 0000000000..46bbf7c7fe
--- /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 100644
index 0000000000..aca1c54cb8
--- /dev/null
+++ b/rust/qom/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/qom/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"))
+    };
+
+    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(())
+}
diff --git a/rust/qom/meson.build b/rust/qom/meson.build
new file mode 100644
index 0000000000..6e95d75fa0
--- /dev/null
+++ b/rust/qom/meson.build
@@ -0,0 +1,61 @@
+_qom_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+# 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/qom.rs',
+    ],
+    {'.': _qom_bindings_inc_rs}
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  rust_args: _qom_cfg,
+  dependencies: [qemuutil_rs, bql_rs, common_rs, migration_rs, qemu_api_macros, qom],
+)
+
+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'])
+
+test('rust-qom-rs-integration',
+    executable(
+        'rust-qom-rs-integration',
+        files('tests/tests.rs'),
+        override_options: ['rust_std=2021', 'build.rust_std=2021'],
+        rust_args: ['--test'],
+        install: false,
+        dependencies: [common_rs, qom_rs, bql_rs, util_rs]),
+    args: [
+        '--test', '--test-threads', '1',
+        '--format', 'pretty',
+    ],
+    protocol: 'rust',
+    suite: ['unit', 'rust'])
diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs
new file mode 100644
index 0000000000..9ffff12cde
--- /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 0000000000..22520906cf
--- /dev/null
+++ b/rust/qom/src/lib.rs
@@ -0,0 +1,4 @@
+pub mod bindings;
+
+mod qom;
+pub use qom::*;
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 e797958e4e..a632ec43f2 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 {
diff --git a/rust/qom/tests/tests.rs b/rust/qom/tests/tests.rs
new file mode 100644
index 0000000000..49f1cbecf5
--- /dev/null
+++ b/rust/qom/tests/tests.rs
@@ -0,0 +1,47 @@
+use std::{ffi::CStr, sync::LazyLock};
+
+use qom::{qom_isa, Object, ObjectClassMethods, ObjectImpl, ObjectType, ParentField};
+use util::bindings::{module_call_init, module_init_type};
+
+#[repr(C)]
+#[derive(qemu_api_macros::Object)]
+pub struct DummyObject {
+    parent: ParentField<Object>,
+}
+
+qom_isa!(DummyObject: Object);
+
+pub struct DummyClass {}
+
+impl DummyClass {
+    pub fn class_init(self: &mut DummyClass) {
+        //
+    }
+}
+
+unsafe impl ObjectType for DummyObject {
+    type Class = DummyClass;
+    const TYPE_NAME: &'static CStr = c"dummy";
+}
+
+impl ObjectImpl for DummyObject {
+    type ParentType = Object;
+    const ABSTRACT: bool = false;
+    const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init;
+}
+
+fn init_qom() {
+    static ONCE: LazyLock<()> = LazyLock::new(|| unsafe {
+        module_call_init(module_init_type::MODULE_INIT_QOM);
+    });
+
+    bql::start_test();
+    LazyLock::force(&ONCE);
+}
+
+#[test]
+/// Create and immediately drop an instance.
+fn test_object_new() {
+    init_qom();
+    drop(DummyObject::new());
+}
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 11/18] rust: split "chardev" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (9 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 10/18] rust: split "qom" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 12/18] rust: split "system" crate marcandre.lureau
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@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                     | 43 ++++++++++++++++++
 rust/chardev/meson.build                  | 54 +++++++++++++++++++++++
 rust/chardev/src/bindings.rs              | 36 +++++++++++++++
 rust/{qemu-api => chardev}/src/chardev.rs |  0
 rust/chardev/src/lib.rs                   |  4 ++
 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                 |  7 ++-
 rust/qemu-api/src/bindings.rs             |  9 +---
 rust/qemu-api/src/lib.rs                  |  1 -
 rust/qemu-api/src/qdev.rs                 |  2 +-
 19 files changed, 214 insertions(+), 17 deletions(-)
 create mode 100644 rust/chardev/wrapper.h
 create mode 100644 rust/chardev/Cargo.toml
 create mode 100644 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 8054913502..4ab89b208c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3511,6 +3511,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 0000000000..65ede6ea6d
--- /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 b99df9f568..07dbc9987a 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 0f1dcea6e4..ac325969cc 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"
@@ -115,6 +127,7 @@ dependencies = [
  "bilge-impl",
  "bits",
  "bql",
+ "chardev",
  "common",
  "migration",
  "qemu_api",
@@ -161,6 +174,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "bql",
+ "chardev",
  "common",
  "foreign",
  "libc",
diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml
new file mode 100644
index 0000000000..7df9c677fc
--- /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 100644
index 0000000000..b684233f7f
--- /dev/null
+++ b/rust/chardev/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/chardev/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"))
+    };
+
+    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(())
+}
diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build
new file mode 100644
index 0000000000..918e0a5224
--- /dev/null
+++ b/rust/chardev/meson.build
@@ -0,0 +1,54 @@
+_chardev_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+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',
+  rust_args: _chardev_cfg,
+  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_api_macros, qom_rs, util_rs, chardev],
+)
+
+chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev])
+
+# 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-chardev-rs-doctests',
+     _chardev_rs,
+     protocol: 'rust',
+     dependencies: chardev_rs,
+     suite: ['doc', 'rust'])
diff --git a/rust/chardev/src/bindings.rs b/rust/chardev/src/bindings.rs
new file mode 100644
index 0000000000..2d98026d62
--- /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 0000000000..2863b4c7ad
--- /dev/null
+++ b/rust/chardev/src/lib.rs
@@ -0,0 +1,4 @@
+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 da89f78727..f7ad5f8e08 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 cd855408a5..552ccc0d60 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -14,6 +14,7 @@ _libpl011_rs = static_library(
     qemu_api,
     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 a3bcd1297a..b1c65bd79c 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -9,6 +9,7 @@
 };
 
 use bql::BqlRefCell;
+use chardev::{CharBackend, Chardev, Event};
 use common::{static_assert, uninit_field_mut, Zeroable};
 use migration::{
     impl_vmstate_forward, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections,
@@ -16,7 +17,6 @@
 };
 use qemu_api::{
     bindings::{qdev_prop_bool, qdev_prop_chr},
-    chardev::{CharBackend, Chardev, Event},
     irq::{IRQState, InterruptSource},
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
diff --git a/rust/meson.build b/rust/meson.build
index 043603d416..4d9e291223 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 333cef1c5d..b8aa6d037c 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 c5038aa5bd..e3af8f86c1 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,7 +64,7 @@ _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, util_rs, migration_rs, bql_rs, qom_rs,
+  dependencies: [anyhow_rs, common_rs, chardev_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs,
                  qom, hwcore, chardev, migration],
 )
 
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 525f136ae2..526bcf8e31 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 f9551c13a5..712116b585 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 a94af90bb1..98d8593ca5 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::vmstate::VMStateDescription;
 use qom::{
@@ -20,7 +21,6 @@
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
-    chardev::Chardev,
     irq::InterruptSource,
 };
 
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 12/18] rust: split "system" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (10 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 11/18] rust: split "chardev" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 13/18] rust: split "hwcore" crate marcandre.lureau
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@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                    | 43 +++++++++++++++++++
 rust/system/meson.build                 | 57 +++++++++++++++++++++++++
 rust/system/src/bindings.rs             | 41 ++++++++++++++++++
 rust/system/src/lib.rs                  |  4 ++
 rust/{qemu-api => system}/src/memory.rs |  2 +-
 24 files changed, 232 insertions(+), 33 deletions(-)
 create mode 100644 rust/system/wrapper.h
 create mode 100644 rust/system/Cargo.toml
 create mode 100644 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 4ab89b208c..ce8bb3c076 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3518,6 +3518,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 07dbc9987a..564733b903 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 0000000000..48abde8505
--- /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 ac325969cc..e4946549c0 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -92,6 +92,7 @@ dependencies = [
  "qemu_api",
  "qemu_api_macros",
  "qom",
+ "system",
  "util",
 ]
 
@@ -133,6 +134,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 fd7cf9b0e1..1c18e69660 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 43109130c5..23b21f0214 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 f7ad5f8e08..e4b1c3f1eb 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 552ccc0d60..06ebaf7ab9 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 b1c65bd79c..5307ac480b 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -18,7 +18,6 @@
 use qemu_api::{
     bindings::{qdev_prop_bool, qdev_prop_chr},
     irq::{IRQState, InterruptSource},
-    memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
     sysbus::{SysBusDevice, SysBusDeviceImpl},
@@ -28,6 +27,7 @@
     qom_isa, IsA, Object, ObjectClassMethods, ObjectDeref, ObjectImpl, ObjectMethods, ObjectType,
     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 19456ec72b..a95b1271c6 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 195dc48e1c..74cbe27df8 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -11,6 +11,7 @@ _libhpet_rs = static_library(
     qemu_api,
     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 b876b35a43..f5cc4ee0ad 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -17,19 +17,17 @@
     VMStateDescription, VMStateFieldHelper,
 };
 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::{qom_isa, Object, ObjectImpl, ObjectType, 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 4d9e291223..d8b71f5506 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 b8aa6d037c..2429c4eb9f 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -21,6 +21,7 @@ util = { path = "../util" }
 bql = { path = "../bql" }
 qemu_api_macros = { path = "../qemu-api-macros" }
 qom = { path = "../qom" }
+system = { path = "../system" }
 anyhow = "~1.0"
 libc = "0.2.162"
 foreign = "~0.3.1"
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index e3af8f86c1..644ca44573 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',
@@ -64,7 +65,7 @@ _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, chardev_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs,
+  dependencies: [anyhow_rs, common_rs, chardev_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs,
                  qom, hwcore, chardev, migration],
 )
 
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 526bcf8e31..63b805c76e 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 712116b585..38ff102b86 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -14,6 +14,5 @@
 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 016e57935a..66b618c705 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::{IsA, Object, ObjectCast, ObjectDeref, ObjectType, Owned};
+use system::MemoryRegion;
 
 use crate::{
     bindings,
     irq::{IRQState, InterruptSource},
-    memory::MemoryRegion,
     qdev::{DeviceClassExt, DeviceImpl, DeviceState},
 };
 
diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
new file mode 100644
index 0000000000..6803895e08
--- /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 100644
index 0000000000..34ba641373
--- /dev/null
+++ b/rust/system/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/system/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"))
+    };
+
+    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(())
+}
diff --git a/rust/system/meson.build b/rust/system/meson.build
new file mode 100644
index 0000000000..cbd3eb4717
--- /dev/null
+++ b/rust/system/meson.build
@@ -0,0 +1,57 @@
+_system_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+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',
+  rust_args: _system_cfg,
+  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_api_macros, qom_rs, util_rs,
+                hwcore],
+)
+
+system_rs = declare_dependency(link_with: [_system_rs],
+  dependencies: [qemu_api_macros, hwcore])
+
+# 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-system-rs-doctests',
+     _system_rs,
+     protocol: 'rust',
+     dependencies: system_rs,
+     suite: ['doc', 'rust'])
+
diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs
new file mode 100644
index 0000000000..43edd98807
--- /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 0000000000..dfb68d3ea0
--- /dev/null
+++ b/rust/system/src/lib.rs
@@ -0,0 +1,4 @@
+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 480cc6b0c5..af69fb30bf 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::{IsA, Object, ObjectType};
 
 use crate::bindings::{self, device_endian, memory_region_init_io};
+pub use crate::bindings::{hwaddr, MemTxAttrs};
 
 pub struct MemoryRegionOps<T>(
     bindings::MemoryRegionOps,
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 13/18] rust: split "hwcore" crate
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (11 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 12/18] rust: split "system" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 14/18] rust: rename qemu_api_macros -> qemu_macros marcandre.lureau
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@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/hw/char/pl011/Cargo.toml             |  1 +
 rust/hw/char/pl011/meson.build            |  1 +
 rust/hw/char/pl011/src/device.rs          | 20 +++--
 rust/hw/core/Cargo.toml                   | 26 +++++++
 rust/hw/core/build.rs                     | 43 +++++++++++
 rust/hw/core/meson.build                  | 94 +++++++++++++++++++++++
 rust/hw/core/src/bindings.rs              | 41 ++++++++++
 rust/{qemu-api => hw/core}/src/irq.rs     |  0
 rust/hw/core/src/lib.rs                   | 12 +++
 rust/{qemu-api => hw/core}/src/qdev.rs    |  6 +-
 rust/{qemu-api => hw/core}/src/sysbus.rs  |  0
 rust/{qemu-api => hw/core}/tests/tests.rs | 20 ++---
 rust/hw/timer/hpet/Cargo.toml             |  1 +
 rust/hw/timer/hpet/meson.build            |  1 +
 rust/hw/timer/hpet/src/device.rs          | 22 +++---
 rust/meson.build                          |  1 +
 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 -
 26 files changed, 305 insertions(+), 77 deletions(-)
 create mode 100644 rust/hw/core/wrapper.h
 create mode 100644 rust/hw/core/Cargo.toml
 create mode 100644 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 (98%)
 rename rust/{qemu-api => hw/core}/src/sysbus.rs (100%)
 rename rust/{qemu-api => hw/core}/tests/tests.rs (89%)
diff --git a/MAINTAINERS b/MAINTAINERS
index ce8bb3c076..3f40a80240 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3513,6 +3513,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 0000000000..3bdbd1249e
--- /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 564733b903..7c9c20b14f 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 e4946549c0..7ed22ca687 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -88,6 +88,7 @@ version = "0.1.0"
 dependencies = [
  "bql",
  "common",
+ "hwcore",
  "migration",
  "qemu_api",
  "qemu_api_macros",
@@ -96,6 +97,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"
@@ -130,6 +145,7 @@ dependencies = [
  "bql",
  "chardev",
  "common",
+ "hwcore",
  "migration",
  "qemu_api",
  "qemu_api_macros",
@@ -179,6 +195,7 @@ dependencies = [
  "chardev",
  "common",
  "foreign",
+ "hwcore",
  "libc",
  "migration",
  "qemu_api_macros",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 1c18e69660..ca94d2cf40 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/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index e4b1c3f1eb..830d88586b 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 06ebaf7ab9..83030476de 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 5307ac480b..e0d89db28d 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -11,18 +11,16 @@
 use bql::BqlRefCell;
 use chardev::{CharBackend, Chardev, Event};
 use common::{static_assert, uninit_field_mut, Zeroable};
+use hwcore::{
+    bindings::{qdev_prop_bool, qdev_prop_chr},
+    declare_properties, define_property, vmstate_clock, Clock, ClockEvent, DeviceImpl,
+    DeviceMethods, DeviceState, IRQState, InterruptSource, Property, ResetType,
+    ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
+};
 use migration::{
     impl_vmstate_forward, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections,
     vmstate_unused, VMStateDescription,
 };
-use qemu_api::{
-    bindings::{qdev_prop_bool, qdev_prop_chr},
-    irq::{IRQState, InterruptSource},
-    prelude::*,
-    qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
-    sysbus::{SysBusDevice, SysBusDeviceImpl},
-    vmstate_clock,
-};
 use qom::{
     qom_isa, IsA, Object, ObjectClassMethods, ObjectDeref, ObjectImpl, ObjectMethods, ObjectType,
     Owned, ParentField, ParentInit,
@@ -786,16 +784,16 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
     ..Zeroable::ZERO
 };
 
-qemu_api::declare_properties! {
+declare_properties! {
     PL011_PROPERTIES,
-    qemu_api::define_property!(
+    define_property!(
         c"chardev",
         PL011State,
         char_backend,
         unsafe { &qdev_prop_chr },
         CharBackend
     ),
-    qemu_api::define_property!(
+    define_property!(
         c"migrate-clk",
         PL011State,
         migrate_clock,
diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml
new file mode 100644
index 0000000000..0b35380264
--- /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 100644
index 0000000000..3f8e1bff89
--- /dev/null
+++ b/rust/hw/core/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/hw/core/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"))
+    };
+
+    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(())
+}
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
new file mode 100644
index 0000000000..d390f2d665
--- /dev/null
+++ b/rust/hw/core/meson.build
@@ -0,0 +1,94 @@
+_hwcore_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+_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',
+  rust_args: _hwcore_cfg,
+  dependencies: [qemu_api_macros, common_rs, bql_rs, chardev_rs, migration_rs, qemuutil_rs, qom_rs, system_rs, util_rs,
+                 qom, hwcore, chardev, migration],
+)
+
+hwcore_rs = declare_dependency(link_with: [_hwcore_rs],
+  dependencies: [qom_rs, hwcore])
+
+# 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-hwcore-rs-doctests',
+     _hwcore_rs,
+     protocol: 'rust',
+     dependencies: hwcore_rs,
+     suite: ['doc', 'rust'])
+
+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 0000000000..919c02b56a
--- /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 0000000000..c5c33f3dec
--- /dev/null
+++ b/rust/hw/core/src/lib.rs
@@ -0,0 +1,12 @@
+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 98%
rename from rust/qemu-api/src/qdev.rs
rename to rust/hw/core/src/qdev.rs
index 98d8593ca5..16a4769758 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -9,7 +9,6 @@
     ptr::NonNull,
 };
 
-pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use chardev::Chardev;
 use common::{callbacks::FnCall, Opaque};
 use migration::vmstate::VMStateDescription;
@@ -19,6 +18,7 @@
 };
 pub 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,
@@ -428,11 +428,11 @@ macro_rules! vmstate_clock {
                 ::common::assert_field_type!(
                     $struct_name,
                     $field_name,
-                    ::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
+                    ::qom::Owned<$crate::Clock> $(, num = $num)?
                 );
                 ::std::mem::offset_of!($struct_name, $field_name)
             },
-            size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
+            size: ::core::mem::size_of::<*const $crate::Clock>(),
             flags: ::migration::VMStateFlags(
                 ::migration::VMStateFlags::VMS_STRUCT.0
                     | ::migration::VMStateFlags::VMS_POINTER.0,
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 89%
rename from rust/qemu-api/tests/tests.rs
rename to rust/hw/core/tests/tests.rs
index 821aabe831..f11c15ac62 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -6,21 +6,17 @@
 
 use bql::BqlCell;
 use common::Zeroable;
-use migration::VMStateDescription;
-use qemu_api::{
-    bindings::qdev_prop_bool,
-    declare_properties, define_property,
-    qdev::{DeviceClassExt, DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
-    sysbus::SysBusDevice,
+use hwcore::{
+    bindings::qdev_prop_bool, declare_properties, define_property, DeviceClassExt, DeviceImpl,
+    DeviceState, Property, ResettablePhasesImpl, SysBusDevice,
 };
+use migration::vmstate::VMStateDescription;
 use qom::{
-    Object, ObjectCast, ObjectClassMethods, ObjectDeref, ObjectImpl, ObjectMethods, ObjectType,
-    ParentField,
+    qom_isa, Object, ObjectCast, ObjectClassMethods, ObjectDeref, ObjectImpl, ObjectMethods,
+    ObjectType, ParentField,
 };
 use util::bindings::{module_call_init, module_init_type};
 
-mod vmstate_tests;
-
 // Test that macros can compile.
 pub static VMSTATE: VMStateDescription = VMStateDescription {
     name: c"name".as_ptr(),
@@ -35,7 +31,7 @@ pub struct DummyState {
     migrate_clock: bool,
 }
 
-qom::qom_isa!(DummyState: Object, DeviceState);
+qom_isa!(DummyState: Object, DeviceState);
 
 pub struct DummyClass {
     parent_class: <DeviceState as ObjectType>::Class,
@@ -86,7 +82,7 @@ pub struct DummyChildState {
     parent: ParentField<DummyState>,
 }
 
-qom::qom_isa!(DummyChildState: Object, DeviceState, DummyState);
+qom_isa!(DummyChildState: Object, DeviceState, DummyState);
 
 pub struct DummyChildClass {
     parent_class: <DummyState as ObjectType>::Class,
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index a95b1271c6..e28d66f645 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 74cbe27df8..d65306c169 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 f5cc4ee0ad..2ea101bb7f 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, Zeroable};
+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::{
     vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
     VMStateDescription, VMStateFieldHelper,
 };
-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::{qom_isa, Object, ObjectImpl, ObjectType, ParentField, ParentInit};
 use system::{
     bindings::{address_space_memory, address_space_stl_le, hwaddr},
@@ -900,9 +898,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,
@@ -910,7 +908,7 @@ impl ObjectImpl for HPETState {
         u8,
         default = HPET_MIN_TIMERS
     ),
-    qemu_api::define_property!(
+    define_property!(
         c"msi",
         HPETState,
         flags,
@@ -919,7 +917,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,
@@ -927,7 +925,7 @@ impl ObjectImpl for HPETState {
         u32,
         default = 0
     ),
-    qemu_api::define_property!(
+    define_property!(
         c"hpet-offset-saved",
         HPETState,
         hpet_offset_saved,
diff --git a/rust/meson.build b/rust/meson.build
index d8b71f5506..041b0a473e 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/Cargo.toml b/rust/qemu-api/Cargo.toml
index 2429c4eb9f..9f216e45a6 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 644ca44573..a4c90b2bf7 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, common_rs, chardev_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs,
+  dependencies: [anyhow_rs, common_rs, chardev_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs, hwcore_rs,
                  qom, hwcore, chardev, migration],
 )
 
@@ -87,7 +74,7 @@ rust.doctest('rust-qemu-api-doctests',
 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 63b805c76e..9c863e9b5b 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 38ff102b86..50fb2fa99d 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -12,7 +12,3 @@
 // for prelude-like modules
 #[rustfmt::skip]
 pub mod prelude;
-
-pub mod irq;
-pub mod qdev;
-pub mod sysbus;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 9e9d1c8247..8db56f9f81 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.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 14/18] rust: rename qemu_api_macros -> qemu_macros
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (12 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 13/18] rust: split "hwcore" crate marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:53   ` Paolo Bonzini
  2025-08-26 14:04 ` [RFC 15/18] rust/hpet: drop now unneeded qemu_api dep marcandre.lureau
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
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>
---
 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                      |  4 ++--
 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/qom/tests/tests.rs                       |  2 +-
 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 ++--
 42 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 3f40a80240..62790c7b5d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3516,7 +3516,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 7ed22ca687..9bb2ab5bf6 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",
 ]
@@ -91,7 +91,7 @@ dependencies = [
  "hwcore",
  "migration",
  "qemu_api",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -105,7 +105,7 @@ dependencies = [
  "chardev",
  "common",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -131,7 +131,7 @@ name = "migration"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_api_macros",
+ "qemu_macros",
  "util",
 ]
 
@@ -148,7 +148,7 @@ dependencies = [
  "hwcore",
  "migration",
  "qemu_api",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -198,14 +198,14 @@ dependencies = [
  "hwcore",
  "libc",
  "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 ca94d2cf40..1acee9e7cb 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 1ff38a4117..7fce972b27 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 2a41e138c5..359ca86f15 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 d485d6bd11..4e091aebbd 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 7df9c677fc..c139177307 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 918e0a5224..487f36e27d 100644
--- a/rust/chardev/meson.build
+++ b/rust/chardev/meson.build
@@ -39,10 +39,10 @@ _chardev_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _chardev_cfg,
-  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_api_macros, qom_rs, util_rs, chardev],
+  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_macros, qom_rs, util_rs, chardev],
 )
 
-chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev])
+chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev])
 
 # 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/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs
index 1a6795a938..fe90126b84 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 e076964634..06f6b8cd29 100644
--- a/rust/common/src/opaque.rs
+++ b/rust/common/src/opaque.rs
@@ -194,7 +194,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
 ///
@@ -230,7 +230,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 830d88586b..9e451fc0aa 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 83030476de..7d90f2aad6 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -12,7 +12,7 @@ _libpl011_rs = static_library(
     migration_rs,
     bql_rs,
     qemu_api,
-    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 e0d89db28d..caaa05c04c 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -106,7 +106,7 @@ pub struct PL011Registers {
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_macros::Object)]
 /// PL011 Device Model in QEMU
 pub struct PL011State {
     pub parent_obj: ParentField<SysBusDevice>,
@@ -691,7 +691,7 @@ pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_macros::Object)]
 /// 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 2bfbd81095..a1c41347ed 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 0b35380264..0eb9ffee26 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 d390f2d665..692c4b8e0f 100644
--- a/rust/hw/core/meson.build
+++ b/rust/hw/core/meson.build
@@ -62,7 +62,7 @@ _hwcore_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _hwcore_cfg,
-  dependencies: [qemu_api_macros, common_rs, bql_rs, chardev_rs, migration_rs, qemuutil_rs, qom_rs, system_rs, util_rs,
+  dependencies: [qemu_macros, common_rs, bql_rs, chardev_rs, migration_rs, qemuutil_rs, qom_rs, system_rs, util_rs,
                  qom, hwcore, chardev, migration],
 )
 
@@ -85,7 +85,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 5a380923fe..828e6cb28f 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 16a4769758..62f518a11a 100644
--- a/rust/hw/core/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -26,7 +26,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 {}
@@ -34,7 +34,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 {}
diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
index 66b618c705..51631cd237 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 f11c15ac62..2898844baf 100644
--- a/rust/hw/core/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -25,7 +25,7 @@
 };
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_macros::Object)]
 pub struct DummyState {
     parent: ParentField<DeviceState>,
     migrate_clock: bool,
@@ -77,7 +77,7 @@ fn vmsd() -> Option<&'static VMStateDescription> {
 }
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_macros::Object)]
 pub struct DummyChildState {
     parent: ParentField<DummyState>,
 }
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index e28d66f645..68e8187bb8 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 d65306c169..b2eee01faf 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -9,7 +9,7 @@ _libhpet_rs = static_library(
     migration_rs,
     bql_rs,
     qemu_api,
-    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 2ea101bb7f..b883d56a9a 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
@@ -516,7 +516,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 041b0a473e..9f6a0b161d 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 98e6df2109..66af81e0a3 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 9f216e45a6..b6744f0286 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -20,7 +20,7 @@ hwcore = { path = "../hw/core" }
 migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
-qemu_api_macros = { path = "../qemu-api-macros" }
+qemu_macros = { path = "../qemu-macros" }
 qom = { path = "../qom" }
 system = { path = "../system" }
 anyhow = "~1.0"
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index a4c90b2bf7..02318950ff 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -52,7 +52,7 @@ _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, chardev_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs, hwcore_rs,
+  dependencies: [anyhow_rs, common_rs, chardev_rs, foreign_rs, libc_rs, qemu_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs, hwcore_rs,
                  qom, hwcore, chardev, migration],
 )
 
@@ -60,7 +60,7 @@ rust.test('rust-qemu-api-tests', _qemu_api_rs,
           suite: ['unit', 'rust'])
 
 qemu_api = declare_dependency(link_with: [_qemu_api_rs],
-  dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
+  dependencies: [qemu_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
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 0cd40c8e16..3b6f1d337f 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 2152bcb99b..d0b2992e20 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 46bbf7c7fe..060ad2ec34 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 6e95d75fa0..40a2bf9ae0 100644
--- a/rust/qom/meson.build
+++ b/rust/qom/meson.build
@@ -31,10 +31,10 @@ _qom_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qom_cfg,
-  dependencies: [qemuutil_rs, bql_rs, common_rs, migration_rs, qemu_api_macros, qom],
+  dependencies: [qemuutil_rs, bql_rs, common_rs, migration_rs, qemu_macros, qom],
 )
 
-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 a632ec43f2..880bef6c47 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/qom/tests/tests.rs b/rust/qom/tests/tests.rs
index 49f1cbecf5..4d710f96a6 100644
--- a/rust/qom/tests/tests.rs
+++ b/rust/qom/tests/tests.rs
@@ -4,7 +4,7 @@
 use util::bindings::{module_call_init, module_init_type};
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_macros::Object)]
 pub struct DummyObject {
     parent: ParentField<Object>,
 }
diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
index 6803895e08..d8338c8348 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 cbd3eb4717..710462376d 100644
--- a/rust/system/meson.build
+++ b/rust/system/meson.build
@@ -39,12 +39,12 @@ _system_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _system_cfg,
-  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_api_macros, qom_rs, util_rs,
+  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_macros, qom_rs, util_rs,
                 hwcore],
 )
 
 system_rs = declare_dependency(link_with: [_system_rs],
-  dependencies: [qemu_api_macros, hwcore])
+  dependencies: [qemu_macros, hwcore])
 
 # 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/system/src/memory.rs b/rust/system/src/memory.rs
index af69fb30bf..898401c33b 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 aa10f03384..c5d1adb8fe 100644
--- a/rust/util/Cargo.toml
+++ b/rust/util/Cargo.toml
@@ -17,7 +17,7 @@ anyhow = "~1.0"
 libc = "0.2.162"
 foreign = "~0.3.1"
 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 3fe8e3975f..1e449b8dc7 100644
--- a/rust/util/meson.build
+++ b/rust/util/meson.build
@@ -45,7 +45,7 @@ _util_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _util_cfg,
-  dependencies: [anyhow_rs, libc_rs, foreign_rs, qemuutil_rs, common_rs, qemu_api_macros],
+  dependencies: [anyhow_rs, libc_rs, foreign_rs, qemuutil_rs, common_rs, qemu_macros],
 )
 
 util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil_rs, qom])
diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs
index 441072a949..42096cef0d 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.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 15/18] rust/hpet: drop now unneeded qemu_api dep
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (13 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 14/18] rust: rename qemu_api_macros -> qemu_macros marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 16/18] rust/pl011: drop dependency on qemu_api marcandre.lureau
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 rust/hw/timer/hpet/Cargo.toml  | 1 -
 rust/hw/timer/hpet/meson.build | 1 -
 2 files changed, 2 deletions(-)
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 68e8187bb8..08bf97af55 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 b2eee01faf..8ab26630d9 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_api,
     qemu_macros,
     qom_rs,
     system_rs,
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 16/18] rust/pl011: drop dependency on qemu_api
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (14 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 15/18] rust/hpet: drop now unneeded qemu_api dep marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 17/18] rust: repurpose qemu_api -> tests marcandre.lureau
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 rust/hw/char/pl011/wrapper.h       | 51 ++++++++++++++++++++++++++++++
 rust/Cargo.lock                    |  2 --
 rust/hw/char/pl011/Cargo.toml      |  1 -
 rust/hw/char/pl011/build.rs        | 43 +++++++++++++++++++++++++
 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, 148 insertions(+), 6 deletions(-)
 create mode 100644 rust/hw/char/pl011/wrapper.h
 create mode 100644 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 0000000000..87a5a589c8
--- /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 9bb2ab5bf6..1a9a36ecec 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -90,7 +90,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_api",
  "qemu_macros",
  "qom",
  "system",
@@ -147,7 +146,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 9e451fc0aa..285d25c217 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 100644
index 0000000000..721bf2c457
--- /dev/null
+++ b/rust/hw/char/pl011/build.rs
@@ -0,0 +1,43 @@
+// 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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+        format!("{root}/rust/hw/char/pl011/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"))
+    };
+
+    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(())
+}
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 7d90f2aad6..a14993f692 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: [
@@ -11,7 +35,6 @@ _libpl011_rs = static_library(
     util_rs,
     migration_rs,
     bql_rs,
-    qemu_api,
     qemu_macros,
     qom_rs,
     chardev_rs,
diff --git a/rust/hw/char/pl011/src/bindings.rs b/rust/hw/char/pl011/src/bindings.rs
new file mode 100644
index 0000000000..bd5ea840cb
--- /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 caaa05c04c..c9f6087540 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -137,7 +137,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 2b70d2ff56..0c19b708c0 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.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 17/18] rust: repurpose qemu_api -> tests
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (15 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 16/18] rust/pl011: drop dependency on qemu_api marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:04 ` [RFC 18/18] docs: update rust.rst marcandre.lureau
  2025-08-26 14:44 ` [RFC 00/18] rust: split qemu-api Paolo Bonzini
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
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>
---
 MAINTAINERS                                   |  2 +-
 rust/qemu-api/wrapper.h                       | 53 -----------
 rust/Cargo.lock                               | 36 ++++----
 rust/Cargo.toml                               |  2 +-
 rust/meson.build                              |  2 +-
 rust/qemu-api/.gitignore                      |  2 -
 rust/qemu-api/README.md                       | 19 ----
 rust/qemu-api/build.rs                        | 43 ---------
 rust/qemu-api/meson.build                     | 87 -------------------
 rust/qemu-api/src/bindings.rs                 | 33 -------
 rust/qemu-api/src/lib.rs                      | 14 ---
 rust/qemu-api/src/prelude.rs                  |  5 --
 rust/{qemu-api => tests}/Cargo.toml           | 11 +--
 rust/tests/meson.build                        | 14 +++
 .../tests/vmstate_tests.rs                    |  0
 15 files changed, 38 insertions(+), 285 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 100644 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 (74%)
 create mode 100644 rust/tests/meson.build
 rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (100%)
diff --git a/MAINTAINERS b/MAINTAINERS
index 62790c7b5d..d978e41e08 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3515,11 +3515,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 7c9c20b14f..0000000000
--- 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 1a9a36ecec..4c15579d1a 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -184,24 +184,6 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "qemu_api"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "bql",
- "chardev",
- "common",
- "foreign",
- "hwcore",
- "libc",
- "migration",
- "qemu_macros",
- "qom",
- "system",
- "util",
-]
-
 [[package]]
 name = "qemu_macros"
 version = "0.1.0"
@@ -252,6 +234,24 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "tests"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bql",
+ "chardev",
+ "common",
+ "foreign",
+ "hwcore",
+ "libc",
+ "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 1acee9e7cb..c253c0e2df 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 9f6a0b161d..32d569f871 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -32,7 +32,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 df6c2163e0..0000000000
--- 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 ed1b7ab263..0000000000
--- 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 100644
index 29d0945625..0000000000
--- a/rust/qemu-api/build.rs
+++ /dev/null
@@ -1,43 +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 file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
-        format!("{root}/rust/qemu-api/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"))
-    };
-
-    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(())
-}
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
deleted file mode 100644
index 02318950ff..0000000000
--- a/rust/qemu-api/meson.build
+++ /dev/null
@@ -1,87 +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, common_rs, chardev_rs, foreign_rs, libc_rs, qemu_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs, hwcore_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],
-  dependencies: [qemu_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,
-     suite: ['doc', 'rust'])
-
-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]),
-    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 9c863e9b5b..0000000000
--- 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 50fb2fa99d..0000000000
--- a/rust/qemu-api/src/lib.rs
+++ /dev/null
@@ -1,14 +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;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
deleted file mode 100644
index 8db56f9f81..0000000000
--- 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 74%
rename from rust/qemu-api/Cargo.toml
rename to rust/tests/Cargo.toml
index b6744f0286..104e7868cb 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
@@ -27,9 +26,5 @@ anyhow = "~1.0"
 libc = "0.2.162"
 foreign = "~0.3.1"
 
-[features]
-default = ["debug_cell"]
-debug_cell = []
-
 [lints]
 workspace = true
diff --git a/rust/tests/meson.build b/rust/tests/meson.build
new file mode 100644
index 0000000000..00688c66fb
--- /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.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* [RFC 18/18] docs: update rust.rst
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (16 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 17/18] rust: repurpose qemu_api -> tests marcandre.lureau
@ 2025-08-26 14:04 ` marcandre.lureau
  2025-08-26 14:44 ` [RFC 00/18] rust: split qemu-api Paolo Bonzini
  18 siblings, 0 replies; 32+ messages in thread
From: marcandre.lureau @ 2025-08-26 14:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Daniel P. Berrangé, Marc-André Lureau,
	qemu-rust, Philippe Mathieu-Daudé, Manos Pitsidianakis
From: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 docs/devel/rust.rst | 51 ++++++++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 24 deletions(-)
diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index b6737536c6..e7967ff7be 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -115,11 +115,14 @@ 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``)
@@ -136,7 +139,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
@@ -152,26 +155,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``      proof of concept
-``zeroable``     stable
-================ ======================
+========================== ======================
+module                     status
+========================== ======================
+``bql::cell``              stable
+``common::assertions``     stable
+``common::bitops``         complete
+``common::callbacks``      complete
+``common::zeroable``       stable
+``hwcore::irq``            complete
+``hwcore::qdev``           stable
+``hwcore::sysbus``         stable
+``migration::vmstate``     proof of concept
+``qom``                    stable
+``system::memory``         stable
+``util::errno``            complete
+``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
-- 
2.50.1
^ permalink raw reply related	[flat|nested] 32+ messages in thread
* Re: [RFC 00/18] rust: split qemu-api
  2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
                   ` (17 preceding siblings ...)
  2025-08-26 14:04 ` [RFC 18/18] docs: update rust.rst marcandre.lureau
@ 2025-08-26 14:44 ` Paolo Bonzini
  2025-08-26 14:55   ` Manos Pitsidianakis
  2025-08-26 15:15   ` Marc-André Lureau
  18 siblings, 2 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-08-26 14:44 UTC (permalink / raw)
  To: marcandre.lureau, qemu-devel
  Cc: Daniel P. Berrangé, qemu-rust, Philippe Mathieu-Daudé,
	Manos Pitsidianakis
On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Hi,
> 
> qemu-api is providing bindings for various internal libraries. Instead, the
> bindings requirements should match the various libraries and use the minimal set
> of dependencies.
> 
> An initial Rust-only "common" crate is introduced, then "util" (for libqemuutil,
> without bql), "migration" (so it doesn't depend on bql), "bql", "qom" (arguably,
> bql shouldn't be required?), and "chardev", "system", "hwcore". Finally the
> qemu-api crates are renamed and repurposed.
> 
> This involves a lot of code churn, so hopefully it can be reviewed and merged
> early and iterated upon :)
The one comment that I would like to handle before merging, is that I'd 
prefer to keep the preludes and, in fact, even add more exports to them 
since they can now be chosen per-crate.  Ideally, many of the crates 
you've crated would be accessed through many "use xyz::prelude::*" 
statements.
Also, if I understood correctly the split util/errno.rs can move to 
common/.  While it has a dependency on libc, it doesn't need bindgen.
There's a bunch of code duplication for the various bindings.rs and 
build.rs files, which is not ideal but shouldn't grow much more than 
this.  I wonder if, later, common code across build.rs could be written 
just once by adding a new crate (e.g. "qemu_meson") to the workspace, 
that can be used as a build-dependency.
Paolo
> Marc-André Lureau (18):
>    rust: remove unused global qemu "allocator"
>    rust: add workspace authors
>    rust: split Rust-only "common" crate
>    rust: split "util" crate
>    rust: move vmstate_clock!() to qdev module
>    rust: move VMState handling to QOM module
>    rust: move Cell vmstate impl
>    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
>    docs: update rust.rst
> 
>   MAINTAINERS                                   |  12 +-
>   docs/devel/rust.rst                           |  51 +--
>   meson.build                                   |   4 -
>   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 ++
>   rust/Cargo.lock                               | 127 ++++++-
>   rust/Cargo.toml                               |  16 +-
>   rust/bits/Cargo.toml                          |   2 +-
>   rust/bits/meson.build                         |   2 +-
>   rust/bits/src/lib.rs                          |   4 +-
>   rust/{qemu-api => bql}/Cargo.toml             |  13 +-
>   rust/{qemu-api => bql}/build.rs               |   2 +-
>   rust/bql/meson.build                          |  52 +++
>   rust/bql/src/bindings.rs                      |  25 ++
>   rust/{qemu-api => bql}/src/cell.rs            | 333 +++---------------
>   rust/bql/src/lib.rs                           |  29 ++
>   rust/chardev/Cargo.toml                       |  24 ++
>   rust/chardev/build.rs                         |  43 +++
>   rust/chardev/meson.build                      |  54 +++
>   rust/chardev/src/bindings.rs                  |  36 ++
>   rust/{qemu-api => chardev}/src/chardev.rs     |  35 +-
>   rust/chardev/src/lib.rs                       |   4 +
>   rust/common/Cargo.toml                        |  16 +
>   rust/common/meson.build                       |  32 ++
>   rust/{qemu-api => common}/src/assertions.rs   |  16 +-
>   rust/{qemu-api => common}/src/bitops.rs       |   1 -
>   rust/{qemu-api => common}/src/callbacks.rs    |  12 +-
>   rust/common/src/lib.rs                        |  17 +
>   rust/common/src/opaque.rs                     | 240 +++++++++++++
>   rust/{qemu-api => common}/src/uninit.rs       |   2 +-
>   rust/common/src/zeroable.rs                   |  18 +
>   rust/hw/char/pl011/Cargo.toml                 |  11 +-
>   rust/hw/char/pl011/build.rs                   |  43 +++
>   rust/hw/char/pl011/meson.build                |  39 +-
>   rust/hw/char/pl011/src/bindings.rs            |  27 ++
>   rust/hw/char/pl011/src/device.rs              |  49 +--
>   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                         |  43 +++
>   rust/{qemu-api => hw/core}/meson.build        |  86 ++---
>   rust/hw/core/src/bindings.rs                  |  41 +++
>   rust/{qemu-api => hw/core}/src/irq.rs         |  18 +-
>   rust/hw/core/src/lib.rs                       |  12 +
>   rust/{qemu-api => hw/core}/src/qdev.rs        |  81 +++--
>   rust/{qemu-api => hw/core}/src/sysbus.rs      |  28 +-
>   rust/{qemu-api => hw/core}/tests/tests.rs     |  29 +-
>   rust/hw/timer/hpet/Cargo.toml                 |  10 +-
>   rust/hw/timer/hpet/meson.build                |  12 +-
>   rust/hw/timer/hpet/src/device.rs              |  56 ++-
>   rust/hw/timer/hpet/src/fw_cfg.rs              |   6 +-
>   rust/meson.build                              |  12 +-
>   rust/migration/Cargo.toml                     |  21 ++
>   rust/migration/build.rs                       |  43 +++
>   rust/migration/meson.build                    |  57 +++
>   rust/migration/src/bindings.rs                |  48 +++
>   rust/migration/src/lib.rs                     |   4 +
>   rust/{qemu-api => migration}/src/vmstate.rs   | 166 ++++-----
>   rust/qemu-api/.gitignore                      |   2 -
>   rust/qemu-api/README.md                       |  19 -
>   rust/qemu-api/src/lib.rs                      | 170 ---------
>   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                                |  20 +-
>   .../src/tests.rs                              |   8 +-
>   rust/qom/Cargo.toml                           |  23 ++
>   rust/qom/build.rs                             |  43 +++
>   rust/qom/meson.build                          |  61 ++++
>   rust/qom/src/bindings.rs                      |  25 ++
>   rust/qom/src/lib.rs                           |   4 +
>   rust/{qemu-api => qom}/src/qom.rs             |  27 +-
>   rust/qom/tests/tests.rs                       |  47 +++
>   rust/system/Cargo.toml                        |  22 ++
>   rust/system/build.rs                          |  43 +++
>   rust/system/meson.build                       |  57 +++
>   rust/{qemu-api => system}/src/bindings.rs     |  33 +-
>   rust/system/src/lib.rs                        |   4 +
>   rust/{qemu-api => system}/src/memory.rs       |  20 +-
>   rust/tests/Cargo.toml                         |  30 ++
>   rust/tests/meson.build                        |  14 +
>   .../tests/vmstate_tests.rs                    |  18 +-
>   rust/util/Cargo.toml                          |  23 ++
>   rust/util/build.rs                            |  43 +++
>   rust/util/meson.build                         |  61 ++++
>   rust/util/src/bindings.rs                     |  25 ++
>   rust/{qemu-api => util}/src/errno.rs          |  11 +-
>   rust/{qemu-api => util}/src/error.rs          |   6 +-
>   rust/util/src/lib.rs                          |  10 +
>   rust/{qemu-api => util}/src/log.rs            |  12 +-
>   rust/{qemu-api => util}/src/module.rs         |   2 +-
>   rust/{qemu-api => util}/src/timer.rs          |  12 +-
>   100 files changed, 2372 insertions(+), 1044 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
>   rename rust/{qemu-api => bql}/Cargo.toml (52%)
>   rename rust/{qemu-api => bql}/build.rs (96%)
>   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 100644 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 (91%)
>   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 (92%)
>   rename rust/{qemu-api => common}/src/bitops.rs (98%)
>   rename rust/{qemu-api => common}/src/callbacks.rs (97%)
>   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
>   create mode 100644 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 100644 rust/hw/core/build.rs
>   rename rust/{qemu-api => hw/core}/meson.build (52%)
>   create mode 100644 rust/hw/core/src/bindings.rs
>   rename rust/{qemu-api => hw/core}/src/irq.rs (92%)
>   create mode 100644 rust/hw/core/src/lib.rs
>   rename rust/{qemu-api => hw/core}/src/qdev.rs (86%)
>   rename rust/{qemu-api => hw/core}/src/sysbus.rs (87%)
>   rename rust/{qemu-api => hw/core}/tests/tests.rs (88%)
>   create mode 100644 rust/migration/Cargo.toml
>   create mode 100644 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 (80%)
>   delete mode 100644 rust/qemu-api/.gitignore
>   delete mode 100644 rust/qemu-api/README.md
>   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 (91%)
>   rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (93%)
>   create mode 100644 rust/qom/Cargo.toml
>   create mode 100644 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
>   rename rust/{qemu-api => qom}/src/qom.rs (98%)
>   create mode 100644 rust/qom/tests/tests.rs
>   create mode 100644 rust/system/Cargo.toml
>   create mode 100644 rust/system/build.rs
>   create mode 100644 rust/system/meson.build
>   rename rust/{qemu-api => system}/src/bindings.rs (56%)
>   create mode 100644 rust/system/src/lib.rs
>   rename rust/{qemu-api => system}/src/memory.rs (95%)
>   create mode 100644 rust/tests/Cargo.toml
>   create mode 100644 rust/tests/meson.build
>   rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (96%)
>   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/errno.rs (98%)
>   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 (93%)
> 
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 14/18] rust: rename qemu_api_macros -> qemu_macros
  2025-08-26 14:04 ` [RFC 14/18] rust: rename qemu_api_macros -> qemu_macros marcandre.lureau
@ 2025-08-26 14:53   ` Paolo Bonzini
  2025-08-26 20:30     ` Marc-André Lureau
  0 siblings, 1 reply; 32+ messages in thread
From: Paolo Bonzini @ 2025-08-26 14:53 UTC (permalink / raw)
  To: marcandre.lureau, qemu-devel
  Cc: Daniel P. Berrangé, qemu-rust, Philippe Mathieu-Daudé,
	Manos Pitsidianakis
On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Since "qemu_api" is no longer the unique crate to provide APIs.
In addition to renaming, maybe you can re-export the macros from the 
appropriate crate?
derive(Object) -> qom
derive(Wrapper) -> common
derive(TryInto) -> common
derive(Device) -> hwcore
That is, keep a single qemu_api_macros crate for the implementation but 
allow using the nicer #[derive(qom::Object)].
Paolo
> Signed-off-by: Marc-André Lureau <marcandre.lureau@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                      |  4 ++--
>   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/qom/tests/tests.rs                       |  2 +-
>   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 ++--
>   42 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 3f40a80240..62790c7b5d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3516,7 +3516,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 7ed22ca687..9bb2ab5bf6 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",
>   ]
> @@ -91,7 +91,7 @@ dependencies = [
>    "hwcore",
>    "migration",
>    "qemu_api",
> - "qemu_api_macros",
> + "qemu_macros",
>    "qom",
>    "system",
>    "util",
> @@ -105,7 +105,7 @@ dependencies = [
>    "chardev",
>    "common",
>    "migration",
> - "qemu_api_macros",
> + "qemu_macros",
>    "qom",
>    "system",
>    "util",
> @@ -131,7 +131,7 @@ name = "migration"
>   version = "0.1.0"
>   dependencies = [
>    "common",
> - "qemu_api_macros",
> + "qemu_macros",
>    "util",
>   ]
>   
> @@ -148,7 +148,7 @@ dependencies = [
>    "hwcore",
>    "migration",
>    "qemu_api",
> - "qemu_api_macros",
> + "qemu_macros",
>    "qom",
>    "system",
>    "util",
> @@ -198,14 +198,14 @@ dependencies = [
>    "hwcore",
>    "libc",
>    "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 ca94d2cf40..1acee9e7cb 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 1ff38a4117..7fce972b27 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 2a41e138c5..359ca86f15 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 d485d6bd11..4e091aebbd 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 7df9c677fc..c139177307 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 918e0a5224..487f36e27d 100644
> --- a/rust/chardev/meson.build
> +++ b/rust/chardev/meson.build
> @@ -39,10 +39,10 @@ _chardev_rs = static_library(
>     override_options: ['rust_std=2021', 'build.rust_std=2021'],
>     rust_abi: 'rust',
>     rust_args: _chardev_cfg,
> -  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_api_macros, qom_rs, util_rs, chardev],
> +  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_macros, qom_rs, util_rs, chardev],
>   )
>   
> -chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev])
> +chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev])
>   
>   # 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/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs
> index 1a6795a938..fe90126b84 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 e076964634..06f6b8cd29 100644
> --- a/rust/common/src/opaque.rs
> +++ b/rust/common/src/opaque.rs
> @@ -194,7 +194,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
>   ///
> @@ -230,7 +230,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 830d88586b..9e451fc0aa 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 83030476de..7d90f2aad6 100644
> --- a/rust/hw/char/pl011/meson.build
> +++ b/rust/hw/char/pl011/meson.build
> @@ -12,7 +12,7 @@ _libpl011_rs = static_library(
>       migration_rs,
>       bql_rs,
>       qemu_api,
> -    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 e0d89db28d..caaa05c04c 100644
> --- a/rust/hw/char/pl011/src/device.rs
> +++ b/rust/hw/char/pl011/src/device.rs
> @@ -106,7 +106,7 @@ pub struct PL011Registers {
>   }
>   
>   #[repr(C)]
> -#[derive(qemu_api_macros::Object)]
> +#[derive(qemu_macros::Object)]
>   /// PL011 Device Model in QEMU
>   pub struct PL011State {
>       pub parent_obj: ParentField<SysBusDevice>,
> @@ -691,7 +691,7 @@ pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
>   }
>   
>   #[repr(C)]
> -#[derive(qemu_api_macros::Object)]
> +#[derive(qemu_macros::Object)]
>   /// 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 2bfbd81095..a1c41347ed 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 0b35380264..0eb9ffee26 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 d390f2d665..692c4b8e0f 100644
> --- a/rust/hw/core/meson.build
> +++ b/rust/hw/core/meson.build
> @@ -62,7 +62,7 @@ _hwcore_rs = static_library(
>     override_options: ['rust_std=2021', 'build.rust_std=2021'],
>     rust_abi: 'rust',
>     rust_args: _hwcore_cfg,
> -  dependencies: [qemu_api_macros, common_rs, bql_rs, chardev_rs, migration_rs, qemuutil_rs, qom_rs, system_rs, util_rs,
> +  dependencies: [qemu_macros, common_rs, bql_rs, chardev_rs, migration_rs, qemuutil_rs, qom_rs, system_rs, util_rs,
>                    qom, hwcore, chardev, migration],
>   )
>   
> @@ -85,7 +85,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 5a380923fe..828e6cb28f 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 16a4769758..62f518a11a 100644
> --- a/rust/hw/core/src/qdev.rs
> +++ b/rust/hw/core/src/qdev.rs
> @@ -26,7 +26,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 {}
> @@ -34,7 +34,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 {}
> diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
> index 66b618c705..51631cd237 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 f11c15ac62..2898844baf 100644
> --- a/rust/hw/core/tests/tests.rs
> +++ b/rust/hw/core/tests/tests.rs
> @@ -25,7 +25,7 @@
>   };
>   
>   #[repr(C)]
> -#[derive(qemu_api_macros::Object)]
> +#[derive(qemu_macros::Object)]
>   pub struct DummyState {
>       parent: ParentField<DeviceState>,
>       migrate_clock: bool,
> @@ -77,7 +77,7 @@ fn vmsd() -> Option<&'static VMStateDescription> {
>   }
>   
>   #[repr(C)]
> -#[derive(qemu_api_macros::Object)]
> +#[derive(qemu_macros::Object)]
>   pub struct DummyChildState {
>       parent: ParentField<DummyState>,
>   }
> diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
> index e28d66f645..68e8187bb8 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 d65306c169..b2eee01faf 100644
> --- a/rust/hw/timer/hpet/meson.build
> +++ b/rust/hw/timer/hpet/meson.build
> @@ -9,7 +9,7 @@ _libhpet_rs = static_library(
>       migration_rs,
>       bql_rs,
>       qemu_api,
> -    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 2ea101bb7f..b883d56a9a 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
> @@ -516,7 +516,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 041b0a473e..9f6a0b161d 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 98e6df2109..66af81e0a3 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 9f216e45a6..b6744f0286 100644
> --- a/rust/qemu-api/Cargo.toml
> +++ b/rust/qemu-api/Cargo.toml
> @@ -20,7 +20,7 @@ hwcore = { path = "../hw/core" }
>   migration = { path = "../migration" }
>   util = { path = "../util" }
>   bql = { path = "../bql" }
> -qemu_api_macros = { path = "../qemu-api-macros" }
> +qemu_macros = { path = "../qemu-macros" }
>   qom = { path = "../qom" }
>   system = { path = "../system" }
>   anyhow = "~1.0"
> diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
> index a4c90b2bf7..02318950ff 100644
> --- a/rust/qemu-api/meson.build
> +++ b/rust/qemu-api/meson.build
> @@ -52,7 +52,7 @@ _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, chardev_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs, hwcore_rs,
> +  dependencies: [anyhow_rs, common_rs, chardev_rs, foreign_rs, libc_rs, qemu_macros, qemuutil_rs, util_rs, migration_rs, bql_rs, qom_rs, system_rs, hwcore_rs,
>                    qom, hwcore, chardev, migration],
>   )
>   
> @@ -60,7 +60,7 @@ rust.test('rust-qemu-api-tests', _qemu_api_rs,
>             suite: ['unit', 'rust'])
>   
>   qemu_api = declare_dependency(link_with: [_qemu_api_rs],
> -  dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
> +  dependencies: [qemu_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
> 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 0cd40c8e16..3b6f1d337f 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 2152bcb99b..d0b2992e20 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 46bbf7c7fe..060ad2ec34 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 6e95d75fa0..40a2bf9ae0 100644
> --- a/rust/qom/meson.build
> +++ b/rust/qom/meson.build
> @@ -31,10 +31,10 @@ _qom_rs = static_library(
>     override_options: ['rust_std=2021', 'build.rust_std=2021'],
>     rust_abi: 'rust',
>     rust_args: _qom_cfg,
> -  dependencies: [qemuutil_rs, bql_rs, common_rs, migration_rs, qemu_api_macros, qom],
> +  dependencies: [qemuutil_rs, bql_rs, common_rs, migration_rs, qemu_macros, qom],
>   )
>   
> -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 a632ec43f2..880bef6c47 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/qom/tests/tests.rs b/rust/qom/tests/tests.rs
> index 49f1cbecf5..4d710f96a6 100644
> --- a/rust/qom/tests/tests.rs
> +++ b/rust/qom/tests/tests.rs
> @@ -4,7 +4,7 @@
>   use util::bindings::{module_call_init, module_init_type};
>   
>   #[repr(C)]
> -#[derive(qemu_api_macros::Object)]
> +#[derive(qemu_macros::Object)]
>   pub struct DummyObject {
>       parent: ParentField<Object>,
>   }
> diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
> index 6803895e08..d8338c8348 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 cbd3eb4717..710462376d 100644
> --- a/rust/system/meson.build
> +++ b/rust/system/meson.build
> @@ -39,12 +39,12 @@ _system_rs = static_library(
>     override_options: ['rust_std=2021', 'build.rust_std=2021'],
>     rust_abi: 'rust',
>     rust_args: _system_cfg,
> -  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_api_macros, qom_rs, util_rs,
> +  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qemu_macros, qom_rs, util_rs,
>                   hwcore],
>   )
>   
>   system_rs = declare_dependency(link_with: [_system_rs],
> -  dependencies: [qemu_api_macros, hwcore])
> +  dependencies: [qemu_macros, hwcore])
>   
>   # 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/system/src/memory.rs b/rust/system/src/memory.rs
> index af69fb30bf..898401c33b 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 aa10f03384..c5d1adb8fe 100644
> --- a/rust/util/Cargo.toml
> +++ b/rust/util/Cargo.toml
> @@ -17,7 +17,7 @@ anyhow = "~1.0"
>   libc = "0.2.162"
>   foreign = "~0.3.1"
>   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 3fe8e3975f..1e449b8dc7 100644
> --- a/rust/util/meson.build
> +++ b/rust/util/meson.build
> @@ -45,7 +45,7 @@ _util_rs = static_library(
>     override_options: ['rust_std=2021', 'build.rust_std=2021'],
>     rust_abi: 'rust',
>     rust_args: _util_cfg,
> -  dependencies: [anyhow_rs, libc_rs, foreign_rs, qemuutil_rs, common_rs, qemu_api_macros],
> +  dependencies: [anyhow_rs, libc_rs, foreign_rs, qemuutil_rs, common_rs, qemu_macros],
>   )
>   
>   util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil_rs, qom])
> diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs
> index 441072a949..42096cef0d 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 {}
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 00/18] rust: split qemu-api
  2025-08-26 14:44 ` [RFC 00/18] rust: split qemu-api Paolo Bonzini
@ 2025-08-26 14:55   ` Manos Pitsidianakis
  2025-08-26 15:22     ` Marc-André Lureau
  2025-08-26 15:33     ` Paolo Bonzini
  2025-08-26 15:15   ` Marc-André Lureau
  1 sibling, 2 replies; 32+ messages in thread
From: Manos Pitsidianakis @ 2025-08-26 14:55 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: marcandre.lureau, qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé
On Tue, Aug 26, 2025 at 5:44 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > Hi,
> >
> > qemu-api is providing bindings for various internal libraries. Instead, the
> > bindings requirements should match the various libraries and use the minimal set
> > of dependencies.
> >
> > An initial Rust-only "common" crate is introduced, then "util" (for libqemuutil,
> > without bql), "migration" (so it doesn't depend on bql), "bql", "qom" (arguably,
> > bql shouldn't be required?), and "chardev", "system", "hwcore". Finally the
> > qemu-api crates are renamed and repurposed.
> >
> > This involves a lot of code churn, so hopefully it can be reviewed and merged
> > early and iterated upon :)
>
> The one comment that I would like to handle before merging, is that I'd
> prefer to keep the preludes and, in fact, even add more exports to them
> since they can now be chosen per-crate.  Ideally, many of the crates
> you've crated would be accessed through many "use xyz::prelude::*"
> statements.
I kind of agree with this sentiment. What this series basically does
is unwrapping most modules to standalone crates. Semantically, they
make sense to be their own crates just like they made sense being
their own modules before). But I'm not sure what developer benefit
this brings, it's like splitting Rust's std crate to separate crates
(ergonomically speaking). Big crates like tokio split implementation
to crates and then re-export them under tokio::* to ease compilation
parallelism which we should definitely try to do as well. However in
our case our Rust wrappers are very small. Let's do this if they ever
end up growing unwieldy.
> Also, if I understood correctly the split util/errno.rs can move to
> common/.  While it has a dependency on libc, it doesn't need bindgen.
>
> There's a bunch of code duplication for the various bindings.rs and
> build.rs files, which is not ideal but shouldn't grow much more than
> this.  I wonder if, later, common code across build.rs could be written
> just once by adding a new crate (e.g. "qemu_meson") to the workspace,
> that can be used as a build-dependency.
>
> Paolo
>
> > Marc-André Lureau (18):
> >    rust: remove unused global qemu "allocator"
> >    rust: add workspace authors
> >    rust: split Rust-only "common" crate
> >    rust: split "util" crate
> >    rust: move vmstate_clock!() to qdev module
> >    rust: move VMState handling to QOM module
> >    rust: move Cell vmstate impl
> >    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
> >    docs: update rust.rst
> >
> >   MAINTAINERS                                   |  12 +-
> >   docs/devel/rust.rst                           |  51 +--
> >   meson.build                                   |   4 -
> >   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 ++
> >   rust/Cargo.lock                               | 127 ++++++-
> >   rust/Cargo.toml                               |  16 +-
> >   rust/bits/Cargo.toml                          |   2 +-
> >   rust/bits/meson.build                         |   2 +-
> >   rust/bits/src/lib.rs                          |   4 +-
> >   rust/{qemu-api => bql}/Cargo.toml             |  13 +-
> >   rust/{qemu-api => bql}/build.rs               |   2 +-
> >   rust/bql/meson.build                          |  52 +++
> >   rust/bql/src/bindings.rs                      |  25 ++
> >   rust/{qemu-api => bql}/src/cell.rs            | 333 +++---------------
> >   rust/bql/src/lib.rs                           |  29 ++
> >   rust/chardev/Cargo.toml                       |  24 ++
> >   rust/chardev/build.rs                         |  43 +++
> >   rust/chardev/meson.build                      |  54 +++
> >   rust/chardev/src/bindings.rs                  |  36 ++
> >   rust/{qemu-api => chardev}/src/chardev.rs     |  35 +-
> >   rust/chardev/src/lib.rs                       |   4 +
> >   rust/common/Cargo.toml                        |  16 +
> >   rust/common/meson.build                       |  32 ++
> >   rust/{qemu-api => common}/src/assertions.rs   |  16 +-
> >   rust/{qemu-api => common}/src/bitops.rs       |   1 -
> >   rust/{qemu-api => common}/src/callbacks.rs    |  12 +-
> >   rust/common/src/lib.rs                        |  17 +
> >   rust/common/src/opaque.rs                     | 240 +++++++++++++
> >   rust/{qemu-api => common}/src/uninit.rs       |   2 +-
> >   rust/common/src/zeroable.rs                   |  18 +
> >   rust/hw/char/pl011/Cargo.toml                 |  11 +-
> >   rust/hw/char/pl011/build.rs                   |  43 +++
> >   rust/hw/char/pl011/meson.build                |  39 +-
> >   rust/hw/char/pl011/src/bindings.rs            |  27 ++
> >   rust/hw/char/pl011/src/device.rs              |  49 +--
> >   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                         |  43 +++
> >   rust/{qemu-api => hw/core}/meson.build        |  86 ++---
> >   rust/hw/core/src/bindings.rs                  |  41 +++
> >   rust/{qemu-api => hw/core}/src/irq.rs         |  18 +-
> >   rust/hw/core/src/lib.rs                       |  12 +
> >   rust/{qemu-api => hw/core}/src/qdev.rs        |  81 +++--
> >   rust/{qemu-api => hw/core}/src/sysbus.rs      |  28 +-
> >   rust/{qemu-api => hw/core}/tests/tests.rs     |  29 +-
> >   rust/hw/timer/hpet/Cargo.toml                 |  10 +-
> >   rust/hw/timer/hpet/meson.build                |  12 +-
> >   rust/hw/timer/hpet/src/device.rs              |  56 ++-
> >   rust/hw/timer/hpet/src/fw_cfg.rs              |   6 +-
> >   rust/meson.build                              |  12 +-
> >   rust/migration/Cargo.toml                     |  21 ++
> >   rust/migration/build.rs                       |  43 +++
> >   rust/migration/meson.build                    |  57 +++
> >   rust/migration/src/bindings.rs                |  48 +++
> >   rust/migration/src/lib.rs                     |   4 +
> >   rust/{qemu-api => migration}/src/vmstate.rs   | 166 ++++-----
> >   rust/qemu-api/.gitignore                      |   2 -
> >   rust/qemu-api/README.md                       |  19 -
> >   rust/qemu-api/src/lib.rs                      | 170 ---------
> >   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                                |  20 +-
> >   .../src/tests.rs                              |   8 +-
> >   rust/qom/Cargo.toml                           |  23 ++
> >   rust/qom/build.rs                             |  43 +++
> >   rust/qom/meson.build                          |  61 ++++
> >   rust/qom/src/bindings.rs                      |  25 ++
> >   rust/qom/src/lib.rs                           |   4 +
> >   rust/{qemu-api => qom}/src/qom.rs             |  27 +-
> >   rust/qom/tests/tests.rs                       |  47 +++
> >   rust/system/Cargo.toml                        |  22 ++
> >   rust/system/build.rs                          |  43 +++
> >   rust/system/meson.build                       |  57 +++
> >   rust/{qemu-api => system}/src/bindings.rs     |  33 +-
> >   rust/system/src/lib.rs                        |   4 +
> >   rust/{qemu-api => system}/src/memory.rs       |  20 +-
> >   rust/tests/Cargo.toml                         |  30 ++
> >   rust/tests/meson.build                        |  14 +
> >   .../tests/vmstate_tests.rs                    |  18 +-
> >   rust/util/Cargo.toml                          |  23 ++
> >   rust/util/build.rs                            |  43 +++
> >   rust/util/meson.build                         |  61 ++++
> >   rust/util/src/bindings.rs                     |  25 ++
> >   rust/{qemu-api => util}/src/errno.rs          |  11 +-
> >   rust/{qemu-api => util}/src/error.rs          |   6 +-
> >   rust/util/src/lib.rs                          |  10 +
> >   rust/{qemu-api => util}/src/log.rs            |  12 +-
> >   rust/{qemu-api => util}/src/module.rs         |   2 +-
> >   rust/{qemu-api => util}/src/timer.rs          |  12 +-
> >   100 files changed, 2372 insertions(+), 1044 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
> >   rename rust/{qemu-api => bql}/Cargo.toml (52%)
> >   rename rust/{qemu-api => bql}/build.rs (96%)
> >   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 100644 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 (91%)
> >   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 (92%)
> >   rename rust/{qemu-api => common}/src/bitops.rs (98%)
> >   rename rust/{qemu-api => common}/src/callbacks.rs (97%)
> >   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
> >   create mode 100644 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 100644 rust/hw/core/build.rs
> >   rename rust/{qemu-api => hw/core}/meson.build (52%)
> >   create mode 100644 rust/hw/core/src/bindings.rs
> >   rename rust/{qemu-api => hw/core}/src/irq.rs (92%)
> >   create mode 100644 rust/hw/core/src/lib.rs
> >   rename rust/{qemu-api => hw/core}/src/qdev.rs (86%)
> >   rename rust/{qemu-api => hw/core}/src/sysbus.rs (87%)
> >   rename rust/{qemu-api => hw/core}/tests/tests.rs (88%)
> >   create mode 100644 rust/migration/Cargo.toml
> >   create mode 100644 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 (80%)
> >   delete mode 100644 rust/qemu-api/.gitignore
> >   delete mode 100644 rust/qemu-api/README.md
> >   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 (91%)
> >   rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (93%)
> >   create mode 100644 rust/qom/Cargo.toml
> >   create mode 100644 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
> >   rename rust/{qemu-api => qom}/src/qom.rs (98%)
> >   create mode 100644 rust/qom/tests/tests.rs
> >   create mode 100644 rust/system/Cargo.toml
> >   create mode 100644 rust/system/build.rs
> >   create mode 100644 rust/system/meson.build
> >   rename rust/{qemu-api => system}/src/bindings.rs (56%)
> >   create mode 100644 rust/system/src/lib.rs
> >   rename rust/{qemu-api => system}/src/memory.rs (95%)
> >   create mode 100644 rust/tests/Cargo.toml
> >   create mode 100644 rust/tests/meson.build
> >   rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (96%)
> >   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/errno.rs (98%)
> >   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 (93%)
> >
>
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 00/18] rust: split qemu-api
  2025-08-26 14:44 ` [RFC 00/18] rust: split qemu-api Paolo Bonzini
  2025-08-26 14:55   ` Manos Pitsidianakis
@ 2025-08-26 15:15   ` Marc-André Lureau
  1 sibling, 0 replies; 32+ messages in thread
From: Marc-André Lureau @ 2025-08-26 15:15 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé, Manos Pitsidianakis
[-- Attachment #1: Type: text/plain, Size: 2270 bytes --]
Hi
On Tue, Aug 26, 2025 at 6:44 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
> On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > Hi,
> >
> > qemu-api is providing bindings for various internal libraries. Instead,
> the
> > bindings requirements should match the various libraries and use the
> minimal set
> > of dependencies.
> >
> > An initial Rust-only "common" crate is introduced, then "util" (for
> libqemuutil,
> > without bql), "migration" (so it doesn't depend on bql), "bql", "qom"
> (arguably,
> > bql shouldn't be required?), and "chardev", "system", "hwcore". Finally
> the
> > qemu-api crates are renamed and repurposed.
> >
> > This involves a lot of code churn, so hopefully it can be reviewed and
> merged
> > early and iterated upon :)
>
> The one comment that I would like to handle before merging, is that I'd
> prefer to keep the preludes and, in fact, even add more exports to them
> since they can now be chosen per-crate.  Ideally, many of the crates
> you've crated would be accessed through many "use xyz::prelude::*"
> statements.
>
It's not very useful if you have to include all the preludes though :)
Also, prelude feels a bit too much magic and may pollute namespace. In
general, not so many crates provide them and that's usually a good thing
imho, since you can explicitly import what you need, and LSP and good error
report make that fairly trivial.
So, I am not convinced having each crate its own prelude is a good thing,
but I don't care much. Can easily be added back though.
>
> Also, if I understood correctly the split util/errno.rs can move to
> common/.  While it has a dependency on libc, it doesn't need bindgen.
>
>
True, I will move it
> There's a bunch of code duplication for the various bindings.rs and
> build.rs files, which is not ideal but shouldn't grow much more than
> this.  I wonder if, later, common code across build.rs could be written
> just once by adding a new crate (e.g. "qemu_meson") to the workspace,
> that can be used as a build-dependency.
>
>
I am not sure, but I can try. Alternatively, we can have a build.rs
symlink, if we can guess the module path.
[-- Attachment #2: Type: text/html, Size: 3645 bytes --]
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 00/18] rust: split qemu-api
  2025-08-26 14:55   ` Manos Pitsidianakis
@ 2025-08-26 15:22     ` Marc-André Lureau
  2025-08-26 15:33     ` Paolo Bonzini
  1 sibling, 0 replies; 32+ messages in thread
From: Marc-André Lureau @ 2025-08-26 15:22 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: Paolo Bonzini, qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé
[-- Attachment #1: Type: text/plain, Size: 14913 bytes --]
Hi
On Tue, Aug 26, 2025 at 6:56 PM Manos Pitsidianakis <
manos.pitsidianakis@linaro.org> wrote:
> On Tue, Aug 26, 2025 at 5:44 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
> >
> > On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> > > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> > >
> > > Hi,
> > >
> > > qemu-api is providing bindings for various internal libraries.
> Instead, the
> > > bindings requirements should match the various libraries and use the
> minimal set
> > > of dependencies.
> > >
> > > An initial Rust-only "common" crate is introduced, then "util" (for
> libqemuutil,
> > > without bql), "migration" (so it doesn't depend on bql), "bql", "qom"
> (arguably,
> > > bql shouldn't be required?), and "chardev", "system", "hwcore".
> Finally the
> > > qemu-api crates are renamed and repurposed.
> > >
> > > This involves a lot of code churn, so hopefully it can be reviewed and
> merged
> > > early and iterated upon :)
> >
> > The one comment that I would like to handle before merging, is that I'd
> > prefer to keep the preludes and, in fact, even add more exports to them
> > since they can now be chosen per-crate.  Ideally, many of the crates
> > you've crated would be accessed through many "use xyz::prelude::*"
> > statements.
>
> I kind of agree with this sentiment. What this series basically does
> is unwrapping most modules to standalone crates. Semantically, they
> make sense to be their own crates just like they made sense being
> their own modules before). But I'm not sure what developer benefit
>
Indeed, that's the point. The benefit is that you can compile and link only
what you need, just like how qemu code and libraries are organized. Modules
become quickly tangled between each other if you don't do it.
> this brings, it's like splitting Rust's std crate to separate crates
> (ergonomically speaking). Big crates like tokio split implementation
> to crates and then re-export them under tokio::* to ease compilation
> parallelism which we should definitely try to do as well. However in
> our case our Rust wrappers are very small. Let's do this if they ever
> end up growing unwieldy.
>
It will become difficult to manage. It's already hard to know which type
comes from which library/header. Better to do the split early imho. It was
nice to have a single crate in the early days, but we should prepare to
scale up now.
> > Also, if I understood correctly the split util/errno.rs can move to
> > common/.  While it has a dependency on libc, it doesn't need bindgen.
> >
> > There's a bunch of code duplication for the various bindings.rs and
> > build.rs files, which is not ideal but shouldn't grow much more than
> > this.  I wonder if, later, common code across build.rs could be written
> > just once by adding a new crate (e.g. "qemu_meson") to the workspace,
> > that can be used as a build-dependency.
> >
> > Paolo
> >
> > > Marc-André Lureau (18):
> > >    rust: remove unused global qemu "allocator"
> > >    rust: add workspace authors
> > >    rust: split Rust-only "common" crate
> > >    rust: split "util" crate
> > >    rust: move vmstate_clock!() to qdev module
> > >    rust: move VMState handling to QOM module
> > >    rust: move Cell vmstate impl
> > >    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
> > >    docs: update rust.rst
> > >
> > >   MAINTAINERS                                   |  12 +-
> > >   docs/devel/rust.rst                           |  51 +--
> > >   meson.build                                   |   4 -
> > >   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 ++
> > >   rust/Cargo.lock                               | 127 ++++++-
> > >   rust/Cargo.toml                               |  16 +-
> > >   rust/bits/Cargo.toml                          |   2 +-
> > >   rust/bits/meson.build                         |   2 +-
> > >   rust/bits/src/lib.rs                          |   4 +-
> > >   rust/{qemu-api => bql}/Cargo.toml             |  13 +-
> > >   rust/{qemu-api => bql}/build.rs               |   2 +-
> > >   rust/bql/meson.build                          |  52 +++
> > >   rust/bql/src/bindings.rs                      |  25 ++
> > >   rust/{qemu-api => bql}/src/cell.rs            | 333
> +++---------------
> > >   rust/bql/src/lib.rs                           |  29 ++
> > >   rust/chardev/Cargo.toml                       |  24 ++
> > >   rust/chardev/build.rs                         |  43 +++
> > >   rust/chardev/meson.build                      |  54 +++
> > >   rust/chardev/src/bindings.rs                  |  36 ++
> > >   rust/{qemu-api => chardev}/src/chardev.rs     |  35 +-
> > >   rust/chardev/src/lib.rs                       |   4 +
> > >   rust/common/Cargo.toml                        |  16 +
> > >   rust/common/meson.build                       |  32 ++
> > >   rust/{qemu-api => common}/src/assertions.rs   |  16 +-
> > >   rust/{qemu-api => common}/src/bitops.rs       |   1 -
> > >   rust/{qemu-api => common}/src/callbacks.rs    |  12 +-
> > >   rust/common/src/lib.rs                        |  17 +
> > >   rust/common/src/opaque.rs                     | 240 +++++++++++++
> > >   rust/{qemu-api => common}/src/uninit.rs       |   2 +-
> > >   rust/common/src/zeroable.rs                   |  18 +
> > >   rust/hw/char/pl011/Cargo.toml                 |  11 +-
> > >   rust/hw/char/pl011/build.rs                   |  43 +++
> > >   rust/hw/char/pl011/meson.build                |  39 +-
> > >   rust/hw/char/pl011/src/bindings.rs            |  27 ++
> > >   rust/hw/char/pl011/src/device.rs              |  49 +--
> > >   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                         |  43 +++
> > >   rust/{qemu-api => hw/core}/meson.build        |  86 ++---
> > >   rust/hw/core/src/bindings.rs                  |  41 +++
> > >   rust/{qemu-api => hw/core}/src/irq.rs         |  18 +-
> > >   rust/hw/core/src/lib.rs                       |  12 +
> > >   rust/{qemu-api => hw/core}/src/qdev.rs        |  81 +++--
> > >   rust/{qemu-api => hw/core}/src/sysbus.rs      |  28 +-
> > >   rust/{qemu-api => hw/core}/tests/tests.rs     |  29 +-
> > >   rust/hw/timer/hpet/Cargo.toml                 |  10 +-
> > >   rust/hw/timer/hpet/meson.build                |  12 +-
> > >   rust/hw/timer/hpet/src/device.rs              |  56 ++-
> > >   rust/hw/timer/hpet/src/fw_cfg.rs              |   6 +-
> > >   rust/meson.build                              |  12 +-
> > >   rust/migration/Cargo.toml                     |  21 ++
> > >   rust/migration/build.rs                       |  43 +++
> > >   rust/migration/meson.build                    |  57 +++
> > >   rust/migration/src/bindings.rs                |  48 +++
> > >   rust/migration/src/lib.rs                     |   4 +
> > >   rust/{qemu-api => migration}/src/vmstate.rs   | 166 ++++-----
> > >   rust/qemu-api/.gitignore                      |   2 -
> > >   rust/qemu-api/README.md                       |  19 -
> > >   rust/qemu-api/src/lib.rs                      | 170 ---------
> > >   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                                |  20 +-
> > >   .../src/tests.rs                              |   8 +-
> > >   rust/qom/Cargo.toml                           |  23 ++
> > >   rust/qom/build.rs                             |  43 +++
> > >   rust/qom/meson.build                          |  61 ++++
> > >   rust/qom/src/bindings.rs                      |  25 ++
> > >   rust/qom/src/lib.rs                           |   4 +
> > >   rust/{qemu-api => qom}/src/qom.rs             |  27 +-
> > >   rust/qom/tests/tests.rs                       |  47 +++
> > >   rust/system/Cargo.toml                        |  22 ++
> > >   rust/system/build.rs                          |  43 +++
> > >   rust/system/meson.build                       |  57 +++
> > >   rust/{qemu-api => system}/src/bindings.rs     |  33 +-
> > >   rust/system/src/lib.rs                        |   4 +
> > >   rust/{qemu-api => system}/src/memory.rs       |  20 +-
> > >   rust/tests/Cargo.toml                         |  30 ++
> > >   rust/tests/meson.build                        |  14 +
> > >   .../tests/vmstate_tests.rs                    |  18 +-
> > >   rust/util/Cargo.toml                          |  23 ++
> > >   rust/util/build.rs                            |  43 +++
> > >   rust/util/meson.build                         |  61 ++++
> > >   rust/util/src/bindings.rs                     |  25 ++
> > >   rust/{qemu-api => util}/src/errno.rs          |  11 +-
> > >   rust/{qemu-api => util}/src/error.rs          |   6 +-
> > >   rust/util/src/lib.rs                          |  10 +
> > >   rust/{qemu-api => util}/src/log.rs            |  12 +-
> > >   rust/{qemu-api => util}/src/module.rs         |   2 +-
> > >   rust/{qemu-api => util}/src/timer.rs          |  12 +-
> > >   100 files changed, 2372 insertions(+), 1044 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
> > >   rename rust/{qemu-api => bql}/Cargo.toml (52%)
> > >   rename rust/{qemu-api => bql}/build.rs (96%)
> > >   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 100644 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 (91%)
> > >   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 (92%)
> > >   rename rust/{qemu-api => common}/src/bitops.rs (98%)
> > >   rename rust/{qemu-api => common}/src/callbacks.rs (97%)
> > >   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
> > >   create mode 100644 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 100644 rust/hw/core/build.rs
> > >   rename rust/{qemu-api => hw/core}/meson.build (52%)
> > >   create mode 100644 rust/hw/core/src/bindings.rs
> > >   rename rust/{qemu-api => hw/core}/src/irq.rs (92%)
> > >   create mode 100644 rust/hw/core/src/lib.rs
> > >   rename rust/{qemu-api => hw/core}/src/qdev.rs (86%)
> > >   rename rust/{qemu-api => hw/core}/src/sysbus.rs (87%)
> > >   rename rust/{qemu-api => hw/core}/tests/tests.rs (88%)
> > >   create mode 100644 rust/migration/Cargo.toml
> > >   create mode 100644 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 (80%)
> > >   delete mode 100644 rust/qemu-api/.gitignore
> > >   delete mode 100644 rust/qemu-api/README.md
> > >   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 (91%)
> > >   rename rust/{qemu-api-macros => qemu-macros}/src/tests.rs (93%)
> > >   create mode 100644 rust/qom/Cargo.toml
> > >   create mode 100644 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
> > >   rename rust/{qemu-api => qom}/src/qom.rs (98%)
> > >   create mode 100644 rust/qom/tests/tests.rs
> > >   create mode 100644 rust/system/Cargo.toml
> > >   create mode 100644 rust/system/build.rs
> > >   create mode 100644 rust/system/meson.build
> > >   rename rust/{qemu-api => system}/src/bindings.rs (56%)
> > >   create mode 100644 rust/system/src/lib.rs
> > >   rename rust/{qemu-api => system}/src/memory.rs (95%)
> > >   create mode 100644 rust/tests/Cargo.toml
> > >   create mode 100644 rust/tests/meson.build
> > >   rename rust/{qemu-api => tests}/tests/vmstate_tests.rs (96%)
> > >   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/errno.rs (98%)
> > >   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 (93%)
> > >
> >
>
>
[-- Attachment #2: Type: text/html, Size: 26819 bytes --]
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 00/18] rust: split qemu-api
  2025-08-26 14:55   ` Manos Pitsidianakis
  2025-08-26 15:22     ` Marc-André Lureau
@ 2025-08-26 15:33     ` Paolo Bonzini
  1 sibling, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-08-26 15:33 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: marcandre.lureau, qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé
On Tue, Aug 26, 2025 at 4:56 PM Manos Pitsidianakis
<manos.pitsidianakis@linaro.org> wrote:
> I kind of agree with this sentiment. What this series basically does
> is unwrapping most modules to standalone crates. Semantically, they
> make sense to be their own crates just like they made sense being
> their own modules before). But I'm not sure what developer benefit
> this brings,
The benefit (or rather, the problem that this solves) is that not all
targets in the QEMU build use all the C libraries; in particular,
tools like qemu-img do not include hw/.  Therefore, when Kevin was
writing his Rust block layer experiments, he had to build a separate
version of qemu_api that left out anything that could cause a linking
failure for qemu-img and friends[1].
Since we already have a well-proven module split in the form of
variables defined with declare_dependency(), using it for Rust as well
makes the most sense.  I can't exclude that in the future we might
split even more (i2c, pci, etc.) but for now the split that Marc-André
used with a single "system" crate is good enough.
Paolo
[1] https://lore.kernel.org/qemu-devel/20250218182019.111467-2-kwolf@redhat.com/
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 07/18] rust: move Cell vmstate impl
  2025-08-26 14:04 ` [RFC 07/18] rust: move Cell vmstate impl marcandre.lureau
@ 2025-08-26 18:28   ` Paolo Bonzini
  2025-08-26 19:06     ` Marc-André Lureau
  0 siblings, 1 reply; 32+ messages in thread
From: Paolo Bonzini @ 2025-08-26 18:28 UTC (permalink / raw)
  To: marcandre.lureau, qemu-devel
  Cc: Daniel P. Berrangé, qemu-rust, Philippe Mathieu-Daudé,
	Manos Pitsidianakis
On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> This will allow to split vmstate to a standalone crate next.
Can you explain why this is needed?  Could "migration" depend on "bql" 
(or even, "bql" could stay in util), and keep the implementation of 
VMState for cells, just like you do for Opaque?
Thanks,
Paolo
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>   rust/qemu-api/src/cell.rs    |  5 ++++-
>   rust/qemu-api/src/vmstate.rs | 16 +++++++---------
>   2 files changed, 11 insertions(+), 10 deletions(-)
> 
> diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
> index 98d720caf9..4bce526e45 100644
> --- a/rust/qemu-api/src/cell.rs
> +++ b/rust/qemu-api/src/cell.rs
> @@ -152,7 +152,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() {
> @@ -866,3 +866,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
>           (**self).fmt(f)
>       }
>   }
> +
> +impl_vmstate_transparent!(BqlCell<T> where T: VMState);
> +impl_vmstate_transparent!(BqlRefCell<T> where T: VMState);
> diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
> index c1e2b06390..9d33997c57 100644
> --- a/rust/qemu-api/src/vmstate.rs
> +++ b/rust/qemu-api/src/vmstate.rs
> @@ -29,8 +29,7 @@
>   
>   use common::{callbacks::FnCall, Zeroable};
>   
> -use crate::bindings::VMStateFlags;
> -pub use crate::bindings::{VMStateDescription, VMStateField};
> +pub use crate::bindings::{VMStateDescription, VMStateField, VMStateFlags};
>   
>   /// This macro is used to call a function with a generic argument bound
>   /// to the type of a field.  The function must take a
> @@ -325,15 +324,16 @@ 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 SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE;
> -            const BASE: VMStateField = VMStateField {
> +        unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
> +            const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = <$base as $crate::vmstate::VMState>::SCALAR_TYPE;
> +            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::vmstate::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG;
>           }
>       };
>   }
> @@ -341,8 +341,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!(::common::Opaque<T> where T: VMState);
>   
>   #[macro_export]
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 07/18] rust: move Cell vmstate impl
  2025-08-26 18:28   ` Paolo Bonzini
@ 2025-08-26 19:06     ` Marc-André Lureau
  2025-08-27  7:35       ` Paolo Bonzini
  0 siblings, 1 reply; 32+ messages in thread
From: Marc-André Lureau @ 2025-08-26 19:06 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé, Manos Pitsidianakis
[-- Attachment #1: Type: text/plain, Size: 4373 bytes --]
Hi
On Tue, Aug 26, 2025 at 10:28 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
> On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > This will allow to split vmstate to a standalone crate next.
>
> Can you explain why this is needed?  Could "migration" depend on "bql"
> (or even, "bql" could stay in util), and keep the implementation of
> VMState for cells, just like you do for Opaque?
>
vmstate doesn't require bql. Why should we enforce it at rust level?
If we merge bql in util, then sure we can make vmstate keep the VMState
impl for BqlCells. But why should we do that? To save one crate? I think it
will more future proof if we have a lower-level util crate without bql, and
higher-level crates that rely on it. Perhaps "bql" should be renamed though
(qemu-loop/iothread or something?)
>
> Thanks,
>
> Paolo
>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >   rust/qemu-api/src/cell.rs    |  5 ++++-
> >   rust/qemu-api/src/vmstate.rs | 16 +++++++---------
> >   2 files changed, 11 insertions(+), 10 deletions(-)
> >
> > diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
> > index 98d720caf9..4bce526e45 100644
> > --- a/rust/qemu-api/src/cell.rs
> > +++ b/rust/qemu-api/src/cell.rs
> > @@ -152,7 +152,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() {
> > @@ -866,3 +866,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) ->
> fmt::Result {
> >           (**self).fmt(f)
> >       }
> >   }
> > +
> > +impl_vmstate_transparent!(BqlCell<T> where T: VMState);
> > +impl_vmstate_transparent!(BqlRefCell<T> where T: VMState);
> > diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
> > index c1e2b06390..9d33997c57 100644
> > --- a/rust/qemu-api/src/vmstate.rs
> > +++ b/rust/qemu-api/src/vmstate.rs
> > @@ -29,8 +29,7 @@
> >
> >   use common::{callbacks::FnCall, Zeroable};
> >
> > -use crate::bindings::VMStateFlags;
> > -pub use crate::bindings::{VMStateDescription, VMStateField};
> > +pub use crate::bindings::{VMStateDescription, VMStateField,
> VMStateFlags};
> >
> >   /// This macro is used to call a function with a generic argument bound
> >   /// to the type of a field.  The function must take a
> > @@ -325,15 +324,16 @@ 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 SCALAR_TYPE: VMStateFieldType = <$base as
> VMState>::SCALAR_TYPE;
> > -            const BASE: VMStateField = VMStateField {
> > +        unsafe impl<$base> $crate::vmstate::VMState for $type where
> $base: $crate::vmstate::VMState $($where)* {
> > +            const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
> <$base as $crate::vmstate::VMState>::SCALAR_TYPE;
> > +            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::vmstate::VMStateFlags = <$base
> as $crate::vmstate::VMState>::VARRAY_FLAG;
> >           }
> >       };
> >   }
> > @@ -341,8 +341,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!(::common::Opaque<T> where T: VMState);
> >
> >   #[macro_export]
>
>
[-- Attachment #2: Type: text/html, Size: 6446 bytes --]
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 14/18] rust: rename qemu_api_macros -> qemu_macros
  2025-08-26 14:53   ` Paolo Bonzini
@ 2025-08-26 20:30     ` Marc-André Lureau
  0 siblings, 0 replies; 32+ messages in thread
From: Marc-André Lureau @ 2025-08-26 20:30 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé, Manos Pitsidianakis
Hi
On Tue, Aug 26, 2025 at 6:55 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> On 8/26/25 16:04, marcandre.lureau@redhat.com wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > Since "qemu_api" is no longer the unique crate to provide APIs.
>
> In addition to renaming, maybe you can re-export the macros from the
> appropriate crate?
>
> derive(Object) -> qom
> derive(Wrapper) -> common
> derive(TryInto) -> common
> derive(Device) -> hwcore
>
> That is, keep a single qemu_api_macros crate for the implementation but
> allow using the nicer #[derive(qom::Object)].
>
Good idea! working on it
-- 
Marc-André Lureau
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 10/18] rust: split "qom" crate
  2025-08-26 14:04 ` [RFC 10/18] rust: split "qom" crate marcandre.lureau
@ 2025-08-27  6:55   ` Zhao Liu
  2025-08-27  8:57     ` Marc-André Lureau
  0 siblings, 1 reply; 32+ messages in thread
From: Zhao Liu @ 2025-08-27  6:55 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, Paolo Bonzini, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé, Manos Pitsidianakis
> diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
> index 7cffb894a8..a3bcd1297a 100644
> --- a/rust/hw/char/pl011/src/device.rs
> +++ b/rust/hw/char/pl011/src/device.rs
> @@ -21,10 +21,13 @@
>      memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
>      prelude::*,
>      qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
> -    qom::{ObjectImpl, Owned, ParentField, ParentInit},
>      sysbus::{SysBusDevice, SysBusDeviceImpl},
>      vmstate_clock,
>  };
> +use qom::{
> +    qom_isa, IsA, Object, ObjectClassMethods, ObjectDeref, ObjectImpl, ObjectMethods, ObjectType,
> +    Owned, ParentField, ParentInit,
> +};
These QOM parts are frequently used and very common. at least for qom,
I think prelude would help a lot.
A qom prelude could help reduce the changes in other parts (pl011/
hpet/memory...).
> diff --git a/rust/qom/meson.build b/rust/qom/meson.build
> new file mode 100644
> index 0000000000..6e95d75fa0
> --- /dev/null
> +++ b/rust/qom/meson.build
> @@ -0,0 +1,61 @@
> +_qom_cfg = run_command(rustc_args,
> +  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
> +  capture: true, check: true).stdout().strip().splitlines()
> +
> +# 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',
There're many binding files with the same name. What about adding a prefix
like "qom-bindings" to distinguish it? This can help search and locate
specific binding file.
> +  include_directories: bindings_incdir,
> +  bindgen_version: ['>=0.60.0'],
> +  args: bindgen_args_common,
> +)
...
> diff --git a/rust/qom/tests/tests.rs b/rust/qom/tests/tests.rs
> new file mode 100644
> index 0000000000..49f1cbecf5
> --- /dev/null
> +++ b/rust/qom/tests/tests.rs
> @@ -0,0 +1,47 @@
> +use std::{ffi::CStr, sync::LazyLock};
LazyLock is useful, but it became stable since v1.80. So if Paolo
decide pick this series after v1.83 support, it's fine.
> +use qom::{qom_isa, Object, ObjectClassMethods, ObjectImpl, ObjectType, ParentField};
> +use util::bindings::{module_call_init, module_init_type};
...
> +fn init_qom() {
> +    static ONCE: LazyLock<()> = LazyLock::new(|| unsafe {
> +        module_call_init(module_init_type::MODULE_INIT_QOM);
> +    });
But for now, we can still use a static BqlCell<bool> as the workaround,
just like rust/hwcore/tests/tests.rs did, to decouple with MSRV
dependency.
And it seems rust/hwcore/tests/tests.rs has already covered this test
case, do we still need this test?
If so, then it's better to add this new test in a seperate patch, which
makes current patch focus on the splitting :-).
> +    bql::start_test();
> +    LazyLock::force(&ONCE);
> +}
> +
Otherwise, LGTM,
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 07/18] rust: move Cell vmstate impl
  2025-08-26 19:06     ` Marc-André Lureau
@ 2025-08-27  7:35       ` Paolo Bonzini
  0 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-08-27  7:35 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé, Manos Pitsidianakis
On 8/26/25 21:06, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 26, 2025 at 10:28 PM Paolo Bonzini <pbonzini@redhat.com 
> <mailto:pbonzini@redhat.com>> wrote:
> 
>     On 8/26/25 16:04, marcandre.lureau@redhat.com
>     <mailto:marcandre.lureau@redhat.com> wrote:
>      > From: Marc-André Lureau <marcandre.lureau@redhat.com
>     <mailto:marcandre.lureau@redhat.com>>
>      >
>      > This will allow to split vmstate to a standalone crate next.
> 
>     Can you explain why this is needed?  Could "migration" depend on "bql"
>     (or even, "bql" could stay in util), and keep the implementation of
>     VMState for cells, just like you do for Opaque?
> 
> 
> vmstate doesn't require bql. Why should we enforce it at rust level?
In some sense it does: pre_load/post_load/pre_save/post_save run inside 
the BQL.  It doesn't require it at the language level, but it does at 
the conceptual level.
Anyhow the code is fine, I just wanted to understand if there was 
something else.
> If we merge bql in util, then sure we can make vmstate keep the VMState 
> impl for BqlCells. But why should we do that? To save one crate? I think 
> it will more future proof if we have a lower-level util crate without 
> bql, and higher-level crates that rely on it. Perhaps "bql" should be 
> renamed though (qemu-loop/iothread or something?)
I'm okay with keeping bql separate, no problem. event_loop is also okay 
as the name.
Paolo
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 10/18] rust: split "qom" crate
  2025-08-27  6:55   ` Zhao Liu
@ 2025-08-27  8:57     ` Marc-André Lureau
  2025-08-27  9:49       ` Paolo Bonzini
  0 siblings, 1 reply; 32+ messages in thread
From: Marc-André Lureau @ 2025-08-27  8:57 UTC (permalink / raw)
  To: Zhao Liu
  Cc: qemu-devel, Paolo Bonzini, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé, Manos Pitsidianakis
[-- Attachment #1: Type: text/plain, Size: 3965 bytes --]
Hi
On Wed, Aug 27, 2025 at 10:33 AM Zhao Liu <zhao1.liu@intel.com> wrote:
> > diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/
> device.rs
> > index 7cffb894a8..a3bcd1297a 100644
> > --- a/rust/hw/char/pl011/src/device.rs
> > +++ b/rust/hw/char/pl011/src/device.rs
> > @@ -21,10 +21,13 @@
> >      memory::{hwaddr, MemoryRegion, MemoryRegionOps,
> MemoryRegionOpsBuilder},
> >      prelude::*,
> >      qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property,
> ResetType, ResettablePhasesImpl},
> > -    qom::{ObjectImpl, Owned, ParentField, ParentInit},
> >      sysbus::{SysBusDevice, SysBusDeviceImpl},
> >      vmstate_clock,
> >  };
> > +use qom::{
> > +    qom_isa, IsA, Object, ObjectClassMethods, ObjectDeref, ObjectImpl,
> ObjectMethods, ObjectType,
> > +    Owned, ParentField, ParentInit,
> > +};
>
> These QOM parts are frequently used and very common. at least for qom,
> I think prelude would help a lot.
ack
> A qom prelude could help reduce the changes in other parts (pl011/
> hpet/memory...).
>
> > diff --git a/rust/qom/meson.build b/rust/qom/meson.build
> > new file mode 100644
> > index 0000000000..6e95d75fa0
> > --- /dev/null
> > +++ b/rust/qom/meson.build
> > @@ -0,0 +1,61 @@
> > +_qom_cfg = run_command(rustc_args,
> > +  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
> > +  capture: true, check: true).stdout().strip().splitlines()
> > +
> > +# 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',
>
> There're many binding files with the same name. What about adding a prefix
> like "qom-bindings" to distinguish it? This can help search and locate
> specific binding file.
>
they are already under different directories :)
>
> > +  include_directories: bindings_incdir,
> > +  bindgen_version: ['>=0.60.0'],
> > +  args: bindgen_args_common,
> > +)
>
> ...
>
> > diff --git a/rust/qom/tests/tests.rs b/rust/qom/tests/tests.rs
> > new file mode 100644
> > index 0000000000..49f1cbecf5
> > --- /dev/null
> > +++ b/rust/qom/tests/tests.rs
> > @@ -0,0 +1,47 @@
> > +use std::{ffi::CStr, sync::LazyLock};
>
> LazyLock is useful, but it became stable since v1.80. So if Paolo
> decide pick this series after v1.83 support, it's fine.
>
ah, right. I wonder why rust-version didn't warn me about this. I'll check
>
> > +use qom::{qom_isa, Object, ObjectClassMethods, ObjectImpl, ObjectType,
> ParentField};
> > +use util::bindings::{module_call_init, module_init_type};
>
> ...
>
> > +fn init_qom() {
> > +    static ONCE: LazyLock<()> = LazyLock::new(|| unsafe {
> > +        module_call_init(module_init_type::MODULE_INIT_QOM);
> > +    });
>
> But for now, we can still use a static BqlCell<bool> as the workaround,
> just like rust/hwcore/tests/tests.rs did, to decouple with MSRV
> dependency.
>
> And it seems rust/hwcore/tests/tests.rs has already covered this test
> case, do we still need this test?
>
I wanted a simpler test that focuses on QOM only. But this may not be
desirable after all.
>
> If so, then it's better to add this new test in a seperate patch, which
> makes current patch focus on the splitting :-).
>
Agree, I'll drop it for now.
>
> > +    bql::start_test();
> > +    LazyLock::force(&ONCE);
> > +}
> > +
>
> Otherwise, LGTM,
>
> Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
>
>
[-- Attachment #2: Type: text/html, Size: 6281 bytes --]
^ permalink raw reply	[flat|nested] 32+ messages in thread
* Re: [RFC 10/18] rust: split "qom" crate
  2025-08-27  8:57     ` Marc-André Lureau
@ 2025-08-27  9:49       ` Paolo Bonzini
  0 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-08-27  9:49 UTC (permalink / raw)
  To: Marc-André Lureau, Zhao Liu
  Cc: qemu-devel, Daniel P. Berrangé, qemu-rust,
	Philippe Mathieu-Daudé, Manos Pitsidianakis
On 8/27/25 10:57, Marc-André Lureau wrote:
> Hi
> 
> On Wed, Aug 27, 2025 at 10:33 AM Zhao Liu <zhao1.liu@intel.com 
> <mailto:zhao1.liu@intel.com>> wrote:
> 
>      > diff --git a/rust/hw/char/pl011/src/device.rs <http://device.rs>
>     b/rust/hw/char/pl011/src/device.rs <http://device.rs>
>      > index 7cffb894a8..a3bcd1297a 100644
>      > --- a/rust/hw/char/pl011/src/device.rs <http://device.rs>
>      > +++ b/rust/hw/char/pl011/src/device.rs <http://device.rs>
>      > @@ -21,10 +21,13 @@
>      >      memory::{hwaddr, MemoryRegion, MemoryRegionOps,
>     MemoryRegionOpsBuilder},
>      >      prelude::*,
>      >      qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property,
>     ResetType, ResettablePhasesImpl},
>      > -    qom::{ObjectImpl, Owned, ParentField, ParentInit},
>      >      sysbus::{SysBusDevice, SysBusDeviceImpl},
>      >      vmstate_clock,
>      >  };
>      > +use qom::{
>      > +    qom_isa, IsA, Object, ObjectClassMethods, ObjectDeref,
>     ObjectImpl, ObjectMethods, ObjectType,
>      > +    Owned, ParentField, ParentInit,
>      > +};
> 
>     These QOM parts are frequently used and very common. at least for qom,
>     I think prelude would help a lot. 
> 
> 
> ack
> 
>     A qom prelude could help reduce the changes in other parts (pl011/
>     hpet/memory...).
> 
>      > diff --git a/rust/qom/meson.build b/rust/qom/meson.build
>      > new file mode 100644
>      > index 0000000000..6e95d75fa0
>      > --- /dev/null
>      > +++ b/rust/qom/meson.build
>      > @@ -0,0 +1,61 @@
>      > +_qom_cfg = run_command(rustc_args,
>      > +  '--config-headers', config_host_h, '--features',
>     files('Cargo.toml'),
>      > +  capture: true, check: true).stdout().strip().splitlines()
>      > +
>      > +# 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 <http://bindings.inc.rs>',
> 
>     There're many binding files with the same name. What about adding a
>     prefix
>     like "qom-bindings" to distinguish it? This can help search and locate
>     specific binding file.
> 
> 
> they are already under different directories :)
> 
> 
>      > +  include_directories: bindings_incdir,
>      > +  bindgen_version: ['>=0.60.0'],
>      > +  args: bindgen_args_common,
>      > +)
> 
>     ...
> 
>      > diff --git a/rust/qom/tests/tests.rs <http://tests.rs> b/rust/
>     qom/tests/tests.rs <http://tests.rs>
>      > new file mode 100644
>      > index 0000000000..49f1cbecf5
>      > --- /dev/null
>      > +++ b/rust/qom/tests/tests.rs <http://tests.rs>
>      > @@ -0,0 +1,47 @@
>      > +use std::{ffi::CStr, sync::LazyLock};
> 
>     LazyLock is useful, but it became stable since v1.80. So if Paolo
>     decide pick this series after v1.83 support, it's fine.
yes, you all can assume 1.83.
Paolo
^ permalink raw reply	[flat|nested] 32+ messages in thread
end of thread, other threads:[~2025-08-27  9:50 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-26 14:04 [RFC 00/18] rust: split qemu-api marcandre.lureau
2025-08-26 14:04 ` [RFC 01/18] rust: remove unused global qemu "allocator" marcandre.lureau
2025-08-26 14:04 ` [RFC 02/18] rust: add workspace authors marcandre.lureau
2025-08-26 14:04 ` [RFC 03/18] rust: split Rust-only "common" crate marcandre.lureau
2025-08-26 14:04 ` [RFC 04/18] rust: split "util" crate marcandre.lureau
2025-08-26 14:04 ` [RFC 05/18] rust: move vmstate_clock!() to qdev module marcandre.lureau
2025-08-26 14:04 ` [RFC 06/18] rust: move VMState handling to QOM module marcandre.lureau
2025-08-26 14:04 ` [RFC 07/18] rust: move Cell vmstate impl marcandre.lureau
2025-08-26 18:28   ` Paolo Bonzini
2025-08-26 19:06     ` Marc-André Lureau
2025-08-27  7:35       ` Paolo Bonzini
2025-08-26 14:04 ` [RFC 08/18] rust: split "migration" crate marcandre.lureau
2025-08-26 14:04 ` [RFC 09/18] rust: split "bql" crate marcandre.lureau
2025-08-26 14:04 ` [RFC 10/18] rust: split "qom" crate marcandre.lureau
2025-08-27  6:55   ` Zhao Liu
2025-08-27  8:57     ` Marc-André Lureau
2025-08-27  9:49       ` Paolo Bonzini
2025-08-26 14:04 ` [RFC 11/18] rust: split "chardev" crate marcandre.lureau
2025-08-26 14:04 ` [RFC 12/18] rust: split "system" crate marcandre.lureau
2025-08-26 14:04 ` [RFC 13/18] rust: split "hwcore" crate marcandre.lureau
2025-08-26 14:04 ` [RFC 14/18] rust: rename qemu_api_macros -> qemu_macros marcandre.lureau
2025-08-26 14:53   ` Paolo Bonzini
2025-08-26 20:30     ` Marc-André Lureau
2025-08-26 14:04 ` [RFC 15/18] rust/hpet: drop now unneeded qemu_api dep marcandre.lureau
2025-08-26 14:04 ` [RFC 16/18] rust/pl011: drop dependency on qemu_api marcandre.lureau
2025-08-26 14:04 ` [RFC 17/18] rust: repurpose qemu_api -> tests marcandre.lureau
2025-08-26 14:04 ` [RFC 18/18] docs: update rust.rst marcandre.lureau
2025-08-26 14:44 ` [RFC 00/18] rust: split qemu-api Paolo Bonzini
2025-08-26 14:55   ` Manos Pitsidianakis
2025-08-26 15:22     ` Marc-André Lureau
2025-08-26 15:33     ` Paolo Bonzini
2025-08-26 15:15   ` Marc-André Lureau
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).