qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/22] rust: split qemu-api
@ 2025-08-27 10:41 marcandre.lureau
  2025-08-27 10:41 ` [PATCH 01/22] docs/rust: update msrv marcandre.lureau
                   ` (22 more replies)
  0 siblings, 23 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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.

rfc->v1:
- added "rust: make build.rs generic over various ./rust/projects" patch, and
  link to common build.rs to avoid duplicated copies
- moved "errno" module to the "common" crate
- add/move the prelude for "qom"
- re-export macros in their corresponding crates
- drop the qom test for now
- added "docs/rust: update msrv" patch
- more misc doc changes
- add some missing spdx tags, and collect a r-b

Marc-André Lureau (22):
  docs/rust: update msrv
  rust: remove unused global qemu "allocator"
  rust: add workspace authors
  rust: make build.rs generic over various ./rust/projects
  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
  rust: re-export qemu_macros internal helper in "bits"
  rust: re-export qemu macros from common/qom/hwcore
  docs: update rust.rst

 MAINTAINERS                                   |  12 +-
 docs/devel/rust.rst                           |  65 ++--
 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                               | 123 ++++++-
 rust/Cargo.toml                               |  20 +-
 rust/bits/Cargo.toml                          |   2 +-
 rust/bits/meson.build                         |   2 +-
 rust/bits/src/lib.rs                          |   7 +-
 rust/{qemu-api => bql}/Cargo.toml             |  13 +-
 rust/bql/build.rs                             |   1 +
 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                       |  23 ++
 rust/chardev/build.rs                         |   1 +
 rust/chardev/meson.build                      |  54 +++
 rust/chardev/src/bindings.rs                  |  36 ++
 rust/{qemu-api => chardev}/src/chardev.rs     |  23 +-
 rust/chardev/src/lib.rs                       |   6 +
 rust/common/Cargo.toml                        |  20 ++
 rust/common/meson.build                       |  34 ++
 rust/{qemu-api => common}/src/assertions.rs   |  16 +-
 rust/{qemu-api => common}/src/bitops.rs       |   1 -
 rust/{qemu-api => common}/src/callbacks.rs    |  12 +-
 rust/{qemu-api => common}/src/errno.rs        |  11 +-
 rust/common/src/lib.rs                        |  22 ++
 rust/common/src/opaque.rs                     | 238 +++++++++++++
 rust/{qemu-api => common}/src/uninit.rs       |   2 +-
 rust/common/src/zeroable.rs                   |  18 +
 rust/hw/char/pl011/Cargo.toml                 |  10 +-
 rust/hw/char/pl011/build.rs                   |   1 +
 rust/hw/char/pl011/meson.build                |  38 +-
 rust/hw/char/pl011/src/bindings.rs            |  27 ++
 rust/hw/char/pl011/src/device.rs              |  46 ++-
 rust/hw/char/pl011/src/lib.rs                 |   1 +
 rust/hw/char/pl011/src/registers.rs           |   4 +-
 rust/hw/core/Cargo.toml                       |  25 ++
 rust/hw/core/build.rs                         |   1 +
 rust/{qemu-api => hw/core}/meson.build        |  86 ++---
 rust/hw/core/src/bindings.rs                  |  41 +++
 rust/{qemu-api => hw/core}/src/irq.rs         |  16 +-
 rust/hw/core/src/lib.rs                       |  14 +
 rust/{qemu-api => hw/core}/src/qdev.rs        |  74 +++-
 rust/{qemu-api => hw/core}/src/sysbus.rs      |  26 +-
 rust/{qemu-api => hw/core}/tests/tests.rs     |  26 +-
 rust/hw/timer/hpet/Cargo.toml                 |   9 +-
 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                              |  13 +-
 rust/migration/Cargo.toml                     |  20 ++
 rust/migration/build.rs                       |   1 +
 rust/migration/meson.build                    |  57 +++
 rust/migration/src/bindings.rs                |  48 +++
 rust/migration/src/lib.rs                     |   6 +
 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                             |   1 +
 rust/qom/meson.build                          |  47 +++
 rust/qom/src/bindings.rs                      |  25 ++
 rust/qom/src/lib.rs                           |  10 +
 rust/qom/src/prelude.rs                       |  12 +
 rust/{qemu-api => qom}/src/qom.rs             |  27 +-
 rust/system/Cargo.toml                        |  21 ++
 rust/system/build.rs                          |   1 +
 rust/system/meson.build                       |  56 +++
 rust/{qemu-api => system}/src/bindings.rs     |  33 +-
 rust/system/src/lib.rs                        |   6 +
 rust/{qemu-api => system}/src/memory.rs       |  18 +-
 rust/tests/Cargo.toml                         |  29 ++
 rust/tests/meson.build                        |  14 +
 .../tests/vmstate_tests.rs                    |  18 +-
 rust/util/Cargo.toml                          |  22 ++
 rust/{qemu-api => util}/build.rs              |  10 +-
 rust/util/meson.build                         |  60 ++++
 rust/util/src/bindings.rs                     |  25 ++
 rust/{qemu-api => util}/src/error.rs          |   6 +-
 rust/util/src/lib.rs                          |   9 +
 rust/{qemu-api => util}/src/log.rs            |  16 +-
 rust/{qemu-api => util}/src/module.rs         |   2 +-
 rust/{qemu-api => util}/src/timer.rs          |  12 +-
 100 files changed, 2039 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%)
 create mode 120000 rust/bql/build.rs
 create mode 100644 rust/bql/meson.build
 create mode 100644 rust/bql/src/bindings.rs
 rename rust/{qemu-api => bql}/src/cell.rs (70%)
 create mode 100644 rust/bql/src/lib.rs
 create mode 100644 rust/chardev/Cargo.toml
 create mode 120000 rust/chardev/build.rs
 create mode 100644 rust/chardev/meson.build
 create mode 100644 rust/chardev/src/bindings.rs
 rename rust/{qemu-api => chardev}/src/chardev.rs (95%)
 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%)
 rename rust/{qemu-api => common}/src/errno.rs (98%)
 create mode 100644 rust/common/src/lib.rs
 create mode 100644 rust/common/src/opaque.rs
 rename rust/{qemu-api => common}/src/uninit.rs (98%)
 create mode 100644 rust/common/src/zeroable.rs
 create mode 120000 rust/hw/char/pl011/build.rs
 create mode 100644 rust/hw/char/pl011/src/bindings.rs
 create mode 100644 rust/hw/core/Cargo.toml
 create mode 120000 rust/hw/core/build.rs
 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 (93%)
 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 (89%)
 rename rust/{qemu-api => hw/core}/tests/tests.rs (89%)
 create mode 100644 rust/migration/Cargo.toml
 create mode 120000 rust/migration/build.rs
 create mode 100644 rust/migration/meson.build
 create mode 100644 rust/migration/src/bindings.rs
 create mode 100644 rust/migration/src/lib.rs
 rename rust/{qemu-api => migration}/src/vmstate.rs (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 120000 rust/qom/build.rs
 create mode 100644 rust/qom/meson.build
 create mode 100644 rust/qom/src/bindings.rs
 create mode 100644 rust/qom/src/lib.rs
 create mode 100644 rust/qom/src/prelude.rs
 rename rust/{qemu-api => qom}/src/qom.rs (98%)
 create mode 100644 rust/system/Cargo.toml
 create mode 120000 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
 rename rust/{qemu-api => util}/build.rs (81%)
 create mode 100644 rust/util/meson.build
 create mode 100644 rust/util/src/bindings.rs
 rename rust/{qemu-api => util}/src/error.rs (98%)
 create mode 100644 rust/util/src/lib.rs
 rename rust/{qemu-api => util}/src/log.rs (93%)
 rename rust/{qemu-api => util}/src/module.rs (97%)
 rename rust/{qemu-api => util}/src/timer.rs (93%)

-- 
2.50.1



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

* [PATCH 01/22] docs/rust: update msrv
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 14:37   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 02/22] rust: remove unused global qemu "allocator" marcandre.lureau
                   ` (21 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, Manos Pitsidianakis

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

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 docs/devel/rust.rst | 2 +-
 rust/Cargo.toml     | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index b6737536c6..f52da6f97e 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -1,4 +1,4 @@
-.. |msrv| replace:: 1.63.0
+.. |msrv| replace:: 1.77.0
 
 Rust in QEMU
 ============
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 0868e1b426..56cfe07791 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -13,6 +13,7 @@ edition = "2021"
 homepage = "https://www.qemu.org"
 license = "GPL-2.0-or-later"
 repository = "https://gitlab.com/qemu-project/qemu/"
+# don't forget to update docs/devel/rust.rst msrv
 rust-version = "1.77.0"
 
 [workspace.lints.rust]
-- 
2.50.1



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

* [PATCH 02/22] rust: remove unused global qemu "allocator"
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
  2025-08-27 10:41 ` [PATCH 01/22] docs/rust: update msrv marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 14:55   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 03/22] rust: add workspace authors marcandre.lureau
                   ` (20 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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 56cfe07791..8b878775ce 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -18,7 +18,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] 38+ messages in thread

* [PATCH 03/22] rust: add workspace authors
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
  2025-08-27 10:41 ` [PATCH 01/22] docs/rust: update msrv marcandre.lureau
  2025-08-27 10:41 ` [PATCH 02/22] rust: remove unused global qemu "allocator" marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 14:56   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 04/22] rust: make build.rs generic over various ./rust/projects marcandre.lureau
                   ` (19 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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 8b878775ce..15c80fdbf4 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -15,6 +15,7 @@ license = "GPL-2.0-or-later"
 repository = "https://gitlab.com/qemu-project/qemu/"
 # don't forget to update docs/devel/rust.rst msrv
 rust-version = "1.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] 38+ messages in thread

* [PATCH 04/22] rust: make build.rs generic over various ./rust/projects
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (2 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 03/22] rust: add workspace authors marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 15:06   ` Zhao Liu
  2025-08-28  7:14   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 05/22] rust: split Rust-only "common" crate marcandre.lureau
                   ` (18 subsequent siblings)
  22 siblings, 2 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, Manos Pitsidianakis

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

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

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 rust/qemu-api/build.rs | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

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



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

* [PATCH 05/22] rust: split Rust-only "common" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (3 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 04/22] rust: make build.rs generic over various ./rust/projects marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 15:20   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 06/22] rust: split "util" crate marcandre.lureau
                   ` (17 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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                             |  10 +
 rust/Cargo.toml                             |   4 +
 rust/common/Cargo.toml                      |  19 ++
 rust/common/meson.build                     |  34 +++
 rust/{qemu-api => common}/src/assertions.rs |  16 +-
 rust/{qemu-api => common}/src/bitops.rs     |   1 -
 rust/{qemu-api => common}/src/callbacks.rs  |  12 +-
 rust/{qemu-api => common}/src/errno.rs      |  11 +-
 rust/common/src/lib.rs                      |  20 ++
 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                    |   3 +-
 rust/qemu-api/meson.build                   |  10 +-
 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/error.rs                  |   3 +-
 rust/qemu-api/src/irq.rs                    |   3 +-
 rust/qemu-api/src/lib.rs                    |   6 -
 rust/qemu-api/src/log.rs                    |   4 +-
 rust/qemu-api/src/memory.rs                 |   5 +-
 rust/qemu-api/src/prelude.rs                |   6 +-
 rust/qemu-api/src/qdev.rs                   |  10 +-
 rust/qemu-api/src/qom.rs                    |   3 +-
 rust/qemu-api/src/sysbus.rs                 |   3 +-
 rust/qemu-api/src/timer.rs                  |   8 +-
 rust/qemu-api/src/vmstate.rs                |  24 +-
 rust/qemu-api/src/zeroable.rs               |  37 ---
 rust/qemu-api/tests/tests.rs                |   2 +-
 rust/qemu-api/tests/vmstate_tests.rs        |   4 +-
 42 files changed, 451 insertions(+), 362 deletions(-)
 create mode 100644 rust/common/Cargo.toml
 create mode 100644 rust/common/meson.build
 rename rust/{qemu-api => common}/src/assertions.rs (92%)
 rename rust/{qemu-api => common}/src/bitops.rs (98%)
 rename rust/{qemu-api => common}/src/callbacks.rs (97%)
 rename rust/{qemu-api => common}/src/errno.rs (98%)
 create mode 100644 rust/common/src/lib.rs
 create mode 100644 rust/common/src/opaque.rs
 rename rust/{qemu-api => common}/src/uninit.rs (98%)
 create mode 100644 rust/common/src/zeroable.rs
 delete mode 100644 rust/qemu-api/src/zeroable.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 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..c098301eaa 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -44,6 +44,13 @@ dependencies = [
  "qemu_api_macros",
 ]
 
+[[package]]
+name = "common"
+version = "0.1.0"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "either"
 version = "1.12.0"
@@ -63,6 +70,7 @@ dependencies = [
 name = "hpet"
 version = "0.1.0"
 dependencies = [
+ "common",
  "qemu_api",
  "qemu_api_macros",
 ]
@@ -89,6 +97,7 @@ dependencies = [
  "bilge",
  "bilge-impl",
  "bits",
+ "common",
  "qemu_api",
  "qemu_api_macros",
 ]
@@ -130,6 +139,7 @@ name = "qemu_api"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "common",
  "foreign",
  "libc",
  "qemu_api_macros",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 15c80fdbf4..49484913e7 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",
@@ -17,6 +18,9 @@ repository = "https://gitlab.com/qemu-project/qemu/"
 rust-version = "1.77.0"
 authors = ["The QEMU Project Developers <qemu-devel@nongnu.org>"]
 
+[workspace.dependencies]
+libc = "0.2.162"
+
 [workspace.lints.rust]
 unexpected_cfgs = { level = "deny", check-cfg = [
     'cfg(MESON)',
diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml
new file mode 100644
index 0000000000..5e106427e8
--- /dev/null
+++ b/rust/common/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "common"
+version = "0.1.0"
+description = "Rust common code for QEMU"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+libc.workspace = true
+
+[lints]
+workspace = true
diff --git a/rust/common/meson.build b/rust/common/meson.build
new file mode 100644
index 0000000000..230a967760
--- /dev/null
+++ b/rust/common/meson.build
@@ -0,0 +1,34 @@
+_common_cfg = run_command(rustc_args,
+  '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+  capture: true, check: true).stdout().strip().splitlines()
+
+_common_rs = static_library(
+  'common',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/assertions.rs',
+      'src/bitops.rs',
+      'src/callbacks.rs',
+      'src/errno.rs',
+      'src/opaque.rs',
+      'src/uninit.rs',
+      'src/zeroable.rs',
+    ],
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  rust_args: _common_cfg,
+  dependencies: [libc_rs],
+)
+
+common_rs = declare_dependency(link_with: [_common_rs])
+
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-common-doctests',
+     _common_rs,
+     protocol: 'rust',
+     dependencies: common_rs,
+     suite: ['doc', 'rust'])
diff --git a/rust/qemu-api/src/assertions.rs b/rust/common/src/assertions.rs
similarity index 92%
rename from rust/qemu-api/src/assertions.rs
rename to rust/common/src/assertions.rs
index 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/qemu-api/src/errno.rs b/rust/common/src/errno.rs
similarity index 98%
rename from rust/qemu-api/src/errno.rs
rename to rust/common/src/errno.rs
index 18d101448b..b48247b947 100644
--- a/rust/qemu-api/src/errno.rs
+++ b/rust/common/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 common::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 common::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 common::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 common::errno::into_neg_errno;
 /// # use std::io;
 /// let err: io::Result<u32> = Ok(0x8899_AABB);
 /// into_neg_errno(err) // panic
@@ -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/common/src/lib.rs b/rust/common/src/lib.rs
new file mode 100644
index 0000000000..25216503aa
--- /dev/null
+++ b/rust/common/src/lib.rs
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod assertions;
+
+pub mod bitops;
+
+pub mod callbacks;
+pub use callbacks::FnCall;
+
+pub mod errno;
+pub use errno::Errno;
+
+pub mod opaque;
+pub use opaque::{Opaque, Wrapper};
+
+pub mod uninit;
+pub use uninit::MaybeUninitField;
+
+pub mod zeroable;
+pub use zeroable::Zeroable;
diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs
new file mode 100644
index 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..feb049c608 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -14,9 +14,10 @@ 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"
+libc.workspace = true
 foreign = "~0.3.1"
 
 [features]
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 062009f161..fe34861a8e 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -52,13 +52,9 @@ _qemu_api_rs = static_library(
   structured_sources(
     [
       'src/lib.rs',
-      'src/assertions.rs',
       'src/bindings.rs',
-      'src/bitops.rs',
-      'src/callbacks.rs',
       'src/cell.rs',
       'src/chardev.rs',
-      'src/errno.rs',
       'src/error.rs',
       'src/irq.rs',
       'src/log.rs',
@@ -69,16 +65,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 +98,7 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [qemu_api]),
+        dependencies: [common_rs, qemu_api]),
     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..4d42306b6c 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -18,10 +18,11 @@
     slice,
 };
 
+use common::{callbacks::FnCall, errno, Opaque};
+
 use crate::{
     bindings,
-    callbacks::FnCall,
-    cell::{BqlRefMut, Opaque},
+    cell::{BqlRefCell, BqlRefMut},
     prelude::*,
 };
 
diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs
index 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..a256eeb44e 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -13,12 +13,8 @@
 #[rustfmt::skip]
 pub mod prelude;
 
-pub mod assertions;
-pub mod bitops;
-pub mod callbacks;
 pub mod cell;
 pub mod chardev;
-pub mod errno;
 pub mod error;
 pub mod irq;
 pub mod log;
@@ -28,8 +24,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/log.rs b/rust/qemu-api/src/log.rs
index a441b8c1f2..d07f6272dc 100644
--- a/rust/qemu-api/src/log.rs
+++ b/rust/qemu-api/src/log.rs
@@ -8,7 +8,9 @@
     ptr::NonNull,
 };
 
-use crate::{bindings, errno};
+use common::errno;
+
+use crate::bindings;
 
 #[repr(u32)]
 /// Represents specific error categories within QEMU's logging system.
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
index 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..dcfe71497f 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -4,13 +4,9 @@
 
 //! Commonly used traits and types for QEMU.
 
-pub use crate::bitops::IntegerExt;
-
 pub use crate::cell::BqlCell;
 pub use crate::cell::BqlRefCell;
 
-pub use crate::errno;
-
 pub use crate::log_mask_ln;
 
 pub use crate::qdev::DeviceMethods;
@@ -19,8 +15,8 @@
 pub use crate::qom::IsA;
 pub use crate::qom::Object;
 pub use crate::qom::ObjectCast;
-pub use crate::qom::ObjectDeref;
 pub use crate::qom::ObjectClassMethods;
+pub use crate::qom::ObjectDeref;
 pub use crate::qom::ObjectMethods;
 pub use crate::qom::ObjectType;
 
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 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] 38+ messages in thread

* [PATCH 06/22] rust: split "util" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (4 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 05/22] rust: split Rust-only "common" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-28  7:10   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 07/22] rust: move vmstate_clock!() to qdev module marcandre.lureau
                   ` (16 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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/common/src/errno.rs              |  2 +-
 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             | 11 ++---
 rust/qemu-api/src/bindings.rs         |  1 +
 rust/qemu-api/src/lib.rs              |  6 ---
 rust/qemu-api/src/prelude.rs          |  2 -
 rust/qemu-api/src/qdev.rs             |  4 +-
 rust/qemu-api/src/sysbus.rs           |  2 +-
 rust/qemu-api/src/vmstate.rs          |  2 +-
 rust/qemu-api/tests/tests.rs          |  3 +-
 rust/util/Cargo.toml                  | 23 ++++++++++
 rust/{qemu-api => util}/build.rs      |  0
 rust/util/meson.build                 | 60 +++++++++++++++++++++++++++
 rust/util/src/bindings.rs             | 25 +++++++++++
 rust/{qemu-api => util}/src/error.rs  |  7 ++--
 rust/util/src/lib.rs                  |  9 ++++
 rust/{qemu-api => util}/src/log.rs    | 12 +++---
 rust/{qemu-api => util}/src/module.rs |  2 +-
 rust/{qemu-api => util}/src/timer.rs  |  0
 33 files changed, 201 insertions(+), 51 deletions(-)
 create mode 100644 rust/util/wrapper.h
 create mode 100644 rust/util/Cargo.toml
 rename rust/{qemu-api => util}/build.rs (100%)
 create mode 100644 rust/util/meson.build
 create mode 100644 rust/util/src/bindings.rs
 rename rust/{qemu-api => util}/src/error.rs (98%)
 create mode 100644 rust/util/src/lib.rs
 rename rust/{qemu-api => util}/src/log.rs (93%)
 rename rust/{qemu-api => util}/src/module.rs (97%)
 rename rust/{qemu-api => util}/src/timer.rs (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 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 c098301eaa..0a0b2668a5 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -73,6 +73,7 @@ dependencies = [
  "common",
  "qemu_api",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -100,6 +101,7 @@ dependencies = [
  "common",
  "qemu_api",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -143,6 +145,7 @@ dependencies = [
  "foreign",
  "libc",
  "qemu_api_macros",
+ "util",
 ]
 
 [[package]]
@@ -180,6 +183,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 49484913e7..3574c882fc 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]
@@ -22,9 +23,7 @@ authors = ["The QEMU Project Developers <qemu-devel@nongnu.org>"]
 libc = "0.2.162"
 
 [workspace.lints.rust]
-unexpected_cfgs = { level = "deny", check-cfg = [
-    'cfg(MESON)',
-] }
+unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] }
 
 # Occasionally, we may need to silence warnings and clippy lints that
 # were only introduced in newer Rust compiler versions.  Do not croak
diff --git a/rust/common/src/errno.rs b/rust/common/src/errno.rs
index b48247b947..a7b23b4092 100644
--- a/rust/common/src/errno.rs
+++ b/rust/common/src/errno.rs
@@ -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/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 feb049c608..6d97a8ab05 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.workspace = true
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index fe34861a8e..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,16 +54,12 @@ _qemu_api_rs = static_library(
       'src/bindings.rs',
       'src/cell.rs',
       'src/chardev.rs',
-      'src/error.rs',
       'src/irq.rs',
-      'src/log.rs',
       'src/memory.rs',
-      'src/module.rs',
       'src/prelude.rs',
       'src/qdev.rs',
       'src/qom.rs',
       'src/sysbus.rs',
-      'src/timer.rs',
       'src/vmstate.rs',
     ],
     {'.' : _qemu_api_bindings_inc_rs},
@@ -72,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],
 )
 
@@ -98,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/lib.rs b/rust/qemu-api/src/lib.rs
index a256eeb44e..db81841a8f 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -15,15 +15,9 @@
 
 pub mod cell;
 pub mod chardev;
-pub mod error;
 pub mod irq;
-pub mod log;
 pub mod memory;
-pub mod module;
 pub mod qdev;
 pub mod qom;
 pub mod sysbus;
-pub mod timer;
 pub mod vmstate;
-
-pub use error::{Error, Result};
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index dcfe71497f..3d771481e4 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -7,8 +7,6 @@
 pub use crate::cell::BqlCell;
 pub use crate::cell::BqlRefCell;
 
-pub use crate::log_mask_ln;
-
 pub use crate::qdev::DeviceMethods;
 
 pub use crate::qom::InterfaceType;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 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/qemu-api/build.rs b/rust/util/build.rs
similarity index 100%
rename from rust/qemu-api/build.rs
rename to rust/util/build.rs
diff --git a/rust/util/meson.build b/rust/util/meson.build
new file mode 100644
index 0000000000..8f3ebf5415
--- /dev/null
+++ b/rust/util/meson.build
@@ -0,0 +1,60 @@
+_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/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/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..16c89b9517
--- /dev/null
+++ b/rust/util/src/lib.rs
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+pub mod error;
+pub mod log;
+pub mod module;
+pub mod timer;
+
+pub use error::{Error, Result};
diff --git a/rust/qemu-api/src/log.rs b/rust/util/src/log.rs
similarity index 93%
rename from rust/qemu-api/src/log.rs
rename to rust/util/src/log.rs
index d07f6272dc..af9a3e9123 100644
--- a/rust/qemu-api/src/log.rs
+++ b/rust/util/src/log.rs
@@ -49,7 +49,7 @@ impl LogGuard {
     /// # Examples
     ///
     /// ```
-    /// # use qemu_api::log::LogGuard;
+    /// # use util::log::LogGuard;
     /// # use std::io::Write;
     /// if let Some(mut log) = LogGuard::new() {
     ///     writeln!(log, "test");
@@ -116,7 +116,7 @@ fn drop(&mut self) {
 /// # Example
 ///
 /// ```
-/// use qemu_api::{log::Log, log_mask_ln};
+/// use util::{log::Log, log_mask_ln};
 ///
 /// let error_address = 0xbad;
 /// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range");
@@ -126,7 +126,7 @@ fn drop(&mut self) {
 /// trailing `,`:
 ///
 /// ```
-/// use qemu_api::{log::Log, log_mask_ln};
+/// use util::{log::Log, log_mask_ln};
 ///
 /// let error_address = 0xbad;
 /// log_mask_ln!(
@@ -139,12 +139,12 @@ fn drop(&mut self) {
 macro_rules! log_mask_ln {
     ($mask:expr, $fmt:tt $($args:tt)*) => {{
         // Type assertion to enforce type `Log` for $mask
-        let _: Log = $mask;
+        let _: $crate::log::Log = $mask;
 
         if unsafe {
-            (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0
+            ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0
         } {
-            _ = ::qemu_api::log::LogGuard::log_fmt(
+            _ = $crate::log::LogGuard::log_fmt(
                 format_args!("{}\n", format_args!($fmt $($args)*)));
         }
     }};
diff --git a/rust/qemu-api/src/module.rs b/rust/util/src/module.rs
similarity index 97%
rename from rust/qemu-api/src/module.rs
rename to rust/util/src/module.rs
index 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] 38+ messages in thread

* [PATCH 07/22] rust: move vmstate_clock!() to qdev module
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (5 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 06/22] rust: split "util" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-29  8:11   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 08/22] rust: move VMState handling to QOM module marcandre.lureau
                   ` (15 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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] 38+ messages in thread

* [PATCH 08/22] rust: move VMState handling to QOM module
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (6 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 07/22] rust: move vmstate_clock!() to qdev module marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-29  8:12   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 09/22] rust: move Cell vmstate impl marcandre.lureau
                   ` (14 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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] 38+ messages in thread

* [PATCH 09/22] rust: move Cell vmstate impl
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (7 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 08/22] rust: move VMState handling to QOM module marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-29  8:22   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 10/22] rust: split "migration" crate marcandre.lureau
                   ` (13 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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] 38+ messages in thread

* [PATCH 10/22] rust: split "migration" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (8 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 09/22] rust: move Cell vmstate impl marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-29  8:59   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 11/22] rust: split "bql" crate marcandre.lureau
                   ` (12 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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                     |   1 +
 rust/migration/meson.build                  |  57 +++++++++++
 rust/migration/src/bindings.rs              |  48 +++++++++
 rust/migration/src/lib.rs                   |   6 ++
 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, 298 insertions(+), 97 deletions(-)
 create mode 100644 rust/migration/wrapper.h
 create mode 100644 rust/migration/Cargo.toml
 create mode 120000 rust/migration/build.rs
 create mode 100644 rust/migration/meson.build
 create mode 100644 rust/migration/src/bindings.rs
 create mode 100644 rust/migration/src/lib.rs
 rename rust/{qemu-api => migration}/src/vmstate.rs (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 0a0b2668a5..b3a810a653 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -71,6 +71,7 @@ name = "hpet"
 version = "0.1.0"
 dependencies = [
  "common",
+ "migration",
  "qemu_api",
  "qemu_api_macros",
  "util",
@@ -91,6 +92,15 @@ version = "0.2.162"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
 
+[[package]]
+name = "migration"
+version = "0.1.0"
+dependencies = [
+ "common",
+ "qemu_api_macros",
+ "util",
+]
+
 [[package]]
 name = "pl011"
 version = "0.1.0"
@@ -99,6 +109,7 @@ dependencies = [
  "bilge-impl",
  "bits",
  "common",
+ "migration",
  "qemu_api",
  "qemu_api_macros",
  "util",
@@ -144,6 +155,7 @@ dependencies = [
  "common",
  "foreign",
  "libc",
+ "migration",
  "qemu_api_macros",
  "util",
 ]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 3574c882fc..0640a21580 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 120000
index 0000000000..71a3167885
--- /dev/null
+++ b/rust/migration/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/migration/meson.build b/rust/migration/meson.build
new file mode 100644
index 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..5f51dde440
--- /dev/null
+++ b/rust/migration/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+pub mod vmstate;
+pub use vmstate::*;
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/migration/src/vmstate.rs
similarity index 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 6d97a8ab05..4e44737843 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] 38+ messages in thread

* [PATCH 11/22] rust: split "bql" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (9 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 10/22] rust: split "migration" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-29  9:13   ` Zhao Liu
  2025-08-27 10:41 ` [PATCH 12/22] rust: split "qom" crate marcandre.lureau
                   ` (11 subsequent siblings)
  22 siblings, 1 reply; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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                    |  1 +
 rust/bql/meson.build                 | 52 ++++++++++++++++
 rust/bql/src/bindings.rs             | 25 ++++++++
 rust/{qemu-api => bql}/src/cell.rs   | 92 ++++++++++------------------
 rust/bql/src/lib.rs                  | 29 +++++++++
 rust/common/src/opaque.rs            |  4 +-
 rust/hw/char/pl011/Cargo.toml        |  1 +
 rust/hw/char/pl011/meson.build       |  1 +
 rust/hw/char/pl011/src/device.rs     |  1 +
 rust/hw/timer/hpet/Cargo.toml        |  1 +
 rust/hw/timer/hpet/meson.build       |  1 +
 rust/hw/timer/hpet/src/device.rs     |  2 +-
 rust/hw/timer/hpet/src/fw_cfg.rs     |  5 +-
 rust/meson.build                     |  1 +
 rust/migration/src/vmstate.rs        |  4 +-
 rust/qemu-api/Cargo.toml             |  5 +-
 rust/qemu-api/meson.build            |  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, 244 insertions(+), 111 deletions(-)
 create mode 100644 rust/bql/wrapper.h
 create mode 100644 rust/bql/Cargo.toml
 create mode 120000 rust/bql/build.rs
 create mode 100644 rust/bql/meson.build
 create mode 100644 rust/bql/src/bindings.rs
 rename rust/{qemu-api => bql}/src/cell.rs (92%)
 create mode 100644 rust/bql/src/lib.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 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 b3a810a653..2a73a0dd45 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -44,6 +44,13 @@ dependencies = [
  "qemu_api_macros",
 ]
 
+[[package]]
+name = "bql"
+version = "0.1.0"
+dependencies = [
+ "migration",
+]
+
 [[package]]
 name = "common"
 version = "0.1.0"
@@ -70,6 +77,7 @@ dependencies = [
 name = "hpet"
 version = "0.1.0"
 dependencies = [
+ "bql",
  "common",
  "migration",
  "qemu_api",
@@ -108,6 +116,7 @@ dependencies = [
  "bilge",
  "bilge-impl",
  "bits",
+ "bql",
  "common",
  "migration",
  "qemu_api",
@@ -152,6 +161,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 0640a21580..73cb54f60b 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 120000
index 0000000000..71a3167885
--- /dev/null
+++ b/rust/bql/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/bql/meson.build b/rust/bql/meson.build
new file mode 100644
index 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 4e44737843..8ba19baae8 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -17,14 +17,11 @@ 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.workspace = true
 foreign = "~0.3.1"
 
-[features]
-default = ["debug_cell"]
-debug_cell = []
-
 [lints]
 workspace = true
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 4d42306b6c..c241b30b00 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -18,13 +18,10 @@
     slice,
 };
 
+use bql::{BqlRefCell, BqlRefMut};
 use common::{callbacks::FnCall, errno, Opaque};
 
-use crate::{
-    bindings,
-    cell::{BqlRefCell, BqlRefMut},
-    prelude::*,
-};
+use crate::{bindings, prelude::*};
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
@@ -44,13 +41,15 @@ pub struct CharBackend {
     _pin: PhantomPinned,
 }
 
-impl Write for BqlRefMut<'_, bindings::CharBackend> {
+pub struct CharBackendMut<'a>(BqlRefMut<'a, bindings::CharBackend>);
+
+impl Write for CharBackendMut<'_> {
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
 
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        let chr: &mut bindings::CharBackend = self;
+        let chr: &mut bindings::CharBackend = &mut self.0;
 
         let len = buf.len().try_into().unwrap();
         let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -58,7 +57,7 @@ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
     }
 
     fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
-        let chr: &mut bindings::CharBackend = self;
+        let chr: &mut bindings::CharBackend = &mut self.0;
 
         let len = buf.len().try_into().unwrap();
         let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -198,7 +197,7 @@ pub fn accept_input(&self) {
     /// the big QEMU lock while the character device is borrowed, as
     /// that might cause C code to write to the character device.
     pub fn borrow_mut(&self) -> impl Write + '_ {
-        self.inner.borrow_mut()
+        CharBackendMut(self.inner.borrow_mut())
     }
 
     /// Send a continuous stream of zero bits on the line if `enabled` is
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index 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] 38+ messages in thread

* [PATCH 12/22] rust: split "qom" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (10 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 11/22] rust: split "bql" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 13/22] rust: split "chardev" crate marcandre.lureau
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, Manos Pitsidianakis

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

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

diff --git a/MAINTAINERS b/MAINTAINERS
index 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 2a73a0dd45..1dbaad3343 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -82,6 +82,7 @@ dependencies = [
  "migration",
  "qemu_api",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -121,6 +122,7 @@ dependencies = [
  "migration",
  "qemu_api",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -167,6 +169,7 @@ dependencies = [
  "libc",
  "migration",
  "qemu_api_macros",
+ "qom",
  "util",
 ]
 
@@ -179,6 +182,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 73cb54f60b..1a081ce8f7 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..f595b950bf 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -21,10 +21,10 @@
     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::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
 use util::{log::Log, log_mask_ln};
 
 use crate::registers::{self, Interrupt, RegisterOffset};
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 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..6d7639b5b9 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -27,10 +27,9 @@
     },
     prelude::*,
     qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
-    qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
-    qom_isa,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
 };
+use qom::{prelude::*, ObjectImpl, ParentField, ParentInit};
 use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
 use crate::fw_cfg::HPETFwConfig;
diff --git a/rust/meson.build b/rust/meson.build
index 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 8ba19baae8..1499e0866d 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.workspace = true
 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 c241b30b00..fcf3778ffb 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -20,8 +20,9 @@
 
 use bql::{BqlRefCell, BqlRefMut};
 use common::{callbacks::FnCall, errno, Opaque};
+use qom::prelude::*;
 
-use crate::{bindings, prelude::*};
+use crate::bindings;
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index 3063fbe97a..fead2bbe8e 100644
--- a/rust/qemu-api/src/irq.rs
+++ b/rust/qemu-api/src/irq.rs
@@ -12,12 +12,9 @@
 
 use bql::BqlCell;
 use common::Opaque;
+use qom::{prelude::*, ObjectClass};
 
-use crate::{
-    bindings::{self, qemu_set_irq},
-    prelude::*,
-    qom::ObjectClass,
-};
+use crate::bindings::{self, qemu_set_irq};
 
 /// An opaque wrapper around [`bindings::IRQState`].
 #[repr(transparent)]
@@ -36,7 +33,7 @@
 ///
 /// Interrupts are implemented as a pointer to the interrupt "sink", which has
 /// type [`IRQState`].  A device exposes its source as a QOM link property using
-/// a function such as [`SysBusDeviceMethods::init_irq`], and
+/// a function such as [`crate::sysbus::SysBusDeviceMethods::init_irq`], and
 /// initially leaves the pointer to a NULL value, representing an unconnected
 /// interrupt. To connect it, whoever creates the device fills the pointer with
 /// the sink's `IRQState *`, for example using `sysbus_connect_irq`.  Because
@@ -114,4 +111,5 @@ unsafe impl ObjectType for IRQState {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) };
 }
+
 qom_isa!(IRQState: Object);
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 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..ecbbd9b604 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/qemu-api/src/memory.rs
@@ -11,11 +11,9 @@
 
 pub use bindings::{hwaddr, MemTxAttrs};
 use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque};
+use qom::prelude::*;
 
-use crate::{
-    bindings::{self, device_endian, memory_region_init_io},
-    prelude::*,
-};
+use crate::bindings::{self, device_endian, memory_region_init_io};
 
 pub struct MemoryRegionOps<T>(
     bindings::MemoryRegionOps,
@@ -186,6 +184,7 @@ unsafe impl ObjectType for MemoryRegion {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) };
 }
+
 qom_isa!(MemoryRegion: Object);
 
 /// A special `MemTxAttrs` constant, used to indicate that no memory
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 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..91c4edf5a3 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -12,14 +12,13 @@
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use common::{callbacks::FnCall, Opaque};
 use migration::vmstate::VMStateDescription;
+use qom::{prelude::*, ObjectClass, ObjectImpl, 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 +163,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,6 +247,7 @@ unsafe impl ObjectType for DeviceState {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
 }
+
 qom_isa!(DeviceState: Object);
 
 /// Initialization methods take a [`ParentInit`] and can be called as
@@ -406,6 +410,7 @@ unsafe impl ObjectType for Clock {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
 }
+
 qom_isa!(Clock: Object);
 
 #[doc(alias = "VMSTATE_CLOCK")]
@@ -420,7 +425,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..59596886e5 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -8,14 +8,13 @@
 
 pub use bindings::SysBusDeviceClass;
 use common::Opaque;
+use qom::{prelude::*, Owned};
 
 use crate::{
     bindings,
     irq::{IRQState, InterruptSource},
     memory::MemoryRegion,
-    prelude::*,
-    qdev::{DeviceImpl, DeviceState},
-    qom::Owned,
+    qdev::{DeviceClassExt, DeviceImpl, DeviceState},
 };
 
 /// A safe wrapper around [`bindings::SysBusDevice`].
@@ -31,6 +30,7 @@ unsafe impl ObjectType for SysBusDevice {
     const TYPE_NAME: &'static CStr =
         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
 }
+
 qom_isa!(SysBusDevice: DeviceState, Object);
 
 // TODO: add virtual methods
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index bc4bd320ce..d8d07f7929 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -10,11 +10,10 @@
 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::{prelude::*, ObjectImpl, ParentField};
 use util::bindings::{module_call_init, module_init_type};
 
 mod vmstate_tests;
diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml
new file mode 100644
index 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 120000
index 0000000000..71a3167885
--- /dev/null
+++ b/rust/qom/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/qom/meson.build b/rust/qom/meson.build
new file mode 100644
index 0000000000..8492868634
--- /dev/null
+++ b/rust/qom/meson.build
@@ -0,0 +1,47 @@
+_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/prelude.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'])
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..35ddc51bb0
--- /dev/null
+++ b/rust/qom/src/lib.rs
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+pub mod prelude;
+
+mod qom;
+pub use qom::*;
diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs
new file mode 100644
index 0000000000..00a6095977
--- /dev/null
+++ b/rust/qom/src/prelude.rs
@@ -0,0 +1,12 @@
+//! Traits and essential types intended for blanket imports.
+
+pub use crate::qom::InterfaceType;
+pub use crate::qom::IsA;
+pub use crate::qom::Object;
+pub use crate::qom::ObjectCast;
+pub use crate::qom::ObjectClassMethods;
+pub use crate::qom::ObjectDeref;
+pub use crate::qom::ObjectMethods;
+pub use crate::qom::ObjectType;
+
+pub use crate::qom_isa;
diff --git a/rust/qemu-api/src/qom.rs b/rust/qom/src/qom.rs
similarity index 99%
rename from rust/qemu-api/src/qom.rs
rename to rust/qom/src/qom.rs
index 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 {
-- 
2.50.1



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

* [PATCH 13/22] rust: split "chardev" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (11 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 12/22] rust: split "qom" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 14/22] rust: split "system" crate marcandre.lureau
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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                     |  1 +
 rust/chardev/meson.build                  | 54 +++++++++++++++++++++++
 rust/chardev/src/bindings.rs              | 36 +++++++++++++++
 rust/{qemu-api => chardev}/src/chardev.rs |  0
 rust/chardev/src/lib.rs                   |  6 +++
 rust/hw/char/pl011/Cargo.toml             |  1 +
 rust/hw/char/pl011/meson.build            |  1 +
 rust/hw/char/pl011/src/device.rs          |  2 +-
 rust/meson.build                          |  1 +
 rust/qemu-api/Cargo.toml                  |  1 +
 rust/qemu-api/meson.build                 |  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, 174 insertions(+), 17 deletions(-)
 create mode 100644 rust/chardev/wrapper.h
 create mode 100644 rust/chardev/Cargo.toml
 create mode 120000 rust/chardev/build.rs
 create mode 100644 rust/chardev/meson.build
 create mode 100644 rust/chardev/src/bindings.rs
 rename rust/{qemu-api => chardev}/src/chardev.rs (100%)
 create mode 100644 rust/chardev/src/lib.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 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 1dbaad3343..8b52843e6c 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -51,6 +51,18 @@ dependencies = [
  "migration",
 ]
 
+[[package]]
+name = "chardev"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "common",
+ "migration",
+ "qemu_api_macros",
+ "qom",
+ "util",
+]
+
 [[package]]
 name = "common"
 version = "0.1.0"
@@ -118,6 +130,7 @@ dependencies = [
  "bilge-impl",
  "bits",
  "bql",
+ "chardev",
  "common",
  "migration",
  "qemu_api",
@@ -164,6 +177,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 120000
index 0000000000..71a3167885
--- /dev/null
+++ b/rust/chardev/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build
new file mode 100644
index 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..2e549f99d9
--- /dev/null
+++ b/rust/chardev/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+mod chardev;
+pub use chardev::*;
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 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 f595b950bf..8aaad6481c 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 1499e0866d..b5cc2b7d41 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 91c4edf5a3..3010a9fba9 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::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit};
@@ -17,7 +18,6 @@
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
-    chardev::Chardev,
     irq::InterruptSource,
 };
 
-- 
2.50.1



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

* [PATCH 14/22] rust: split "system" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (12 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 13/22] rust: split "chardev" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 15/22] rust: split "hwcore" crate marcandre.lureau
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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                    |  1 +
 rust/system/meson.build                 | 57 +++++++++++++++++++++++++
 rust/system/src/bindings.rs             | 41 ++++++++++++++++++
 rust/system/src/lib.rs                  |  6 +++
 rust/{qemu-api => system}/src/memory.rs |  2 +-
 24 files changed, 192 insertions(+), 33 deletions(-)
 create mode 100644 rust/system/wrapper.h
 create mode 100644 rust/system/Cargo.toml
 create mode 120000 rust/system/build.rs
 create mode 100644 rust/system/meson.build
 create mode 100644 rust/system/src/bindings.rs
 create mode 100644 rust/system/src/lib.rs
 rename rust/{qemu-api => system}/src/memory.rs (99%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 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 8b52843e6c..1003dc05c3 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -95,6 +95,7 @@ dependencies = [
  "qemu_api",
  "qemu_api_macros",
  "qom",
+ "system",
  "util",
 ]
 
@@ -136,6 +137,7 @@ dependencies = [
  "qemu_api",
  "qemu_api_macros",
  "qom",
+ "system",
  "util",
 ]
 
@@ -184,6 +186,7 @@ dependencies = [
  "migration",
  "qemu_api_macros",
  "qom",
+ "system",
  "util",
 ]
 
@@ -227,6 +230,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 1a081ce8f7..ea25474ce4 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 8aaad6481c..b9c4f1c350 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -18,13 +18,13 @@
 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},
     vmstate_clock,
 };
 use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
+use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
 use util::{log::Log, log_mask_ln};
 
 use crate::registers::{self, Interrupt, RegisterOffset};
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 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 6d7639b5b9..184315a696 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::{prelude::*, ObjectImpl, ParentField, ParentInit};
+use system::{
+    bindings::{address_space_memory, address_space_stl_le, hwaddr},
+    MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
+};
 use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
 
 use crate::fw_cfg::HPETFwConfig;
diff --git a/rust/meson.build b/rust/meson.build
index 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 b5cc2b7d41..cc1bfec44a 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.workspace = true
 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 59596886e5..81014f9ebf 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -9,11 +9,11 @@
 pub use bindings::SysBusDeviceClass;
 use common::Opaque;
 use qom::{prelude::*, Owned};
+use system::MemoryRegion;
 
 use crate::{
     bindings,
     irq::{IRQState, InterruptSource},
-    memory::MemoryRegion,
     qdev::{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 120000
index 0000000000..71a3167885
--- /dev/null
+++ b/rust/system/build.rs
@@ -0,0 +1 @@
+../util/build.rs
\ No newline at end of file
diff --git a/rust/system/meson.build b/rust/system/meson.build
new file mode 100644
index 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..aafe9a866c
--- /dev/null
+++ b/rust/system/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+mod memory;
+pub use memory::*;
diff --git a/rust/qemu-api/src/memory.rs b/rust/system/src/memory.rs
similarity index 99%
rename from rust/qemu-api/src/memory.rs
rename to rust/system/src/memory.rs
index ecbbd9b604..29568ed767 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/system/src/memory.rs
@@ -9,11 +9,11 @@
     marker::PhantomData,
 };
 
-pub use bindings::{hwaddr, MemTxAttrs};
 use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque};
 use qom::prelude::*;
 
 use crate::bindings::{self, device_endian, memory_region_init_io};
+pub use crate::bindings::{hwaddr, MemTxAttrs};
 
 pub struct MemoryRegionOps<T>(
     bindings::MemoryRegionOps,
-- 
2.50.1



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

* [PATCH 15/22] rust: split "hwcore" crate
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (13 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 14/22] rust: split "system" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 16/22] rust: rename qemu_api_macros -> qemu_macros marcandre.lureau
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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                     |  1 +
 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                   | 14 ++++
 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 | 12 +--
 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, 261 insertions(+), 73 deletions(-)
 create mode 100644 rust/hw/core/wrapper.h
 create mode 100644 rust/hw/core/Cargo.toml
 create mode 120000 rust/hw/core/build.rs
 create mode 100644 rust/hw/core/meson.build
 create mode 100644 rust/hw/core/src/bindings.rs
 rename rust/{qemu-api => hw/core}/src/irq.rs (100%)
 create mode 100644 rust/hw/core/src/lib.rs
 rename rust/{qemu-api => hw/core}/src/qdev.rs (98%)
 rename rust/{qemu-api => hw/core}/src/sysbus.rs (100%)
 rename rust/{qemu-api => hw/core}/tests/tests.rs (94%)

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 1003dc05c3..d454692c91 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -91,6 +91,7 @@ version = "0.1.0"
 dependencies = [
  "bql",
  "common",
+ "hwcore",
  "migration",
  "qemu_api",
  "qemu_api_macros",
@@ -99,6 +100,20 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "hwcore"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "chardev",
+ "common",
+ "migration",
+ "qemu_api_macros",
+ "qom",
+ "system",
+ "util",
+]
+
 [[package]]
 name = "itertools"
 version = "0.11.0"
@@ -133,6 +148,7 @@ dependencies = [
  "bql",
  "chardev",
  "common",
+ "hwcore",
  "migration",
  "qemu_api",
  "qemu_api_macros",
@@ -182,6 +198,7 @@ dependencies = [
  "chardev",
  "common",
  "foreign",
+ "hwcore",
  "libc",
  "migration",
  "qemu_api_macros",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index ea25474ce4..11280ad524 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 b9c4f1c350..ffc805d4f6 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::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
 use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
 use util::{log::Log, log_mask_ln};
@@ -783,16 +781,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 120000
index 0000000000..2a79ee31b8
--- /dev/null
+++ b/rust/hw/core/build.rs
@@ -0,0 +1 @@
+../../util/build.rs
\ No newline at end of file
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
new file mode 100644
index 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..c5588d9bc2
--- /dev/null
+++ b/rust/hw/core/src/lib.rs
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub use qom;
+
+pub mod bindings;
+
+mod irq;
+pub use irq::*;
+
+mod qdev;
+pub use qdev::*;
+
+mod sysbus;
+pub use sysbus::*;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/hw/core/src/qdev.rs
similarity index 98%
rename from rust/qemu-api/src/qdev.rs
rename to rust/hw/core/src/qdev.rs
index 3010a9fba9..359eee7ec9 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -9,13 +9,13 @@
     ptr::NonNull,
 };
 
-pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use chardev::Chardev;
 use common::{callbacks::FnCall, Opaque};
 use migration::vmstate::VMStateDescription;
 use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit};
 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,
@@ -425,11 +425,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 94%
rename from rust/qemu-api/tests/tests.rs
rename to rust/hw/core/tests/tests.rs
index d8d07f7929..421b35d98d 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -6,18 +6,14 @@
 
 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::{prelude::*, ObjectImpl, 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(),
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 184315a696..b8e6b1fe31 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::{prelude::*, ObjectImpl, 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 cc1bfec44a..f8b80e5771 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] 38+ messages in thread

* [PATCH 16/22] rust: rename qemu_api_macros -> qemu_macros
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (14 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 15/22] rust: split "hwcore" crate marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 17/22] rust/hpet: drop now unneeded qemu_api dep marcandre.lureau
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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/system/Cargo.toml                        |  2 +-
 rust/system/meson.build                       |  4 ++--
 rust/system/src/memory.rs                     |  2 +-
 rust/util/Cargo.toml                          |  2 +-
 rust/util/meson.build                         |  2 +-
 rust/util/src/timer.rs                        |  4 ++--
 41 files changed, 68 insertions(+), 68 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 d454692c91..91f137d75c 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -41,7 +41,7 @@ dependencies = [
 name = "bits"
 version = "0.1.0"
 dependencies = [
- "qemu_api_macros",
+ "qemu_macros",
 ]
 
 [[package]]
@@ -58,7 +58,7 @@ dependencies = [
  "bql",
  "common",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "util",
 ]
@@ -94,7 +94,7 @@ dependencies = [
  "hwcore",
  "migration",
  "qemu_api",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -108,7 +108,7 @@ dependencies = [
  "chardev",
  "common",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -134,7 +134,7 @@ name = "migration"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_api_macros",
+ "qemu_macros",
  "util",
 ]
 
@@ -151,7 +151,7 @@ dependencies = [
  "hwcore",
  "migration",
  "qemu_api",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "system",
  "util",
@@ -201,14 +201,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",
@@ -223,7 +223,7 @@ dependencies = [
  "bql",
  "common",
  "migration",
- "qemu_api_macros",
+ "qemu_macros",
  "util",
 ]
 
@@ -252,7 +252,7 @@ name = "system"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_api_macros",
+ "qemu_macros",
  "qom",
  "util",
 ]
@@ -271,7 +271,7 @@ dependencies = [
  "common",
  "foreign",
  "libc",
- "qemu_api_macros",
+ "qemu_macros",
 ]
 
 [[package]]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 11280ad524..4a97510676 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 fcf3778ffb..0e0a8f5556 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 ffc805d4f6..b7c1cb68a2 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -103,7 +103,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>,
@@ -688,7 +688,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 fead2bbe8e..d8d964cad2 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 359eee7ec9..69d8a7f4c1 100644
--- a/rust/hw/core/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -23,7 +23,7 @@
 
 /// A safe wrapper around [`bindings::Clock`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct Clock(Opaque<bindings::Clock>);
 
 unsafe impl Send for Clock {}
@@ -31,7 +31,7 @@ unsafe impl Sync for Clock {}
 
 /// A safe wrapper around [`bindings::DeviceState`].
 #[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, qemu_macros::Wrapper)]
 pub struct DeviceState(Opaque<bindings::DeviceState>);
 
 unsafe impl Send for DeviceState {}
diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
index 81014f9ebf..020d650b50 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 421b35d98d..dff2daae35 100644
--- a/rust/hw/core/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -22,7 +22,7 @@
 };
 
 #[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qemu_macros::Object)]
 pub struct DummyState {
     parent: ParentField<DeviceState>,
     migrate_clock: bool,
@@ -74,7 +74,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 b8e6b1fe31..0ada7fcfde 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 f8b80e5771..c77f6af174 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 8492868634..d0b2615c52 100644
--- a/rust/qom/meson.build
+++ b/rust/qom/meson.build
@@ -32,10 +32,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/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 29568ed767..7312f809f5 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 8f3ebf5415..a7a5bd8d99 100644
--- a/rust/util/meson.build
+++ b/rust/util/meson.build
@@ -44,7 +44,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] 38+ messages in thread

* [PATCH 17/22] rust/hpet: drop now unneeded qemu_api dep
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (15 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 16/22] rust: rename qemu_api_macros -> qemu_macros marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 18/22] rust/pl011: drop dependency on qemu_api marcandre.lureau
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, Manos Pitsidianakis

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

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 rust/Cargo.lock                | 1 -
 rust/hw/timer/hpet/Cargo.toml  | 1 -
 rust/hw/timer/hpet/meson.build | 1 -
 3 files changed, 3 deletions(-)

diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 91f137d75c..80ad6f0fc6 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -93,7 +93,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_api",
  "qemu_macros",
  "qom",
  "system",
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 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] 38+ messages in thread

* [PATCH 18/22] rust/pl011: drop dependency on qemu_api
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (16 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 17/22] rust/hpet: drop now unneeded qemu_api dep marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 19/22] rust: repurpose qemu_api -> tests marcandre.lureau
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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                    |  1 -
 rust/hw/char/pl011/Cargo.toml      |  1 -
 rust/hw/char/pl011/build.rs        |  1 +
 rust/hw/char/pl011/meson.build     | 27 ++++++++++++++--
 rust/hw/char/pl011/src/bindings.rs | 27 ++++++++++++++++
 rust/hw/char/pl011/src/device.rs   |  2 +-
 rust/hw/char/pl011/src/lib.rs      |  1 +
 8 files changed, 106 insertions(+), 5 deletions(-)
 create mode 100644 rust/hw/char/pl011/wrapper.h
 create mode 120000 rust/hw/char/pl011/build.rs
 create mode 100644 rust/hw/char/pl011/src/bindings.rs

diff --git a/rust/hw/char/pl011/wrapper.h b/rust/hw/char/pl011/wrapper.h
new file mode 100644
index 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 80ad6f0fc6..d0299dc262 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -149,7 +149,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_api",
  "qemu_macros",
  "qom",
  "system",
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 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 120000
index 0000000000..5f5060db35
--- /dev/null
+++ b/rust/hw/char/pl011/build.rs
@@ -0,0 +1 @@
+../../../util/build.rs
\ No newline at end of file
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 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 b7c1cb68a2..5ce39b002e 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -134,7 +134,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] 38+ messages in thread

* [PATCH 19/22] rust: repurpose qemu_api -> tests
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (17 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 18/22] rust/pl011: drop dependency on qemu_api marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 20/22] rust: re-export qemu_macros internal helper in "bits" marcandre.lureau
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, 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/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           |  7 +-
 rust/tests/meson.build                        | 14 +++
 .../tests/vmstate_tests.rs                    |  0
 14 files changed, 38 insertions(+), 238 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/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 (80%)
 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 d0299dc262..b7e55e0129 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -187,24 +187,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"
@@ -255,6 +237,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 4a97510676..84b6eefefb 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/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 80%
rename from rust/qemu-api/Cargo.toml
rename to rust/tests/Cargo.toml
index c77f6af174..079c490363 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/tests/Cargo.toml
@@ -1,12 +1,11 @@
 [package]
-name = "qemu_api"
+name = "tests"
 version = "0.1.0"
-authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
-description = "Rust bindings for QEMU"
-readme = "README.md"
+description = "Rust integration tests for QEMU"
 resolver = "2"
 publish = false
 
+authors.workspace = true
 edition.workspace = true
 homepage.workspace = true
 license.workspace = true
diff --git a/rust/tests/meson.build b/rust/tests/meson.build
new file mode 100644
index 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] 38+ messages in thread

* [PATCH 20/22] rust: re-export qemu_macros internal helper in "bits"
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (18 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 19/22] rust: repurpose qemu_api -> tests marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 21/22] rust: re-export qemu macros from common/qom/hwcore marcandre.lureau
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, Manos Pitsidianakis

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

Avoid the need to import "qemu_macros".

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 rust/bits/src/lib.rs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

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



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

* [PATCH 21/22] rust: re-export qemu macros from common/qom/hwcore
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (19 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 20/22] rust: re-export qemu_macros internal helper in "bits" marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 10:41 ` [PATCH 22/22] docs: update rust.rst marcandre.lureau
  2025-08-27 17:25 ` [PATCH 00/22] rust: split qemu-api Paolo Bonzini
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, Manos Pitsidianakis

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

This is just a bit nicer.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 docs/devel/rust.rst                 | 2 +-
 rust/Cargo.lock                     | 9 +--------
 rust/chardev/Cargo.toml             | 1 -
 rust/chardev/meson.build            | 4 ++--
 rust/chardev/src/chardev.rs         | 2 +-
 rust/common/Cargo.toml              | 1 +
 rust/common/meson.build             | 2 +-
 rust/common/src/lib.rs              | 2 ++
 rust/common/src/opaque.rs           | 4 +---
 rust/hw/char/pl011/Cargo.toml       | 1 -
 rust/hw/char/pl011/meson.build      | 3 +--
 rust/hw/char/pl011/src/device.rs    | 4 ++--
 rust/hw/char/pl011/src/registers.rs | 2 +-
 rust/hw/core/Cargo.toml             | 1 -
 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       | 1 -
 rust/hw/timer/hpet/meson.build      | 4 ----
 rust/hw/timer/hpet/src/device.rs    | 6 +++---
 rust/meson.build                    | 3 ++-
 rust/migration/Cargo.toml           | 1 -
 rust/qom/src/lib.rs                 | 2 ++
 rust/qom/src/qom.rs                 | 4 ++--
 rust/system/Cargo.toml              | 1 -
 rust/system/meson.build             | 5 ++---
 rust/system/src/memory.rs           | 2 +-
 rust/tests/Cargo.toml               | 1 -
 rust/util/Cargo.toml                | 1 -
 rust/util/meson.build               | 2 +-
 rust/util/src/timer.rs              | 4 ++--
 33 files changed, 37 insertions(+), 54 deletions(-)

diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index f52da6f97e..1e77a1c2b1 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -290,7 +290,7 @@ a raw pointer, for use in calls to C functions.  It can be used for
 example as follows::
 
     #[repr(transparent)]
-    #[derive(Debug, qemu_api_macros::Wrapper)]
+    #[derive(Debug, common::Wrapper)]
     pub struct Object(Opaque<bindings::Object>);
 
 where the special ``derive`` macro provides useful methods such as
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index b7e55e0129..8e8ab6fd8c 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -58,7 +58,6 @@ dependencies = [
  "bql",
  "common",
  "migration",
- "qemu_macros",
  "qom",
  "util",
 ]
@@ -68,6 +67,7 @@ name = "common"
 version = "0.1.0"
 dependencies = [
  "libc",
+ "qemu_macros",
 ]
 
 [[package]]
@@ -93,7 +93,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_macros",
  "qom",
  "system",
  "util",
@@ -107,7 +106,6 @@ dependencies = [
  "chardev",
  "common",
  "migration",
- "qemu_macros",
  "qom",
  "system",
  "util",
@@ -133,7 +131,6 @@ name = "migration"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_macros",
  "util",
 ]
 
@@ -149,7 +146,6 @@ dependencies = [
  "common",
  "hwcore",
  "migration",
- "qemu_macros",
  "qom",
  "system",
  "util",
@@ -232,7 +228,6 @@ name = "system"
 version = "0.1.0"
 dependencies = [
  "common",
- "qemu_macros",
  "qom",
  "util",
 ]
@@ -249,7 +244,6 @@ dependencies = [
  "hwcore",
  "libc",
  "migration",
- "qemu_macros",
  "qom",
  "system",
  "util",
@@ -269,7 +263,6 @@ dependencies = [
  "common",
  "foreign",
  "libc",
- "qemu_macros",
 ]
 
 [[package]]
diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml
index c139177307..3e77972546 100644
--- a/rust/chardev/Cargo.toml
+++ b/rust/chardev/Cargo.toml
@@ -18,7 +18,6 @@ bql = { path = "../bql" }
 migration = { path = "../migration" }
 qom = { path = "../qom" }
 util = { path = "../util" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build
index 487f36e27d..b0fd415686 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_macros, qom_rs, util_rs, chardev],
+  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qom_rs, util_rs, chardev],
 )
 
-chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev])
+chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [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 0e0a8f5556..07b02bdd76 100644
--- a/rust/chardev/src/chardev.rs
+++ b/rust/chardev/src/chardev.rs
@@ -26,7 +26,7 @@
 
 /// A safe wrapper around [`bindings::Chardev`].
 #[repr(transparent)]
-#[derive(qemu_macros::Wrapper)]
+#[derive(common::Wrapper)]
 pub struct Chardev(Opaque<bindings::Chardev>);
 
 pub type ChardevClass = bindings::ChardevClass;
diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml
index 5e106427e8..0e1b4fc505 100644
--- a/rust/common/Cargo.toml
+++ b/rust/common/Cargo.toml
@@ -14,6 +14,7 @@ rust-version.workspace = true
 
 [dependencies]
 libc.workspace = true
+qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/common/meson.build b/rust/common/meson.build
index 230a967760..b805e0faf5 100644
--- a/rust/common/meson.build
+++ b/rust/common/meson.build
@@ -19,7 +19,7 @@ _common_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _common_cfg,
-  dependencies: [libc_rs],
+  dependencies: [libc_rs, qemu_macros],
 )
 
 common_rs = declare_dependency(link_with: [_common_rs])
diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs
index 25216503aa..8311bf945d 100644
--- a/rust/common/src/lib.rs
+++ b/rust/common/src/lib.rs
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+pub use qemu_macros::{TryInto, Wrapper};
+
 pub mod assertions;
 
 pub mod bitops;
diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs
index 06f6b8cd29..ffc749aa0e 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_macros::Wrapper`] derive macro.
+/// Usually defined via the [`crate::Wrapper`] derive macro.
 ///
 /// # Examples
 ///
@@ -229,8 +229,6 @@ pub unsafe fn new() -> Self {
 /// ```
 ///
 /// They are not defined here to allow them to be `const`.
-///
-/// [`qemu_macros::Wrapper`]: ../../qemu_macros/derive.Wrapper.html
 pub unsafe trait Wrapper {
     type Wrapped;
 }
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 285d25c217..b2418abc4b 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -24,7 +24,6 @@ qom = { path = "../../../qom" }
 chardev = { path = "../../../chardev" }
 system = { path = "../../../system" }
 hwcore = { path = "../../../hw/core" }
-qemu_macros = { path = "../../../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index a14993f692..cee6beff87 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -35,7 +35,6 @@ _libpl011_rs = static_library(
     util_rs,
     migration_rs,
     bql_rs,
-    qemu_macros,
     qom_rs,
     chardev_rs,
     system_rs,
@@ -47,6 +46,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_macros],
+  dependencies: [bilge_impl_rs],
   variables: {'crate': 'pl011'},
 )])
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 5ce39b002e..c3a729b59f 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -103,7 +103,7 @@ pub struct PL011Registers {
 }
 
 #[repr(C)]
-#[derive(qemu_macros::Object)]
+#[derive(qom::Object)]
 /// PL011 Device Model in QEMU
 pub struct PL011State {
     pub parent_obj: ParentField<SysBusDevice>,
@@ -688,7 +688,7 @@ pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
 }
 
 #[repr(C)]
-#[derive(qemu_macros::Object)]
+#[derive(qom::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 a1c41347ed..0c3a4d7d21 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -16,7 +16,7 @@
 #[doc(alias = "offset")]
 #[allow(non_camel_case_types)]
 #[repr(u64)]
-#[derive(Debug, Eq, PartialEq, qemu_macros::TryInto)]
+#[derive(Debug, Eq, PartialEq, common::TryInto)]
 pub enum RegisterOffset {
     /// Data Register
     ///
diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml
index 0eb9ffee26..cc7321cfb4 100644
--- a/rust/hw/core/Cargo.toml
+++ b/rust/hw/core/Cargo.toml
@@ -20,7 +20,6 @@ chardev = { path = "../../chardev" }
 migration = { path = "../../migration" }
 system = { path = "../../system" }
 util = { path = "../../util" }
-qemu_macros = { path = "../../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
index 692c4b8e0f..394df40e88 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_macros, common_rs, bql_rs, chardev_rs, migration_rs, qemuutil_rs, qom_rs, system_rs, util_rs,
+  dependencies: [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_macros, util_rs]),
+        dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs
index d8d964cad2..e0d7784d97 100644
--- a/rust/hw/core/src/irq.rs
+++ b/rust/hw/core/src/irq.rs
@@ -18,7 +18,7 @@
 
 /// An opaque wrapper around [`bindings::IRQState`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct IRQState(Opaque<bindings::IRQState>);
 
 /// Interrupt sources are used by devices to pass changes to a value (typically
diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs
index 69d8a7f4c1..ffb88948af 100644
--- a/rust/hw/core/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -23,7 +23,7 @@
 
 /// A safe wrapper around [`bindings::Clock`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct Clock(Opaque<bindings::Clock>);
 
 unsafe impl Send for Clock {}
@@ -31,7 +31,7 @@ unsafe impl Sync for Clock {}
 
 /// A safe wrapper around [`bindings::DeviceState`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct DeviceState(Opaque<bindings::DeviceState>);
 
 unsafe impl Send for DeviceState {}
diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
index 020d650b50..45203229b1 100644
--- a/rust/hw/core/src/sysbus.rs
+++ b/rust/hw/core/src/sysbus.rs
@@ -19,7 +19,7 @@
 
 /// A safe wrapper around [`bindings::SysBusDevice`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct SysBusDevice(Opaque<bindings::SysBusDevice>);
 
 unsafe impl Send for SysBusDevice {}
diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs
index dff2daae35..53979ead8f 100644
--- a/rust/hw/core/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -22,7 +22,7 @@
 };
 
 #[repr(C)]
-#[derive(qemu_macros::Object)]
+#[derive(qom::Object)]
 pub struct DummyState {
     parent: ParentField<DeviceState>,
     migrate_clock: bool,
@@ -74,7 +74,7 @@ fn vmsd() -> Option<&'static VMStateDescription> {
 }
 
 #[repr(C)]
-#[derive(qemu_macros::Object)]
+#[derive(qom::Object)]
 pub struct DummyChildState {
     parent: ParentField<DummyState>,
 }
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 08bf97af55..f781b28d8b 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -17,7 +17,6 @@ migration = { path = "../../../migration" }
 bql = { path = "../../../bql" }
 qom = { path = "../../../qom" }
 system = { path = "../../../system" }
-qemu_macros = { path = "../../../qemu-macros" }
 hwcore = { path = "../../../hw/core" }
 
 [lints]
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index 8ab26630d9..bb64b96672 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -8,7 +8,6 @@ _libhpet_rs = static_library(
     util_rs,
     migration_rs,
     bql_rs,
-    qemu_macros,
     qom_rs,
     system_rs,
     hwcore_rs,
@@ -17,8 +16,5 @@ _libhpet_rs = static_library(
 
 rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency(
   link_whole: [_libhpet_rs],
-  # Putting proc macro crates in `dependencies` is necessary for Meson to find
-  # them when compiling the root per-target static rust lib.
-  dependencies: [qemu_macros],
   variables: {'crate': 'hpet'},
 )])
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 0ada7fcfde..43ee39fbf9 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -97,7 +97,7 @@
 /// Timer N Interrupt Routing Capability (bits 32:63)
 const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32;
 
-#[derive(qemu_macros::TryInto)]
+#[derive(common::TryInto)]
 #[repr(u64)]
 #[allow(non_camel_case_types)]
 /// Timer registers, masked by 0x18
@@ -110,7 +110,7 @@ enum TimerRegister {
     ROUTE = 16,
 }
 
-#[derive(qemu_macros::TryInto)]
+#[derive(common::TryInto)]
 #[repr(u64)]
 #[allow(non_camel_case_types)]
 /// Global registers
@@ -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_macros::Object)]
+#[derive(qom::Object)]
 pub struct HPETState {
     parent_obj: ParentField<SysBusDevice>,
     iomem: MemoryRegion,
diff --git a/rust/meson.build b/rust/meson.build
index 32d569f871..6b50aa39b6 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -22,8 +22,9 @@ qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true)
 
 genrs = []
 
-subdir('common')
 subdir('qemu-macros')
+
+subdir('common')
 subdir('bits')
 subdir('util')
 subdir('migration')
diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml
index 66af81e0a3..708bfaaa68 100644
--- a/rust/migration/Cargo.toml
+++ b/rust/migration/Cargo.toml
@@ -15,7 +15,6 @@ rust-version.workspace = true
 [dependencies]
 common = { path = "../common" }
 util = { path = "../util" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs
index 35ddc51bb0..007b604d73 100644
--- a/rust/qom/src/lib.rs
+++ b/rust/qom/src/lib.rs
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+pub use qemu_macros::Object;
+
 pub mod bindings;
 
 pub mod prelude;
diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs
index 880bef6c47..06a7f1c3b0 100644
--- a/rust/qom/src/qom.rs
+++ b/rust/qom/src/qom.rs
@@ -112,7 +112,7 @@
 
 /// A safe wrapper around [`bindings::Object`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct Object(Opaque<bindings::Object>);
 
 unsafe impl Send for Object {}
@@ -173,7 +173,7 @@ fn as_ref(&self) -> &$parent {
 ///
 /// ```ignore
 /// #[repr(C)]
-/// #[derive(qemu_macros::Object)]
+/// #[derive(qom::Object)]
 /// pub struct MyDevice {
 ///     parent: ParentField<DeviceState>,
 ///     ...
diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
index d8338c8348..7fd369b9e3 100644
--- a/rust/system/Cargo.toml
+++ b/rust/system/Cargo.toml
@@ -16,7 +16,6 @@ rust-version.workspace = true
 common = { path = "../common" }
 qom = { path = "../qom" }
 util = { path = "../util" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/system/meson.build b/rust/system/meson.build
index 710462376d..13cf4ed9cb 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_macros, qom_rs, util_rs,
+  dependencies: [qemuutil_rs, common_rs, bql_rs, migration_rs, qom_rs, util_rs,
                 hwcore],
 )
 
 system_rs = declare_dependency(link_with: [_system_rs],
-  dependencies: [qemu_macros, hwcore])
+  dependencies: [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
@@ -54,4 +54,3 @@ rust.doctest('rust-system-rs-doctests',
      protocol: 'rust',
      dependencies: system_rs,
      suite: ['doc', 'rust'])
-
diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs
index 7312f809f5..02aa3af7b1 100644
--- a/rust/system/src/memory.rs
+++ b/rust/system/src/memory.rs
@@ -129,7 +129,7 @@ fn default() -> Self {
 
 /// A safe wrapper around [`bindings::MemoryRegion`].
 #[repr(transparent)]
-#[derive(qemu_macros::Wrapper)]
+#[derive(common::Wrapper)]
 pub struct MemoryRegion(Opaque<bindings::MemoryRegion>);
 
 unsafe impl Send for MemoryRegion {}
diff --git a/rust/tests/Cargo.toml b/rust/tests/Cargo.toml
index 079c490363..50d63afa02 100644
--- a/rust/tests/Cargo.toml
+++ b/rust/tests/Cargo.toml
@@ -19,7 +19,6 @@ hwcore = { path = "../hw/core" }
 migration = { path = "../migration" }
 util = { path = "../util" }
 bql = { path = "../bql" }
-qemu_macros = { path = "../qemu-macros" }
 qom = { path = "../qom" }
 system = { path = "../system" }
 anyhow = "~1.0"
diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml
index c5d1adb8fe..cbc8e5675d 100644
--- a/rust/util/Cargo.toml
+++ b/rust/util/Cargo.toml
@@ -17,7 +17,6 @@ anyhow = "~1.0"
 libc = "0.2.162"
 foreign = "~0.3.1"
 common = { path = "../common" }
-qemu_macros = { path = "../qemu-macros" }
 
 [lints]
 workspace = true
diff --git a/rust/util/meson.build b/rust/util/meson.build
index a7a5bd8d99..c2fbf49d11 100644
--- a/rust/util/meson.build
+++ b/rust/util/meson.build
@@ -44,7 +44,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_macros],
+  dependencies: [anyhow_rs, libc_rs, foreign_rs, qemuutil_rs, common_rs],
 )
 
 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 42096cef0d..a87d299ebc 100644
--- a/rust/util/src/timer.rs
+++ b/rust/util/src/timer.rs
@@ -15,14 +15,14 @@
 
 /// A safe wrapper around [`bindings::QEMUTimer`].
 #[repr(transparent)]
-#[derive(Debug, qemu_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
 pub struct Timer(Opaque<bindings::QEMUTimer>);
 
 unsafe impl Send for Timer {}
 unsafe impl Sync for Timer {}
 
 #[repr(transparent)]
-#[derive(qemu_macros::Wrapper)]
+#[derive(common::Wrapper)]
 pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);
 
 unsafe impl Send for TimerListGroup {}
-- 
2.50.1



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

* [PATCH 22/22] docs: update rust.rst
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (20 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 21/22] rust: re-export qemu macros from common/qom/hwcore marcandre.lureau
@ 2025-08-27 10:41 ` marcandre.lureau
  2025-08-27 17:25 ` [PATCH 00/22] rust: split qemu-api Paolo Bonzini
  22 siblings, 0 replies; 38+ messages in thread
From: marcandre.lureau @ 2025-08-27 10:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Daniel P. Berrangé, Philippe Mathieu-Daudé,
	pbonzini, Marc-André Lureau, Manos Pitsidianakis

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

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 docs/devel/rust.rst | 61 ++++++++++++++++++++++++---------------------
 1 file changed, 32 insertions(+), 29 deletions(-)

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



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

* Re: [PATCH 01/22] docs/rust: update msrv
  2025-08-27 10:41 ` [PATCH 01/22] docs/rust: update msrv marcandre.lureau
@ 2025-08-27 14:37   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-27 14:37 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:23PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:23 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 01/22] docs/rust: update msrv
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  docs/devel/rust.rst | 2 +-
>  rust/Cargo.toml     | 1 +
>  2 files changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
> index b6737536c6..f52da6f97e 100644
> --- a/docs/devel/rust.rst
> +++ b/docs/devel/rust.rst
> @@ -1,4 +1,4 @@
> -.. |msrv| replace:: 1.63.0
> +.. |msrv| replace:: 1.77.0
>
>  Rust in QEMU
>  ============
> diff --git a/rust/Cargo.toml b/rust/Cargo.toml
> index 0868e1b426..56cfe07791 100644
> --- a/rust/Cargo.toml
> +++ b/rust/Cargo.toml
> @@ -13,6 +13,7 @@ edition = "2021"
>  homepage = "https://www.qemu.org"
>  license = "GPL-2.0-or-later"
>  repository = "https://gitlab.com/qemu-project/qemu/"
> +# don't forget to update docs/devel/rust.rst msrv

Good comment!

>  rust-version = "1.77.0"
>  

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



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

* Re: [PATCH 02/22] rust: remove unused global qemu "allocator"
  2025-08-27 10:41 ` [PATCH 02/22] rust: remove unused global qemu "allocator" marcandre.lureau
@ 2025-08-27 14:55   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-27 14:55 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:24PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:24 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 02/22] rust: remove unused global qemu "allocator"
> 
> 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(-)

There's a discussion almost one years ago:

https://lore.kernel.org/qemu-devel/CABgObfYY1PrD9dRKRgzgyRAe8gzp-4KHvV1-Bgq32_2jpaTMPg@mail.gmail.com/#t

Now we have libc crate and have an example which allocates the C structure
at Rust side:

let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>());

this indeed seems simpler than enabling QemuAllocator.

So it's fine for me to remove this allocator:

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


let's see if Paolo & Manos have other comment.




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

* Re: [PATCH 03/22] rust: add workspace authors
  2025-08-27 10:41 ` [PATCH 03/22] rust: add workspace authors marcandre.lureau
@ 2025-08-27 14:56   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-27 14:56 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:25PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:25 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 03/22] rust: add workspace authors
> 
> 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(+)

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



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

* Re: [PATCH 04/22] rust: make build.rs generic over various ./rust/projects
  2025-08-27 10:41 ` [PATCH 04/22] rust: make build.rs generic over various ./rust/projects marcandre.lureau
@ 2025-08-27 15:06   ` Zhao Liu
  2025-08-28  7:14   ` Zhao Liu
  1 sibling, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-27 15:06 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:26PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:26 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 04/22] rust: make build.rs generic over various
>  ./rust/projects
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Guess the name of the subdir from the manifest directory, instead of
> hard-coding it. In the following commits, other crates can then link to
> this file, instead of maintaining their own copy.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  rust/qemu-api/build.rs | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)> 

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



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

* Re: [PATCH 05/22] rust: split Rust-only "common" crate
  2025-08-27 10:41 ` [PATCH 05/22] rust: split Rust-only "common" crate marcandre.lureau
@ 2025-08-27 15:20   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-27 15:20 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:27PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:27 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 05/22] rust: split Rust-only "common" crate
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---

...

> diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml
> new file mode 100644
> index 0000000000..5e106427e8
> --- /dev/null
> +++ b/rust/common/Cargo.toml
> @@ -0,0 +1,19 @@
> +[package]
> +name = "common"
> +version = "0.1.0"
> +description = "Rust common code for QEMU"

Meybe "Rust-only common code for QEMU"

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



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

* Re: [PATCH 00/22] rust: split qemu-api
  2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
                   ` (21 preceding siblings ...)
  2025-08-27 10:41 ` [PATCH 22/22] docs: update rust.rst marcandre.lureau
@ 2025-08-27 17:25 ` Paolo Bonzini
  22 siblings, 0 replies; 38+ messages in thread
From: Paolo Bonzini @ 2025-08-27 17:25 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 12:42 PM <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.

The only question I have is about having bindings in each crate rather
than in a single common crate. I'm okay with doing this on top,
especially given that I spent too much time rebasing on top of all the
pending patches. :)

Right now I'm stuck with doctests failures with the patches that use
Meson's new support for mixed C and Rust targets. I'll probably drop
that, but (as a heads up) I"m probably not going to send out a pull
request until after KVM Forum.

Paolo



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

* Re: [PATCH 06/22] rust: split "util" crate
  2025-08-27 10:41 ` [PATCH 06/22] rust: split "util" crate marcandre.lureau
@ 2025-08-28  7:10   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-28  7:10 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:28PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:28 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 06/22] rust: split "util" crate
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---

...

> diff --git a/rust/common/src/errno.rs b/rust/common/src/errno.rs
> index b48247b947..a7b23b4092 100644
> --- a/rust/common/src/errno.rs
> +++ b/rust/common/src/errno.rs
> @@ -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;

typo?

>      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;

typo?

With fixed,

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



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

* Re: [PATCH 04/22] rust: make build.rs generic over various ./rust/projects
  2025-08-27 10:41 ` [PATCH 04/22] rust: make build.rs generic over various ./rust/projects marcandre.lureau
  2025-08-27 15:06   ` Zhao Liu
@ 2025-08-28  7:14   ` Zhao Liu
  1 sibling, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-28  7:14 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:26PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:26 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 04/22] rust: make build.rs generic over various
>  ./rust/projects
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Guess the name of the subdir from the manifest directory, instead of
> hard-coding it. In the following commits, other crates can then link to
> this file, instead of maintaining their own copy.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  rust/qemu-api/build.rs | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
> index 29d0945625..92237183ec 100644
> --- a/rust/qemu-api/build.rs
> +++ b/rust/qemu-api/build.rs
> @@ -9,12 +9,14 @@
>  use std::{env, fs::remove_file, io::Result, path::Path};
>  
>  fn main() -> Result<()> {
> +    let manifest_dir = env!("CARGO_MANIFEST_DIR");
>      let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
> -        format!("{root}/rust/qemu-api/bindings.inc.rs")
> +        let sub = get_rust_subdir(manifest_dir).unwrap();
> +        format!("{root}/{sub}/bindings.inc.rs")
>      } else {
>          // Placing bindings.inc.rs in the source directory is supported
>          // but not documented or encouraged.
> -        format!("{}/src/bindings.inc.rs", env!("CARGO_MANIFEST_DIR"))
> +        format!("{}/src/bindings.inc.rs", manifest_dir)

Only a nit: cargo clippy suggests this inline style:

format!("{manifest_dir}/src/bindings.inc.rs")

>      };
  


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

* Re: [PATCH 07/22] rust: move vmstate_clock!() to qdev module
  2025-08-27 10:41 ` [PATCH 07/22] rust: move vmstate_clock!() to qdev module marcandre.lureau
@ 2025-08-29  8:11   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-29  8:11 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:29PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:29 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 07/22] rust: move vmstate_clock!() to qdev module
> 
> 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(-)

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



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

* Re: [PATCH 08/22] rust: move VMState handling to QOM module
  2025-08-27 10:41 ` [PATCH 08/22] rust: move VMState handling to QOM module marcandre.lureau
@ 2025-08-29  8:12   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-29  8:12 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:30PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:30 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 08/22] rust: move VMState handling to QOM module
> 
> 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(-)

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



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

* Re: [PATCH 09/22] rust: move Cell vmstate impl
  2025-08-27 10:41 ` [PATCH 09/22] rust: move Cell vmstate impl marcandre.lureau
@ 2025-08-29  8:22   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-29  8:22 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:31PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:31 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 09/22] rust: move Cell vmstate impl
> 
> 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(-)

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



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

* Re: [PATCH 10/22] rust: split "migration" crate
  2025-08-27 10:41 ` [PATCH 10/22] rust: split "migration" crate marcandre.lureau
@ 2025-08-29  8:59   ` Zhao Liu
  2025-08-29 11:50     ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Zhao Liu @ 2025-08-29  8:59 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

Hi Marc-Andre,

On Wed, Aug 27, 2025 at 02:41:32PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:32 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 10/22] rust: split "migration" crate
> 
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---

...

> @@ -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.
> + */

Could we use /* SPDX-License-Identifier: GPL-2.0-or-later */ directly ?

[snip]

> +// 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.

A question: Sorry I didn't get your point about why we need
VMStateFieldHelper?

For its use case:

> -        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,

It seems VMStateFieldHelper add another wrapper around vmstate_struct
(and vmstate_of).

The builder pattern is good, but I'm afraid this builder makes the use of
vmstate_struct! more complex.

> -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
>      }

If we could have a build() method then user doesn't need to write ".0"
at the end.

>  }

And there's another VMStateDescriptionBuilder:

https://lore.kernel.org/qemu-devel/20250505100854.73936-4-pbonzini@redhat.com/#t

I think Paolo has the plan to merge it with v1.83 support. So if this
VMStateFieldHelper is necessary, it's better seperate this into another
patch and base it over VMStateDescriptionBuilder if possible.

Thanks,
Zhao



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

* Re: [PATCH 11/22] rust: split "bql" crate
  2025-08-27 10:41 ` [PATCH 11/22] rust: split "bql" crate marcandre.lureau
@ 2025-08-29  9:13   ` Zhao Liu
  0 siblings, 0 replies; 38+ messages in thread
From: Zhao Liu @ 2025-08-29  9:13 UTC (permalink / raw)
  To: marcandre.lureau
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

On Wed, Aug 27, 2025 at 02:41:33PM +0400, marcandre.lureau@redhat.com wrote:
> Date: Wed, 27 Aug 2025 14:41:33 +0400
> From: marcandre.lureau@redhat.com
> Subject: [PATCH 11/22] rust: split "bql" crate
> 
> 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>
> ---

[snip]

> 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 {

I prefer the original names: bql_locked()...

> +    // SAFETY: the function does nothing but return a thread-local bool
> +    unsafe { bql_locked() }
> +}
> +
> +pub fn block_unlock(increase: bool) {

... and bql_block_unlock.

> +    // SAFETY: this only adjusts a counter
> +    unsafe {
> +        bql_block_unlock(increase);
> +    }
> +}

The consistent naming can help C developers become familiar with the
same interfaces in Rust.

And we will have more and more lock bindings - add lock name in the
method name could help avoid confusion.

Others LGTM, so

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



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

* Re: [PATCH 10/22] rust: split "migration" crate
  2025-08-29  8:59   ` Zhao Liu
@ 2025-08-29 11:50     ` Marc-André Lureau
  2025-08-29 11:54       ` Daniel P. Berrangé
  0 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2025-08-29 11:50 UTC (permalink / raw)
  To: Zhao Liu
  Cc: qemu-devel, qemu-rust, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, pbonzini, Manos Pitsidianakis

[-- Attachment #1: Type: text/plain, Size: 4013 bytes --]

Hi

On Fri, Aug 29, 2025 at 12:37 PM Zhao Liu <zhao1.liu@intel.com> wrote:

> Hi Marc-Andre,
>
> On Wed, Aug 27, 2025 at 02:41:32PM +0400, marcandre.lureau@redhat.com
> wrote:
> > Date: Wed, 27 Aug 2025 14:41:32 +0400
> > From: marcandre.lureau@redhat.com
> > Subject: [PATCH 10/22] rust: split "migration" crate
> >
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
>
> ...
>
> > @@ -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.
> > + */
>
> Could we use /* SPDX-License-Identifier: GPL-2.0-or-later */ directly ?
>
> [snip]
>

I guess we could. Probably for a future cleanup though.


>
> > +// 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.
>
> A question: Sorry I didn't get your point about why we need
> VMStateFieldHelper?
>
> For its use case:
>
> > -        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,
>
> It seems VMStateFieldHelper add another wrapper around vmstate_struct
> (and vmstate_of).
>
> The builder pattern is good, but I'm afraid this builder makes the use of
> vmstate_struct! more complex.
>
> > -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
> >      }
>
> If we could have a build() method then user doesn't need to write ".0"
> at the end.
>
> >  }
>
> And there's another VMStateDescriptionBuilder:
>
>
> https://lore.kernel.org/qemu-devel/20250505100854.73936-4-pbonzini@redhat.com/#t
>
> I think Paolo has the plan to merge it with v1.83 support. So if this
> VMStateFieldHelper is necessary, it's better seperate this into another
> patch and base it over VMStateDescriptionBuilder if possible.
>
>
Paolo rebased it and dropped the need for the VMStateFieldHelper.

[-- Attachment #2: Type: text/html, Size: 5426 bytes --]

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

* Re: [PATCH 10/22] rust: split "migration" crate
  2025-08-29 11:50     ` Marc-André Lureau
@ 2025-08-29 11:54       ` Daniel P. Berrangé
  0 siblings, 0 replies; 38+ messages in thread
From: Daniel P. Berrangé @ 2025-08-29 11:54 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: Zhao Liu, qemu-devel, qemu-rust, Philippe Mathieu-Daudé,
	pbonzini, Manos Pitsidianakis

On Fri, Aug 29, 2025 at 03:50:02PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Fri, Aug 29, 2025 at 12:37 PM Zhao Liu <zhao1.liu@intel.com> wrote:
> 
> > Hi Marc-Andre,
> >
> > On Wed, Aug 27, 2025 at 02:41:32PM +0400, marcandre.lureau@redhat.com
> > wrote:
> > > Date: Wed, 27 Aug 2025 14:41:32 +0400
> > > From: marcandre.lureau@redhat.com
> > > Subject: [PATCH 10/22] rust: split "migration" crate
> > >
> > > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> > >
> > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > > ---
> >
> > ...
> >
> > > @@ -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.
> > > + */
> >
> > Could we use /* SPDX-License-Identifier: GPL-2.0-or-later */ directly ?
> >
> > [snip]
> >
> 
> I guess we could. Probably for a future cleanup though.

checkpatch will complain about this - all newly introduced files
should be using SPDX-License-Identifier from the start, with no
boilerplate text present.

Also it will warn about any license use which is not GPL-2.0-or-later,
requesting that it be explained in the commit message.

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



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

end of thread, other threads:[~2025-08-30 18:27 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-27 10:41 [PATCH 00/22] rust: split qemu-api marcandre.lureau
2025-08-27 10:41 ` [PATCH 01/22] docs/rust: update msrv marcandre.lureau
2025-08-27 14:37   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 02/22] rust: remove unused global qemu "allocator" marcandre.lureau
2025-08-27 14:55   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 03/22] rust: add workspace authors marcandre.lureau
2025-08-27 14:56   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 04/22] rust: make build.rs generic over various ./rust/projects marcandre.lureau
2025-08-27 15:06   ` Zhao Liu
2025-08-28  7:14   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 05/22] rust: split Rust-only "common" crate marcandre.lureau
2025-08-27 15:20   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 06/22] rust: split "util" crate marcandre.lureau
2025-08-28  7:10   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 07/22] rust: move vmstate_clock!() to qdev module marcandre.lureau
2025-08-29  8:11   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 08/22] rust: move VMState handling to QOM module marcandre.lureau
2025-08-29  8:12   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 09/22] rust: move Cell vmstate impl marcandre.lureau
2025-08-29  8:22   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 10/22] rust: split "migration" crate marcandre.lureau
2025-08-29  8:59   ` Zhao Liu
2025-08-29 11:50     ` Marc-André Lureau
2025-08-29 11:54       ` Daniel P. Berrangé
2025-08-27 10:41 ` [PATCH 11/22] rust: split "bql" crate marcandre.lureau
2025-08-29  9:13   ` Zhao Liu
2025-08-27 10:41 ` [PATCH 12/22] rust: split "qom" crate marcandre.lureau
2025-08-27 10:41 ` [PATCH 13/22] rust: split "chardev" crate marcandre.lureau
2025-08-27 10:41 ` [PATCH 14/22] rust: split "system" crate marcandre.lureau
2025-08-27 10:41 ` [PATCH 15/22] rust: split "hwcore" crate marcandre.lureau
2025-08-27 10:41 ` [PATCH 16/22] rust: rename qemu_api_macros -> qemu_macros marcandre.lureau
2025-08-27 10:41 ` [PATCH 17/22] rust/hpet: drop now unneeded qemu_api dep marcandre.lureau
2025-08-27 10:41 ` [PATCH 18/22] rust/pl011: drop dependency on qemu_api marcandre.lureau
2025-08-27 10:41 ` [PATCH 19/22] rust: repurpose qemu_api -> tests marcandre.lureau
2025-08-27 10:41 ` [PATCH 20/22] rust: re-export qemu_macros internal helper in "bits" marcandre.lureau
2025-08-27 10:41 ` [PATCH 21/22] rust: re-export qemu macros from common/qom/hwcore marcandre.lureau
2025-08-27 10:41 ` [PATCH 22/22] docs: update rust.rst marcandre.lureau
2025-08-27 17:25 ` [PATCH 00/22] rust: split qemu-api Paolo Bonzini

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).