summary refs log tree commit diff stats
path: root/rust/qemu-api
diff options
context:
space:
mode:
Diffstat (limited to 'rust/qemu-api')
-rw-r--r--rust/qemu-api/Cargo.toml4
l---------[-rw-r--r--]rust/qemu-api/build.rs50
-rw-r--r--rust/qemu-api/meson.build14
-rw-r--r--rust/qemu-api/src/bindings.rs1
-rw-r--r--rust/qemu-api/src/error.rs417
-rw-r--r--rust/qemu-api/src/lib.rs6
-rw-r--r--rust/qemu-api/src/log.rs151
-rw-r--r--rust/qemu-api/src/module.rs43
-rw-r--r--rust/qemu-api/src/prelude.rs2
-rw-r--r--rust/qemu-api/src/qdev.rs4
-rw-r--r--rust/qemu-api/src/sysbus.rs2
-rw-r--r--rust/qemu-api/src/timer.rs125
-rw-r--r--rust/qemu-api/src/vmstate.rs2
-rw-r--r--rust/qemu-api/tests/tests.rs2
-rw-r--r--rust/qemu-api/wrapper.h6
15 files changed, 11 insertions, 818 deletions
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 2e0e204491..fbfb894421 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -15,10 +15,8 @@ rust-version.workspace = true
 
 [dependencies]
 common = { path = "../common" }
+util = { path = "../util" }
 qemu_api_macros = { path = "../qemu-api-macros" }
-anyhow = { workspace = true }
-foreign = { workspace = true }
-libc = { workspace = true }
 
 [features]
 default = ["debug_cell"]
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
index 5654d1d562..71a3167885 100644..120000
--- a/rust/qemu-api/build.rs
+++ b/rust/qemu-api/build.rs
@@ -1,49 +1 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#[cfg(unix)]
-use std::os::unix::fs::symlink as symlink_file;
-#[cfg(windows)]
-use std::os::windows::fs::symlink_file;
-use std::{env, fs::remove_file, io::Result, path::Path};
-
-fn main() -> Result<()> {
-    let manifest_dir = env!("CARGO_MANIFEST_DIR");
-    let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
-        let sub = get_rust_subdir(manifest_dir).unwrap();
-        format!("{root}/{sub}/bindings.inc.rs")
-    } else {
-        // Placing bindings.inc.rs in the source directory is supported
-        // but not documented or encouraged.
-        format!("{manifest_dir}/src/bindings.inc.rs")
-    };
-
-    let file = Path::new(&file);
-    if !Path::new(&file).exists() {
-        panic!(concat!(
-            "\n",
-            "    No generated C bindings found! Maybe you wanted one of\n",
-            "    `make clippy`, `make rustfmt`, `make rustdoc`?\n",
-            "\n",
-            "    For other uses of `cargo`, start a subshell with\n",
-            "    `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n",
-            "    the top of the build tree."
-        ));
-    }
-
-    let out_dir = env::var("OUT_DIR").unwrap();
-    let dest_path = format!("{out_dir}/bindings.inc.rs");
-    let dest_path = Path::new(&dest_path);
-    if dest_path.symlink_metadata().is_ok() {
-        remove_file(dest_path)?;
-    }
-    symlink_file(file, dest_path)?;
-
-    println!("cargo:rerun-if-changed=build.rs");
-    Ok(())
-}
-
-fn get_rust_subdir(path: &str) -> Option<&str> {
-    path.find("/rust").map(|index| &path[index + 1..])
-}
+../util/build.rs
\ No newline at end of file
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 64af3caef5..7734f656a2 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -14,10 +14,8 @@ c_enums = [
   'MigrationPolicy',
   'MigrationPriority',
   'QEMUChrEvent',
-  'QEMUClockType',
   'ResetType',
   'device_endian',
-  'module_init_type',
 ]
 _qemu_api_bindgen_args = []
 foreach enum : c_enums
@@ -31,6 +29,7 @@ foreach enum : c_bitfields
   _qemu_api_bindgen_args += ['--bitfield-enum', enum]
 endforeach
 
+_qemu_api_bindgen_args += ['--blocklist-type', 'Error']
 # TODO: Remove this comment when the clang/libclang mismatch issue is solved.
 #
 # Rust bindings generation with `bindgen` might fail in some cases where the
@@ -55,16 +54,12 @@ _qemu_api_rs = static_library(
       'src/bindings.rs',
       'src/cell.rs',
       'src/chardev.rs',
-      'src/error.rs',
       'src/irq.rs',
-      'src/log.rs',
       'src/memory.rs',
-      'src/module.rs',
       'src/prelude.rs',
       'src/qdev.rs',
       'src/qom.rs',
       'src/sysbus.rs',
-      'src/timer.rs',
       'src/vmstate.rs',
     ],
     {'.' : _qemu_api_bindings_inc_rs},
@@ -72,13 +67,10 @@ _qemu_api_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
-  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs,
+  dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, util_rs,
                  qom, hwcore, chardev, migration],
 )
 
-rust.test('rust-qemu-api-tests', _qemu_api_rs,
-          suite: ['unit', 'rust'])
-
 qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
   dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
 
@@ -98,7 +90,7 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [common_rs, qemu_api_rs]),
+        dependencies: [common_rs, util_rs, qemu_api_rs]),
     args: [
         '--test', '--test-threads', '1',
         '--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 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/error.rs b/rust/qemu-api/src/error.rs
deleted file mode 100644
index 8bac3cbec8..0000000000
--- a/rust/qemu-api/src/error.rs
+++ /dev/null
@@ -1,417 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-//! Error propagation for QEMU Rust code
-//!
-//! This module contains [`Error`], the bridge between Rust errors and
-//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error)
-//! struct.
-//!
-//! For FFI code, [`Error`] provides functions to simplify conversion between
-//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions:
-//!
-//! * [`ok_or_propagate`](crate::Error::ok_or_propagate),
-//!   [`bool_or_propagate`](crate::Error::bool_or_propagate),
-//!   [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build
-//!   a C return value while also propagating an error condition
-//!
-//! * [`err_or_else`](crate::Error::err_or_else) and
-//!   [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result`
-//!
-//! 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
-//! [`std::error::Error`] interface to let C errors participate in Rust's error
-//! handling functionality.
-//!
-//! Rust code can also create use this module to create an error object that
-//! will be passed up to C code, though in most cases this will be done
-//! transparently through the `?` operator.  Errors can be constructed from a
-//! simple error string, from an [`anyhow::Error`] to pass any other Rust error
-//! 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
-//! to how QEMU's C code handles errno values, the string and the
-//! `anyhow::Error` object will be concatenated with `:` as the separator.
-
-use std::{
-    borrow::Cow,
-    ffi::{c_char, c_int, c_void, CStr},
-    fmt::{self, Display},
-    panic, ptr,
-};
-
-use foreign::{prelude::*, OwnedPointer};
-
-use crate::bindings;
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-#[derive(Debug)]
-pub struct Error {
-    msg: Option<Cow<'static, str>>,
-    /// Appends the print string of the error to the msg if not None
-    cause: Option<anyhow::Error>,
-    file: &'static str,
-    line: u32,
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        self.cause.as_ref().map(AsRef::as_ref)
-    }
-
-    #[allow(deprecated)]
-    fn description(&self) -> &str {
-        self.msg
-            .as_deref()
-            .or_else(|| self.cause.as_deref().map(std::error::Error::description))
-            .expect("no message nor cause?")
-    }
-}
-
-impl Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut prefix = "";
-        if let Some(ref msg) = self.msg {
-            write!(f, "{msg}")?;
-            prefix = ": ";
-        }
-        if let Some(ref cause) = self.cause {
-            write!(f, "{prefix}{cause}")?;
-        } else if prefix.is_empty() {
-            panic!("no message nor cause?");
-        }
-        Ok(())
-    }
-}
-
-impl From<String> for Error {
-    #[track_caller]
-    fn from(msg: String) -> Self {
-        let location = panic::Location::caller();
-        Error {
-            msg: Some(Cow::Owned(msg)),
-            cause: None,
-            file: location.file(),
-            line: location.line(),
-        }
-    }
-}
-
-impl From<&'static str> for Error {
-    #[track_caller]
-    fn from(msg: &'static str) -> Self {
-        let location = panic::Location::caller();
-        Error {
-            msg: Some(Cow::Borrowed(msg)),
-            cause: None,
-            file: location.file(),
-            line: location.line(),
-        }
-    }
-}
-
-impl From<anyhow::Error> for Error {
-    #[track_caller]
-    fn from(error: anyhow::Error) -> Self {
-        let location = panic::Location::caller();
-        Error {
-            msg: None,
-            cause: Some(error),
-            file: location.file(),
-            line: location.line(),
-        }
-    }
-}
-
-impl Error {
-    /// Create a new error, prepending `msg` to the
-    /// description of `cause`
-    #[track_caller]
-    pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self {
-        let location = panic::Location::caller();
-        Error {
-            msg: Some(msg.into()),
-            cause: Some(cause.into()),
-            file: location.file(),
-            line: location.line(),
-        }
-    }
-
-    /// Consume a result, returning `false` if it is an error and
-    /// `true` if it is successful.  The error is propagated into
-    /// `errp` like the C API `error_propagate` would do.
-    ///
-    /// # Safety
-    ///
-    /// `errp` must be a valid argument to `error_propagate`;
-    /// typically it is received from C code and need not be
-    /// checked further at the Rust↔C boundary.
-    pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool {
-        // SAFETY: caller guarantees errp is valid
-        unsafe { Self::ok_or_propagate(result, errp) }.is_some()
-    }
-
-    /// Consume a result, returning a `NULL` pointer if it is an error and
-    /// a C representation of the contents if it is successful.  This is
-    /// similar to the C API `error_propagate`, but it panics if `*errp`
-    /// is not `NULL`.
-    ///
-    /// # Safety
-    ///
-    /// `errp` must be a valid argument to `error_propagate`;
-    /// typically it is received from C code and need not be
-    /// checked further at the Rust↔C boundary.
-    ///
-    /// See [`propagate`](Error::propagate) for more information.
-    #[must_use]
-    pub unsafe fn ptr_or_propagate<T: CloneToForeign>(
-        result: Result<T>,
-        errp: *mut *mut bindings::Error,
-    ) -> *mut T::Foreign {
-        // SAFETY: caller guarantees errp is valid
-        unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr()
-    }
-
-    /// Consume a result in the same way as `self.ok()`, but also propagate
-    /// a possible error into `errp`.  This is similar to the C API
-    /// `error_propagate`, but it panics if `*errp` is not `NULL`.
-    ///
-    /// # Safety
-    ///
-    /// `errp` must be a valid argument to `error_propagate`;
-    /// typically it is received from C code and need not be
-    /// checked further at the Rust↔C boundary.
-    ///
-    /// See [`propagate`](Error::propagate) for more information.
-    pub unsafe fn ok_or_propagate<T>(
-        result: Result<T>,
-        errp: *mut *mut bindings::Error,
-    ) -> Option<T> {
-        result.map_err(|err| unsafe { err.propagate(errp) }).ok()
-    }
-
-    /// Equivalent of the C function `error_propagate`.  Fill `*errp`
-    /// with the information container in `self` if `errp` is not NULL;
-    /// then consume it.
-    ///
-    /// This is similar to the C API `error_propagate`, but it panics if
-    /// `*errp` is not `NULL`.
-    ///
-    /// # Safety
-    ///
-    /// `errp` must be a valid argument to `error_propagate`; it can be
-    /// `NULL` or it can point to any of:
-    /// * `error_abort`
-    /// * `error_fatal`
-    /// * a local variable of (C) type `Error *`
-    ///
-    /// Typically `errp` is received from C code and need not be
-    /// checked further at the Rust↔C boundary.
-    pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) {
-        if errp.is_null() {
-            return;
-        }
-
-        // SAFETY: caller guarantees that errp and *errp are valid
-        unsafe {
-            assert_eq!(*errp, ptr::null_mut());
-            bindings::error_propagate(errp, self.clone_to_foreign_ptr());
-        }
-    }
-
-    /// Convert a C `Error*` into a Rust `Result`, using
-    /// `Ok(())` if `c_error` is NULL.  Free the `Error*`.
-    ///
-    /// # Safety
-    ///
-    /// `c_error` must be `NULL` or valid; typically it was initialized
-    /// with `ptr::null_mut()` and passed by reference to a C function.
-    pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> {
-        // SAFETY: caller guarantees c_error is valid
-        unsafe { Self::err_or_else(c_error, || ()) }
-    }
-
-    /// Convert a C `Error*` into a Rust `Result`, calling `f()` to
-    /// obtain an `Ok` value if `c_error` is NULL.  Free the `Error*`.
-    ///
-    /// # Safety
-    ///
-    /// `c_error` must be `NULL` or point to a valid C [`struct
-    /// Error`](bindings::Error); typically it was initialized with
-    /// `ptr::null_mut()` and passed by reference to a C function.
-    pub unsafe fn err_or_else<T, F: FnOnce() -> T>(
-        c_error: *mut bindings::Error,
-        f: F,
-    ) -> Result<T> {
-        // SAFETY: caller guarantees c_error is valid
-        let err = unsafe { Option::<Self>::from_foreign(c_error) };
-        match err {
-            None => Ok(f()),
-            Some(err) => Err(err),
-        }
-    }
-}
-
-impl FreeForeign for Error {
-    type Foreign = bindings::Error;
-
-    unsafe fn free_foreign(p: *mut bindings::Error) {
-        // SAFETY: caller guarantees p is valid
-        unsafe {
-            bindings::error_free(p);
-        }
-    }
-}
-
-impl CloneToForeign for Error {
-    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
-        // SAFETY: all arguments are controlled by this function
-        unsafe {
-            let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>());
-            let err: &mut bindings::Error = &mut *err.cast();
-            *err = bindings::Error {
-                msg: format!("{self}").clone_to_foreign_ptr(),
-                err_class: bindings::ERROR_CLASS_GENERIC_ERROR,
-                src_len: self.file.len() as c_int,
-                src: self.file.as_ptr().cast::<c_char>(),
-                line: self.line as c_int,
-                func: ptr::null_mut(),
-                hint: ptr::null_mut(),
-            };
-            OwnedPointer::new(err)
-        }
-    }
-}
-
-impl FromForeign for Error {
-    unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self {
-        // SAFETY: caller guarantees c_error is valid
-        unsafe {
-            let error = &*c_error;
-            let file = if error.src_len < 0 {
-                // NUL-terminated
-                CStr::from_ptr(error.src).to_str()
-            } else {
-                // Can become str::from_utf8 with Rust 1.87.0
-                std::str::from_utf8(std::slice::from_raw_parts(
-                    &*error.src.cast::<u8>(),
-                    error.src_len as usize,
-                ))
-            };
-
-            Error {
-                msg: FromForeign::cloned_from_foreign(error.msg),
-                cause: None,
-                file: file.unwrap(),
-                line: error.line as u32,
-            }
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use std::ffi::CStr;
-
-    use anyhow::anyhow;
-    use common::assert_match;
-    use foreign::OwnedPointer;
-
-    use super::*;
-    use crate::bindings;
-
-    #[track_caller]
-    fn error_for_test(msg: &CStr) -> OwnedPointer<Error> {
-        // SAFETY: all arguments are controlled by this function
-        let location = panic::Location::caller();
-        unsafe {
-            let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>());
-            let err: &mut bindings::Error = &mut *err.cast();
-            *err = bindings::Error {
-                msg: msg.clone_to_foreign_ptr(),
-                err_class: bindings::ERROR_CLASS_GENERIC_ERROR,
-                src_len: location.file().len() as c_int,
-                src: location.file().as_ptr().cast::<c_char>(),
-                line: location.line() as c_int,
-                func: ptr::null_mut(),
-                hint: ptr::null_mut(),
-            };
-            OwnedPointer::new(err)
-        }
-    }
-
-    unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr {
-        unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) }
-    }
-
-    #[test]
-    #[allow(deprecated)]
-    fn test_description() {
-        use std::error::Error;
-
-        assert_eq!(super::Error::from("msg").description(), "msg");
-        assert_eq!(super::Error::from("msg".to_owned()).description(), "msg");
-    }
-
-    #[test]
-    fn test_display() {
-        assert_eq!(&*format!("{}", Error::from("msg")), "msg");
-        assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg");
-        assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg");
-
-        assert_eq!(
-            &*format!("{}", Error::with_error("msg", anyhow!("cause"))),
-            "msg: cause"
-        );
-    }
-
-    #[test]
-    fn test_bool_or_propagate() {
-        unsafe {
-            let mut local_err: *mut bindings::Error = ptr::null_mut();
-
-            assert!(Error::bool_or_propagate(Ok(()), &mut local_err));
-            assert_eq!(local_err, ptr::null_mut());
-
-            let my_err = Error::from("msg");
-            assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err));
-            assert_ne!(local_err, ptr::null_mut());
-            assert_eq!(error_get_pretty(local_err), c"msg");
-            bindings::error_free(local_err);
-        }
-    }
-
-    #[test]
-    fn test_ptr_or_propagate() {
-        unsafe {
-            let mut local_err: *mut bindings::Error = ptr::null_mut();
-
-            let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err);
-            assert_eq!(String::from_foreign(ret), "abc");
-            assert_eq!(local_err, ptr::null_mut());
-
-            let my_err = Error::from("msg");
-            assert_eq!(
-                Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err),
-                ptr::null_mut()
-            );
-            assert_ne!(local_err, ptr::null_mut());
-            assert_eq!(error_get_pretty(local_err), c"msg");
-            bindings::error_free(local_err);
-        }
-    }
-
-    #[test]
-    fn test_err_or_unit() {
-        unsafe {
-            let result = Error::err_or_unit(ptr::null_mut());
-            assert_match!(result, Ok(()));
-
-            let err = error_for_test(c"msg");
-            let err = Error::err_or_unit(err.into_inner()).unwrap_err();
-            assert_eq!(&*format!("{err}"), "msg");
-        }
-    }
-}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 6232e39e71..54b252fb2c 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -15,19 +15,13 @@ pub mod prelude;
 
 pub mod cell;
 pub mod chardev;
-pub mod error;
 pub mod irq;
-pub mod log;
 pub mod memory;
-pub mod module;
 pub mod qdev;
 pub mod qom;
 pub mod sysbus;
-pub mod timer;
 pub mod vmstate;
 
 // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
 // crate).
 extern crate self as qemu_api;
-
-pub use error::{Error, Result};
diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs
deleted file mode 100644
index d07f6272dc..0000000000
--- a/rust/qemu-api/src/log.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2025 Bernhard Beschow <shentey@gmail.com>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-//! Bindings for QEMU's logging infrastructure
-
-use std::{
-    io::{self, Write},
-    ptr::NonNull,
-};
-
-use common::errno;
-
-use crate::bindings;
-
-#[repr(u32)]
-/// Represents specific error categories within QEMU's logging system.
-///
-/// The `Log` enum provides a Rust abstraction for logging errors, corresponding
-/// to a subset of the error categories defined in the C implementation.
-pub enum Log {
-    /// Log invalid access caused by the guest.
-    /// Corresponds to `LOG_GUEST_ERROR` in the C implementation.
-    GuestError = bindings::LOG_GUEST_ERROR,
-
-    /// Log guest access of unimplemented functionality.
-    /// Corresponds to `LOG_UNIMP` in the C implementation.
-    Unimp = bindings::LOG_UNIMP,
-}
-
-/// A RAII guard for QEMU's logging infrastructure.  Creating the guard
-/// locks the log file, and dropping it (letting it go out of scope) unlocks
-/// the file.
-///
-/// As long as the guard lives, it can be written to using [`std::io::Write`].
-///
-/// The locking is recursive, therefore owning a guard does not prevent
-/// using [`log_mask_ln!()`](crate::log_mask_ln).
-pub struct LogGuard(NonNull<bindings::FILE>);
-
-impl LogGuard {
-    /// Return a RAII guard that writes to QEMU's logging infrastructure.
-    /// The log file is locked while the guard exists, ensuring that there
-    /// is no tearing of the messages.
-    ///
-    /// Return `None` if the log file is closed and could not be opened.
-    /// Do *not* use `unwrap()` on the result; failure can be handled simply
-    /// by not logging anything.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use qemu_api::log::LogGuard;
-    /// # use std::io::Write;
-    /// if let Some(mut log) = LogGuard::new() {
-    ///     writeln!(log, "test");
-    /// }
-    /// ```
-    pub fn new() -> Option<Self> {
-        let f = unsafe { bindings::qemu_log_trylock() }.cast();
-        NonNull::new(f).map(Self)
-    }
-
-    /// Writes a formatted string into the log, returning any error encountered.
-    ///
-    /// This method is primarily used by the
-    /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it
-    /// to be called explicitly.  It is public because it is the only way to
-    /// examine the error, which `log_mask_ln!()` ignores
-    ///
-    /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end.
-    pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> {
-        if let Some(mut log) = Self::new() {
-            log.write_fmt(args)?;
-        }
-        Ok(())
-    }
-}
-
-impl Write for LogGuard {
-    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
-        let ret = unsafe {
-            bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr())
-        };
-        errno::into_io_result(ret)
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        // Do nothing, dropping the guard takes care of flushing
-        Ok(())
-    }
-}
-
-impl Drop for LogGuard {
-    fn drop(&mut self) {
-        unsafe {
-            bindings::qemu_log_unlock(self.0.as_ptr());
-        }
-    }
-}
-
-/// A macro to log messages conditionally based on a provided mask.
-///
-/// The `log_mask_ln` macro checks whether the given mask matches the current
-/// log level and, if so, formats and logs the message. It is the Rust
-/// counterpart of the `qemu_log_mask()` macro in the C implementation.
-///
-/// Errors from writing to the log are ignored.
-///
-/// # Parameters
-///
-/// - `$mask`: A log level mask. This should be a variant of the `Log` enum.
-/// - `$fmt`: A format string following the syntax and rules of the `format!`
-///   macro. It specifies the structure of the log message.
-/// - `$args`: Optional arguments to be interpolated into the format string.
-///
-/// # Example
-///
-/// ```
-/// use qemu_api::{log::Log, log_mask_ln};
-///
-/// let error_address = 0xbad;
-/// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range");
-/// ```
-///
-/// It is also possible to use printf-style formatting, as well as having a
-/// trailing `,`:
-///
-/// ```
-/// use qemu_api::{log::Log, log_mask_ln};
-///
-/// let error_address = 0xbad;
-/// log_mask_ln!(
-///     Log::GuestError,
-///     "Address 0x{:x} out of range",
-///     error_address,
-/// );
-/// ```
-#[macro_export]
-macro_rules! log_mask_ln {
-    ($mask:expr, $fmt:tt $($args:tt)*) => {{
-        // Type assertion to enforce type `Log` for $mask
-        let _: Log = $mask;
-
-        if unsafe {
-            (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0
-        } {
-            _ = ::qemu_api::log::LogGuard::log_fmt(
-                format_args!("{}\n", format_args!($fmt $($args)*)));
-        }
-    }};
-}
diff --git a/rust/qemu-api/src/module.rs b/rust/qemu-api/src/module.rs
deleted file mode 100644
index fa5cea3598..0000000000
--- a/rust/qemu-api/src/module.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-//! Macro to register blocks of code that run as QEMU starts up.
-
-#[macro_export]
-macro_rules! module_init {
-    ($type:ident => $body:block) => {
-        const _: () = {
-            #[used]
-            #[cfg_attr(
-                not(any(target_vendor = "apple", target_os = "windows")),
-                link_section = ".init_array"
-            )]
-            #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
-            #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
-            pub static LOAD_MODULE: extern "C" fn() = {
-                extern "C" fn init_fn() {
-                    $body
-                }
-
-                extern "C" fn ctor_fn() {
-                    unsafe {
-                        $crate::bindings::register_module_init(
-                            Some(init_fn),
-                            $crate::bindings::module_init_type::$type,
-                        );
-                    }
-                }
-
-                ctor_fn
-            };
-        };
-    };
-
-    // shortcut because it's quite common that $body needs unsafe {}
-    ($type:ident => unsafe $body:block) => {
-        $crate::module_init! {
-            $type => { unsafe { $body } }
-        }
-    };
-}
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 8be125fae4..d87479ce13 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -11,12 +11,12 @@ use std::{
 
 pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
 use common::{callbacks::FnCall, Opaque};
+use util::{Error, Result};
 
 use crate::{
     bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
     cell::bql_locked,
     chardev::Chardev,
-    error::{Error, Result},
     impl_vmstate_c_struct,
     irq::InterruptSource,
     prelude::*,
@@ -183,7 +183,7 @@ pub trait DeviceImpl:
 /// readable/writeable from one thread at any time.
 unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(
     dev: *mut bindings::DeviceState,
-    errp: *mut *mut bindings::Error,
+    errp: *mut *mut util::bindings::Error,
 ) {
     let state = NonNull::new(dev).unwrap().cast::<T>();
     let result = T::REALIZE.unwrap()(unsafe { state.as_ref() });
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index 4a5b4cbbf6..2dbfc31dbd 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -114,7 +114,7 @@ where
         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/timer.rs b/rust/qemu-api/src/timer.rs
deleted file mode 100644
index 383e1a6e77..0000000000
--- a/rust/qemu-api/src/timer.rs
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (C) 2024 Intel Corporation.
-// Author(s): Zhao Liu <zhao1.liu@intel.com>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-use std::{
-    ffi::{c_int, c_void},
-    pin::Pin,
-};
-
-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`].
-#[repr(transparent)]
-#[derive(Debug, qemu_api_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)]
-pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);
-
-unsafe impl Send for TimerListGroup {}
-unsafe impl Sync for TimerListGroup {}
-
-impl Timer {
-    pub const MS: u32 = bindings::SCALE_MS;
-    pub const US: u32 = bindings::SCALE_US;
-    pub const NS: u32 = bindings::SCALE_NS;
-
-    /// Create a `Timer` struct without initializing it.
-    ///
-    /// # Safety
-    ///
-    /// The timer must be initialized before it is armed with
-    /// [`modify`](Self::modify).
-    pub const unsafe fn new() -> Self {
-        // SAFETY: requirements relayed to callers of Timer::new
-        Self(unsafe { Opaque::zeroed() })
-    }
-
-    /// Create a new timer with the given attributes.
-    pub fn init_full<'timer, 'opaque: 'timer, T, F>(
-        self: Pin<&'timer mut Self>,
-        timer_list_group: Option<&TimerListGroup>,
-        clk_type: ClockType,
-        scale: u32,
-        attributes: u32,
-        _cb: F,
-        opaque: &'opaque T,
-    ) where
-        F: for<'a> FnCall<(&'a T,)>,
-    {
-        const { assert!(F::IS_SOME) };
-
-        /// timer expiration callback
-        unsafe extern "C" fn rust_timer_handler<T, F: for<'a> FnCall<(&'a T,)>>(
-            opaque: *mut c_void,
-        ) {
-            // SAFETY: the opaque was passed as a reference to `T`.
-            F::call((unsafe { &*(opaque.cast::<T>()) },))
-        }
-
-        let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::<T, F>;
-
-        // SAFETY: the opaque outlives the timer
-        unsafe {
-            timer_init_full(
-                self.as_mut_ptr(),
-                if let Some(g) = timer_list_group {
-                    g as *const TimerListGroup as *mut _
-                } else {
-                    ::core::ptr::null_mut()
-                },
-                clk_type.id,
-                scale as c_int,
-                attributes as c_int,
-                Some(timer_cb),
-                (opaque as *const T).cast::<c_void>().cast_mut(),
-            )
-        }
-    }
-
-    pub fn modify(&self, expire_time: u64) {
-        // SAFETY: the only way to obtain a Timer safely is via methods that
-        // take a Pin<&mut Self>, therefore the timer is pinned
-        unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) }
-    }
-
-    pub fn delete(&self) {
-        // SAFETY: the only way to obtain a Timer safely is via methods that
-        // take a Pin<&mut Self>, therefore the timer is pinned
-        unsafe { timer_del(self.as_mut_ptr()) }
-    }
-}
-
-// FIXME: use something like PinnedDrop from the pinned_init crate
-impl Drop for Timer {
-    fn drop(&mut self) {
-        self.delete()
-    }
-}
-
-pub struct ClockType {
-    id: QEMUClockType,
-}
-
-impl ClockType {
-    pub fn get_ns(&self) -> u64 {
-        // SAFETY: cannot be created outside this module, therefore id
-        // is valid
-        (unsafe { qemu_clock_get_ns(self.id) }) as u64
-    }
-}
-
-pub const CLOCK_VIRTUAL: ClockType = ClockType {
-    id: QEMUClockType::QEMU_CLOCK_VIRTUAL,
-};
-
-pub const NANOSECONDS_PER_SECOND: u64 = 1000000000;
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 06aac3a73f..37e47cc4c6 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -315,7 +315,7 @@ impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8);
 impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16);
 impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
 impl_vmstate_scalar!(vmstate_info_uint64, u64);
-impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer);
+impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer);
 
 #[macro_export]
 macro_rules! impl_vmstate_c_struct {
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 1349568741..70ef4a80d5 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -5,7 +5,6 @@
 use std::{ffi::CStr, ptr::addr_of};
 
 use qemu_api::{
-    bindings::{module_call_init, module_init_type},
     cell::{self, BqlCell},
     prelude::*,
     qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
@@ -13,6 +12,7 @@ use qemu_api::{
     sysbus::SysBusDevice,
     vmstate::{VMStateDescription, VMStateDescriptionBuilder},
 };
+use util::bindings::{module_call_init, module_init_type};
 
 mod vmstate_tests;
 
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"