From f665219b03dda114539c637c50c748a2ebd73c82 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:43 +0200 Subject: rust: remove unused global qemu "allocator" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Link: https://lore.kernel.org/r/20250827104147.717203-3-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/lib.rs | 135 ----------------------------------------------- 1 file changed, 135 deletions(-) (limited to 'rust/qemu-api/src/lib.rs') diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index bcb51c7986..daa2493bb6 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -36,139 +36,4 @@ pub mod zeroable; // crate). extern crate self as qemu_api; -use std::{ - alloc::{GlobalAlloc, Layout}, - ffi::c_void, -}; - pub use error::{Error, Result}; - -#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] -extern "C" { - fn g_aligned_alloc0( - n_blocks: bindings::gsize, - n_block_bytes: bindings::gsize, - alignment: bindings::gsize, - ) -> bindings::gpointer; - fn g_aligned_free(mem: bindings::gpointer); -} - -#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] -extern "C" { - fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void; - fn qemu_vfree(ptr: *mut c_void); -} - -extern "C" { - fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer; - fn g_free(mem: bindings::gpointer); -} - -/// An allocator that uses the same allocator as QEMU in C. -/// -/// It is enabled by default with the `allocator` feature. -/// -/// To set it up manually as a global allocator in your crate: -/// -/// ```ignore -/// use qemu_api::QemuAllocator; -/// -/// #[global_allocator] -/// static GLOBAL: QemuAllocator = QemuAllocator::new(); -/// ``` -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct QemuAllocator { - _unused: [u8; 0], -} - -#[cfg_attr(all(feature = "allocator", not(test)), global_allocator)] -pub static GLOBAL: QemuAllocator = QemuAllocator::new(); - -impl QemuAllocator { - // From the glibc documentation, on GNU systems, malloc guarantees 16-byte - // alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See - // https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html. - // This alignment guarantee also applies to Windows and Android. On Darwin - // and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems. - #[cfg(all( - target_pointer_width = "32", - not(any(target_os = "macos", target_os = "openbsd")) - ))] - pub const DEFAULT_ALIGNMENT_BYTES: Option = Some(8); - #[cfg(all( - target_pointer_width = "64", - not(any(target_os = "macos", target_os = "openbsd")) - ))] - pub const DEFAULT_ALIGNMENT_BYTES: Option = 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 = Some(16); - #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] - pub const DEFAULT_ALIGNMENT_BYTES: Option = 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::() } - } 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::() - } - } - #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] - { - // SAFETY: qemu_memalign() is safe to call. - unsafe { qemu_memalign(layout.align(), layout.size()).cast::() } - } - } - } - - 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::<_>()) } - } - } - } -} -- cgit 1.4.1 From 593c408a6a8cd8b0af9bf60c7c3625da7910a737 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:48 +0200 Subject: rust: split Rust-only "common" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-6-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 10 + rust/Cargo.toml | 1 + rust/common/Cargo.toml | 19 ++ rust/common/meson.build | 34 ++++ rust/common/src/assertions.rs | 148 +++++++++++++++ rust/common/src/bitops.rs | 118 ++++++++++++ rust/common/src/callbacks.rs | 216 +++++++++++++++++++++ rust/common/src/errno.rs | 354 +++++++++++++++++++++++++++++++++++ rust/common/src/lib.rs | 20 ++ rust/common/src/opaque.rs | 238 +++++++++++++++++++++++ rust/common/src/uninit.rs | 85 +++++++++ rust/common/src/zeroable.rs | 18 ++ rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 3 +- rust/hw/char/pl011/src/device.rs | 3 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 3 +- rust/hw/timer/hpet/src/device.rs | 2 +- rust/hw/timer/hpet/src/fw_cfg.rs | 3 +- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 16 +- rust/qemu-api-macros/src/tests.rs | 6 +- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 14 +- rust/qemu-api/src/assertions.rs | 148 --------------- rust/qemu-api/src/bindings.rs | 21 +++ rust/qemu-api/src/bitops.rs | 119 ------------ rust/qemu-api/src/callbacks.rs | 216 --------------------- rust/qemu-api/src/cell.rs | 235 +---------------------- rust/qemu-api/src/chardev.rs | 5 +- rust/qemu-api/src/errno.rs | 354 ----------------------------------- 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/uninit.rs | 85 --------- rust/qemu-api/src/vmstate.rs | 23 +-- rust/qemu-api/src/zeroable.rs | 37 ---- rust/qemu-api/tests/vmstate_tests.rs | 3 +- 46 files changed, 1351 insertions(+), 1262 deletions(-) create mode 100644 rust/common/Cargo.toml create mode 100644 rust/common/meson.build create mode 100644 rust/common/src/assertions.rs create mode 100644 rust/common/src/bitops.rs create mode 100644 rust/common/src/callbacks.rs create mode 100644 rust/common/src/errno.rs create mode 100644 rust/common/src/lib.rs create mode 100644 rust/common/src/opaque.rs create mode 100644 rust/common/src/uninit.rs create mode 100644 rust/common/src/zeroable.rs delete mode 100644 rust/qemu-api/src/assertions.rs delete mode 100644 rust/qemu-api/src/bitops.rs delete mode 100644 rust/qemu-api/src/callbacks.rs delete mode 100644 rust/qemu-api/src/errno.rs delete mode 100644 rust/qemu-api/src/uninit.rs delete mode 100644 rust/qemu-api/src/zeroable.rs (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index fb045388b9..cee5a34206 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3515,6 +3515,7 @@ F: include/hw/registerfields.h Rust M: Manos Pitsidianakis 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 4baf6ba663..71e8c7ed62 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 cd4bf8ef8e..c0426d4243 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "bits", + "common", "qemu-api-macros", "qemu-api", "hw/char/pl011", diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml new file mode 100644 index 0000000000..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/common/src/assertions.rs b/rust/common/src/assertions.rs new file mode 100644 index 0000000000..91f83a5d3d --- /dev/null +++ b/rust/common/src/assertions.rs @@ -0,0 +1,148 @@ +// Copyright 2024, Red Hat Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +#![doc(hidden)] +//! This module provides macros to check the equality of types and +//! the type of `struct` fields. This can be useful to ensure that +//! types match the expectations of C code. +//! +//! Documentation is hidden because it only exposes macros, which +//! are exported directly from `common`. + +// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 +// (stackoverflow answers are released under MIT license). + +#[doc(hidden)] +pub trait EqType { + type Itself; +} + +impl EqType for T { + type Itself = T; +} + +/// Assert that two types are the same. +/// +/// # Examples +/// +/// ``` +/// # use common::assert_same_type; +/// # use std::ops::Deref; +/// assert_same_type!(u32, u32); +/// assert_same_type!( as Deref>::Target, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use common::assert_same_type; +/// assert_same_type!(&Box, &u32); +/// ``` +#[macro_export] +macro_rules! assert_same_type { + ($t1:ty, $t2:ty) => { + const _: () = { + #[allow(unused)] + fn assert_same_type(v: $t1) { + fn types_must_be_equal(_: T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $t2>(v); + } + }; + }; +} + +/// Assert that a field of a struct has the given type. +/// +/// # Examples +/// +/// ``` +/// # use common::assert_field_type; +/// pub struct A { +/// field1: u32, +/// } +/// +/// assert_field_type!(A, field1, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use common::assert_field_type; +/// # pub struct A { field1: u32 } +/// assert_field_type!(A, field1, i32); +/// ``` +#[macro_export] +macro_rules! assert_field_type { + (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { + const _: () = { + #[allow(unused)] + const fn assert_field_type($param_name: &$t) { + const fn types_must_be_equal(_: &T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $ti>(&$($field)*); + } + }; + }; + + ($t:ty, $i:tt, $ti:ty) => { + $crate::assert_field_type!(@internal v, $ti, $t, v.$i); + }; +} + +/// Assert that an expression matches a pattern. This can also be +/// useful to compare enums that do not implement `Eq`. +/// +/// # Examples +/// +/// ``` +/// # use common::assert_match; +/// // JoinHandle does not implement `Eq`, therefore the result +/// // does not either. +/// let result: Result, u32> = Err(42); +/// assert_match!(result, Err(42)); +/// ``` +#[macro_export] +macro_rules! assert_match { + ($a:expr, $b:pat) => { + assert!( + match $a { + $b => true, + _ => false, + }, + "{} = {:?} does not match {}", + stringify!($a), + $a, + stringify!($b) + ); + }; +} + +/// Assert at compile time that an expression is true. This is similar +/// to `const { assert!(...); }` but it works outside functions, as well as +/// on versions of Rust before 1.79. +/// +/// # Examples +/// +/// ``` +/// # use common::static_assert; +/// static_assert!("abc".len() == 3); +/// ``` +/// +/// ```compile_fail +/// # use common::static_assert; +/// static_assert!("abc".len() == 2); // does not compile +/// ``` +#[macro_export] +macro_rules! static_assert { + ($x:expr) => { + const _: () = assert!($x); + }; +} diff --git a/rust/common/src/bitops.rs b/rust/common/src/bitops.rs new file mode 100644 index 0000000000..06c78c3b8a --- /dev/null +++ b/rust/common/src/bitops.rs @@ -0,0 +1,118 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This module provides bit operation extensions to integer types. + +use std::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, +}; + +/// Trait for extensions to integer types +pub trait IntegerExt: + Add + AddAssign + + BitAnd + BitAndAssign + + BitOr + BitOrAssign + + BitXor + BitXorAssign + + Copy + + Div + DivAssign + + Eq + + Mul + MulAssign + + Not + Ord + PartialOrd + + Rem + RemAssign + + Shl + ShlAssign + + Shl + ShlAssign + // add more as needed + Shr + ShrAssign + + Shr + ShrAssign // add more as needed +{ + const BITS: u32; + const MAX: Self; + const MIN: Self; + const ONE: Self; + const ZERO: Self; + + #[inline] + #[must_use] + fn bit(start: u32) -> Self + { + debug_assert!(start < Self::BITS); + + Self::ONE << start + } + + #[inline] + #[must_use] + fn mask(start: u32, length: u32) -> Self + { + /* FIXME: Implement a more elegant check with error handling support? */ + debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start); + + (Self::MAX >> (Self::BITS - length)) << start + } + + #[inline] + #[must_use] + fn deposit(self, start: u32, length: u32, + fieldval: U) -> Self + where Self: From + { + debug_assert!(length <= U::BITS); + + let mask = Self::mask(start, length); + (self & !mask) | ((Self::from(fieldval) << start) & mask) + } + + #[inline] + #[must_use] + fn extract(self, start: u32, length: u32) -> Self + { + let mask = Self::mask(start, length); + (self & mask) >> start + } +} + +macro_rules! impl_num_ext { + ($type:ty) => { + impl IntegerExt for $type { + const BITS: u32 = <$type>::BITS; + const MAX: Self = <$type>::MAX; + const MIN: Self = <$type>::MIN; + const ONE: Self = 1; + const ZERO: Self = 0; + } + }; +} + +impl_num_ext!(u8); +impl_num_ext!(u16); +impl_num_ext!(u32); +impl_num_ext!(u64); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deposit() { + assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15); + assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15); + } + + #[test] + fn test_extract() { + assert_eq!(15u32.extract(2, 4), 3); + } + + #[test] + fn test_bit() { + assert_eq!(u8::bit(7), 128); + assert_eq!(u32::bit(16), 0x10000); + } + + #[test] + fn test_mask() { + assert_eq!(u8::mask(7, 1), 128); + assert_eq!(u32::mask(8, 8), 0xff00); + } +} diff --git a/rust/common/src/callbacks.rs b/rust/common/src/callbacks.rs new file mode 100644 index 0000000000..b8898fe96f --- /dev/null +++ b/rust/common/src/callbacks.rs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT + +//! Utility functions to deal with callbacks from C to Rust. + +use std::{mem, ptr::NonNull}; + +/// Trait for functions (types implementing [`Fn`]) that can be used as +/// callbacks. These include both zero-capture closures and function pointers. +/// +/// In Rust, calling a function through the `Fn` trait normally requires a +/// `self` parameter, even though for zero-sized functions (including function +/// pointers) the type itself contains all necessary information to call the +/// function. This trait provides a `call` function that doesn't require `self`, +/// allowing zero-sized functions to be called using only their type. +/// +/// This enables zero-sized functions to be passed entirely through generic +/// parameters and resolved at compile-time. A typical use is a function +/// receiving an unused parameter of generic type `F` and calling it via +/// `F::call` or passing it to another function via `func::`. +/// +/// QEMU uses this trick to create wrappers to C callbacks. The wrappers +/// are needed to convert an opaque `*mut c_void` into a Rust reference, +/// but they only have a single opaque that they can use. The `FnCall` +/// trait makes it possible to use that opaque for `self` or any other +/// reference: +/// +/// ```ignore +/// // The compiler creates a new `rust_bh_cb` wrapper for each function +/// // passed to `qemu_bh_schedule_oneshot` below. +/// unsafe extern "C" fn rust_bh_cb FnCall<(&'a T,)>>( +/// opaque: *mut c_void, +/// ) { +/// // SAFETY: the opaque was passed as a reference to `T`. +/// F::call((unsafe { &*(opaque.cast::()) }, )) +/// } +/// +/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`. +/// // Using a reference allows usage in const context. +/// fn qemu_bh_schedule_oneshot FnCall<(&'a T,)>>(_f: &F, opaque: &T) { +/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; +/// unsafe { +/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void) +/// } +/// } +/// ``` +/// +/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore +/// compiled to a separate function ("monomorphization"). If you wanted +/// to pass `self` as the opaque value, the generic parameters would be +/// `rust_bh_cb::`. +/// +/// `Args` is a tuple type whose types are the arguments of the function, +/// while `R` is the returned type. +/// +/// # Examples +/// +/// ``` +/// # use common::callbacks::FnCall; +/// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// F::call((s,)) +/// } +/// +/// let s: String = call_it(&str::to_owned, "hello world"); +/// assert_eq!(s, "hello world"); +/// ``` +/// +/// Note that the compiler will produce a different version of `call_it` for +/// each function that is passed to it. Therefore the argument is not really +/// used, except to decide what is `F` and what `F::call` does. +/// +/// Attempting to pass a non-zero-sized closure causes a compile-time failure: +/// +/// ```compile_fail +/// # use common::callbacks::FnCall; +/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { +/// # F::call((s,)) +/// # } +/// let x: &'static str = "goodbye world"; +/// call_it(&move |_| String::from(x), "hello workd"); +/// ``` +/// +/// `()` can be used to indicate "no function": +/// +/// ``` +/// # use common::callbacks::FnCall; +/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { +/// if F::IS_SOME { +/// Some(F::call((s,))) +/// } else { +/// None +/// } +/// } +/// +/// assert!(optional(&(), "hello world").is_none()); +/// ``` +/// +/// Invoking `F::call` will then be a run-time error. +/// +/// ```should_panic +/// # use common::callbacks::FnCall; +/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// # F::call((s,)) +/// # } +/// let s: String = call_it(&(), "hello world"); // panics +/// ``` +/// +/// # Safety +/// +/// Because `Self` is a zero-sized type, all instances of the type are +/// equivalent. However, in addition to this, `Self` must have no invariants +/// that could be violated by creating a reference to it. +/// +/// This is always true for zero-capture closures and function pointers, as long +/// as the code is able to name the function in the first place. +pub unsafe trait FnCall: 'static + Sync + Sized { + /// `true` if `Self` is an actual function type and not `()`. + /// + /// # Examples + /// + /// You can use `IS_SOME` to catch this at compile time: + /// + /// ```compile_fail + /// # use common::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// const { assert!(F::IS_SOME) } + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + const IS_SOME: bool; + + /// `false` if `Self` is an actual function type, `true` if it is `()`. + fn is_none() -> bool { + !Self::IS_SOME + } + + /// `true` if `Self` is an actual function type, `false` if it is `()`. + fn is_some() -> bool { + Self::IS_SOME + } + + /// Call the function with the arguments in args. + fn call(a: Args) -> R; +} + +/// `()` acts as a "null" callback. Using `()` and `function` is nicer +/// than `None` and `Some(function)`, because the compiler is unable to +/// infer the type of just `None`. Therefore, the trait itself acts as the +/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. +unsafe impl FnCall for () { + const IS_SOME: bool = false; + + /// Call the function with the arguments in args. + fn call(_a: Args) -> R { + panic!("callback not specified") + } +} + +macro_rules! impl_call { + ($($args:ident,)* ) => ( + // SAFETY: because each function is treated as a separate type, + // accessing `FnCall` is only possible in code that would be + // allowed to call the function. + unsafe impl FnCall<($($args,)*), R> for F + where + F: 'static + Sync + Sized + Fn($($args, )*) -> R, + { + const IS_SOME: bool = true; + + #[inline(always)] + fn call(a: ($($args,)*)) -> R { + const { assert!(mem::size_of::() == 0) }; + + // SAFETY: the safety of this method is the condition for implementing + // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, + // see https://github.com/rust-lang/libs-team/issues/292. + let f: &'static F = unsafe { &*NonNull::::dangling().as_ptr() }; + let ($($args,)*) = a; + f($($args,)*) + } + } + ) +} + +impl_call!(_1, _2, _3, _4, _5,); +impl_call!(_1, _2, _3, _4,); +impl_call!(_1, _2, _3,); +impl_call!(_1, _2,); +impl_call!(_1,); +impl_call!(); + +#[cfg(test)] +mod tests { + use super::*; + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { + F::call(("hello world",)) + } + + #[test] + fn test_call() { + assert_eq!(do_test_call(&str::to_owned), "hello world") + } + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { + assert!(F::is_some()); + } + + #[test] + fn test_is_some() { + do_test_is_some(&str::to_owned); + } +} diff --git a/rust/common/src/errno.rs b/rust/common/src/errno.rs new file mode 100644 index 0000000000..64b2933b07 --- /dev/null +++ b/rust/common/src/errno.rs @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Utility functions to convert `errno` to and from +//! [`io::Error`]/[`io::Result`] +//! +//! QEMU C functions often have a "positive success/negative `errno`" calling +//! convention. This module provides functions to portably convert an integer +//! into an [`io::Result`] and back. + +use std::{ + convert::{self, TryFrom}, + io::{self, ErrorKind}, +}; + +/// An `errno` value that can be converted into an [`io::Error`] +pub struct Errno(pub u16); + +// On Unix, from_raw_os_error takes an errno value and OS errors +// are printed using strerror. On Windows however it takes a +// GetLastError() value; therefore we need to convert errno values +// into io::Error by hand. This is the same mapping that the +// standard library uses to retrieve the kind of OS errors +// (`std::sys::pal::unix::decode_error_kind`). +impl From for ErrorKind { + fn from(value: Errno) -> ErrorKind { + use ErrorKind::*; + let Errno(errno) = value; + match i32::from(errno) { + libc::EPERM | libc::EACCES => PermissionDenied, + libc::ENOENT => NotFound, + libc::EINTR => Interrupted, + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + libc::ENOMEM => OutOfMemory, + libc::EEXIST => AlreadyExists, + libc::EINVAL => InvalidInput, + libc::EPIPE => BrokenPipe, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::ENOTCONN => NotConnected, + libc::ENOTSUP => Unsupported, + libc::ETIMEDOUT => TimedOut, + _ => Other, + } + } +} + +// This is used on Windows for all io::Errors, but also on Unix if the +// io::Error does not have a raw OS error. This is the reversed +// mapping of the above; EIO is returned for unknown ErrorKinds. +impl From for Errno { + fn from(value: io::ErrorKind) -> Errno { + use ErrorKind::*; + let errno = match value { + // can be both EPERM or EACCES :( pick one + PermissionDenied => libc::EPERM, + NotFound => libc::ENOENT, + Interrupted => libc::EINTR, + WouldBlock => libc::EAGAIN, + OutOfMemory => libc::ENOMEM, + AlreadyExists => libc::EEXIST, + InvalidInput => libc::EINVAL, + BrokenPipe => libc::EPIPE, + AddrInUse => libc::EADDRINUSE, + AddrNotAvailable => libc::EADDRNOTAVAIL, + ConnectionAborted => libc::ECONNABORTED, + ConnectionRefused => libc::ECONNREFUSED, + ConnectionReset => libc::ECONNRESET, + NotConnected => libc::ENOTCONN, + Unsupported => libc::ENOTSUP, + TimedOut => libc::ETIMEDOUT, + _ => libc::EIO, + }; + Errno(errno as u16) + } +} + +impl From for io::Error { + #[cfg(unix)] + fn from(value: Errno) -> io::Error { + let Errno(errno) = value; + io::Error::from_raw_os_error(errno.into()) + } + + #[cfg(windows)] + fn from(value: Errno) -> io::Error { + let error_kind: ErrorKind = value.into(); + error_kind.into() + } +} + +impl From for Errno { + fn from(value: io::Error) -> Errno { + if cfg!(unix) { + if let Some(errno) = value.raw_os_error() { + return Errno(u16::try_from(errno).unwrap()); + } + } + value.kind().into() + } +} + +impl From for Errno { + fn from(_value: convert::Infallible) -> Errno { + panic!("unreachable") + } +} + +/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] +/// for the "right" set of types. +mod traits { + use super::Errno; + + /// A signed type that can be converted into an + /// [`io::Result`](std::io::Result) + pub trait GetErrno { + /// Unsigned variant of `Self`, used as the type for the `Ok` case. + type Out; + + /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative + fn into_errno_result(self) -> Result; + } + + /// A type that can be taken out of an [`io::Result`](std::io::Result) and + /// converted into "positive success/negative `errno`" convention. + pub trait MergeErrno { + /// Signed variant of `Self`, used as the return type of + /// [`into_neg_errno`](super::into_neg_errno). + type Out: From + std::ops::Neg; + + /// Return `self`, asserting that it is in range + fn map_ok(self) -> Self::Out; + } + + macro_rules! get_errno { + ($t:ty, $out:ty) => { + impl GetErrno for $t { + type Out = $out; + fn into_errno_result(self) -> Result { + match self { + 0.. => Ok(self as $out), + -65535..=-1 => Err(Errno(-self as u16)), + _ => panic!("{self} is not a negative errno"), + } + } + } + }; + } + + get_errno!(i32, u32); + get_errno!(i64, u64); + get_errno!(isize, usize); + + macro_rules! merge_errno { + ($t:ty, $out:ty) => { + impl MergeErrno for $t { + type Out = $out; + fn map_ok(self) -> Self::Out { + self.try_into().unwrap() + } + } + }; + } + + merge_errno!(u8, i32); + merge_errno!(u16, i32); + merge_errno!(u32, i32); + merge_errno!(u64, i64); + + impl MergeErrno for () { + type Out = i32; + fn map_ok(self) -> i32 { + 0 + } + } +} + +use traits::{GetErrno, MergeErrno}; + +/// Convert an integer value into a [`io::Result`]. +/// +/// Positive values are turned into an `Ok` result; negative values +/// are interpreted as negated `errno` and turned into an `Err`. +/// +/// ``` +/// # use common::errno::into_io_result; +/// # use std::io::ErrorKind; +/// let ok = into_io_result(1i32).unwrap(); +/// assert_eq!(ok, 1u32); +/// +/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM +/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); +/// ``` +/// +/// # Panics +/// +/// Since the result is an unsigned integer, negative values must +/// be close to 0; values that are too far away are considered +/// likely overflows and will panic: +/// +/// ```should_panic +/// # use common::errno::into_io_result; +/// # #[allow(dead_code)] +/// let err = into_io_result(-0x1234_5678i32); // panic +/// ``` +pub fn into_io_result(value: T) -> io::Result { + value.into_errno_result().map_err(Into::into) +} + +/// Convert a [`Result`] into an integer value, using negative `errno` +/// values to report errors. +/// +/// ``` +/// # use common::errno::into_neg_errno; +/// # use std::io::{self, ErrorKind}; +/// let ok: io::Result<()> = Ok(()); +/// assert_eq!(into_neg_errno(ok), 0); +/// +/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); +/// assert_eq!(into_neg_errno(err), -22); // -EINVAL +/// ``` +/// +/// Since this module also provides the ability to convert [`io::Error`] +/// to an `errno` value, [`io::Result`] is the most commonly used type +/// for the argument of this function: +/// +/// # Panics +/// +/// Since the result is a signed integer, integer `Ok` values must remain +/// positive: +/// +/// ```should_panic +/// # use common::errno::into_neg_errno; +/// # use std::io; +/// let err: io::Result = Ok(0x8899_AABB); +/// into_neg_errno(err) // panic +/// # ; +/// ``` +pub fn into_neg_errno>(value: Result) -> T::Out { + match value { + Ok(x) => x.map_ok(), + Err(err) => -T::Out::from(err.into().0), + } +} + +#[cfg(test)] +mod tests { + use std::io::ErrorKind; + + use super::*; + use crate::assert_match; + + #[test] + pub fn test_from_u8() { + let ok: io::Result<_> = Ok(42u8); + assert_eq!(into_neg_errno(ok), 42); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_from_u16() { + let ok: io::Result<_> = Ok(1234u16); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i32() { + assert_match!(into_io_result(1234i32), Ok(1234)); + + let err = into_io_result(-1i32).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(1)); + assert_match!(err.kind(), ErrorKind::PermissionDenied); + } + + #[test] + pub fn test_from_u32() { + let ok: io::Result<_> = Ok(1234u32); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i64() { + assert_match!(into_io_result(1234i64), Ok(1234)); + + let err = into_io_result(-22i64).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(22)); + assert_match!(err.kind(), ErrorKind::InvalidInput); + } + + #[test] + pub fn test_from_u64() { + let ok: io::Result<_> = Ok(1234u64); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::InvalidInput.into()); + assert_eq!(into_neg_errno(err), -22); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(6)); + assert_eq!(into_neg_errno(os_err), -6); + } + } + + #[test] + pub fn test_isize() { + assert_match!(into_io_result(1234isize), Ok(1234)); + + let err = into_io_result(-4isize).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(4)); + assert_match!(err.kind(), ErrorKind::Interrupted); + } + + #[test] + pub fn test_from_unit() { + let ok: io::Result<_> = Ok(()); + assert_eq!(into_neg_errno(ok), 0); + + let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); + assert_eq!(into_neg_errno(err), -12); + + if cfg!(unix) { + let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); + assert_eq!(into_neg_errno(os_err), -2); + } + } +} 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..d25a5f3ae1 --- /dev/null +++ b/rust/common/src/opaque.rs @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: MIT + +//! ## Opaque wrappers +//! +//! The cell types from the previous section are useful at the boundaries +//! of code that requires interior mutability. When writing glue code that +//! interacts directly with C structs, however, it is useful to operate +//! at a lower level. +//! +//! C functions often violate Rust's fundamental assumptions about memory +//! safety by modifying memory even if it is shared. Furthermore, C structs +//! often start their life uninitialized and may be populated lazily. +//! +//! For this reason, this module provides the [`Opaque`] 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::::uninit(); +//! qemu_struct_init(state.as_mut_ptr()); +//! } +//! ``` +//! +//! [`Opaque`] will usually be wrapped one level further, so that +//! bridge methods can be added to the wrapper: +//! +//! ```ignore +//! pub struct MyStruct(Opaque); +//! +//! impl MyStruct { +//! fn new() -> Pin> { +//! 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`] 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`] 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`] 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` 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` is `#[repr(transparent)]`, so that it matches the memory layout +/// of `T`. +#[repr(transparent)] +pub struct Opaque { + value: UnsafeCell>, + // PhantomPinned also allows multiple references to the `Opaque`, i.e. + // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; + // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. + _pin: PhantomPinned, +} + +impl Opaque { + /// 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::(); + // SAFETY: Self is a transparent wrapper over T + unsafe { ptr.as_ref() } + } + + /// Creates a new opaque object with uninitialized contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be initialized and pinned before + /// calling them. + pub const unsafe fn uninit() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::uninit()), + _pin: PhantomPinned, + } + } + + /// Creates a new opaque object with zeroed contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned (and possibly initialized) + /// before calling them. + pub const unsafe fn zeroed() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::zeroed()), + _pin: PhantomPinned, + } + } + + /// Returns a raw mutable pointer to the opaque data. + pub const fn as_mut_ptr(&self) -> *mut T { + UnsafeCell::get(&self.value).cast() + } + + /// Returns a raw pointer to the opaque data. + pub const fn as_ptr(&self) -> *const T { + self.as_mut_ptr().cast_const() + } + + /// Returns a raw pointer to the opaque data that can be passed to a + /// C function as `void *`. + pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { + UnsafeCell::get(&self.value).cast() + } + + /// Converts a raw pointer to the wrapped type. + pub const fn raw_get(slot: *mut Self) -> *mut T { + // Compare with Linux's raw_get method, which goes through an UnsafeCell + // because it takes a *const Self instead. + slot.cast() + } +} + +impl fmt::Debug for Opaque { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut name: String = "Opaque<".to_string(); + name += std::any::type_name::(); + name += ">"; + f.debug_tuple(&name).field(&self.as_ptr()).finish() + } +} + +impl Opaque { + /// 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, +/// } +/// +/// 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 Wrapper for Opaque { + type Wrapped = T; +} diff --git a/rust/common/src/uninit.rs b/rust/common/src/uninit.rs new file mode 100644 index 0000000000..e7f9fcd2e3 --- /dev/null +++ b/rust/common/src/uninit.rs @@ -0,0 +1,85 @@ +//! Access fields of a [`MaybeUninit`] + +use std::{ + mem::MaybeUninit, + ops::{Deref, DerefMut}, +}; + +pub struct MaybeUninitField<'a, T, U> { + parent: &'a mut MaybeUninit, + child: *mut U, +} + +impl<'a, T, U> MaybeUninitField<'a, T, U> { + #[doc(hidden)] + pub const fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { + MaybeUninitField { parent, child } + } + + /// Return a constant pointer to the containing object of the field. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub const fn parent(f: &Self) -> *const T { + f.parent.as_ptr() + } + + /// Return a mutable pointer to the containing object. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub const fn parent_mut(f: &mut Self) -> *mut T { + f.parent.as_mut_ptr() + } +} + +impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { + type Target = MaybeUninit; + + fn deref(&self) -> &MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &*(self.child.cast()) } + } +} + +impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { + fn deref_mut(&mut self) -> &mut MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &mut *(self.child.cast()) } + } +} + +/// ``` +/// #[derive(Debug)] +/// struct S { +/// x: u32, +/// y: u32, +/// } +/// +/// # use std::mem::MaybeUninit; +/// # use common::{assert_match, uninit_field_mut}; +/// +/// let mut s: MaybeUninit = MaybeUninit::zeroed(); +/// uninit_field_mut!(s, x).write(5); +/// let s = unsafe { s.assume_init() }; +/// assert_match!(s, S { x: 5, y: 0 }); +/// ``` +#[macro_export] +macro_rules! uninit_field_mut { + ($container:expr, $($field:tt)+) => {{ + let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container; + let container_ptr__ = container__.as_mut_ptr(); + + // SAFETY: the container is not used directly, only through a MaybeUninit<>, + // so the safety is delegated to the caller and to final invocation of + // assume_init() + let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) }; + $crate::uninit::MaybeUninitField::new(container__, target__) + }}; +} 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::::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` 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::::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..c4a9f531f7 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -7,7 +7,8 @@ _libpl011_rs = static_library( bilge_rs, bilge_impl_rs, bits_rs, - qemu_api, + common_rs, + qemu_api_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 8411db8d00..b4aa6c45f8 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -4,6 +4,7 @@ use std::{ffi::CStr, mem::size_of}; +use common::{static_assert, uninit_field_mut}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, impl_vmstate_struct, @@ -14,9 +15,7 @@ use qemu_api::{ prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField, ParentInit}, - static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, - uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 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..c91d330439 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -4,7 +4,8 @@ _libhpet_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', dependencies: [ - qemu_api, + common_rs, + qemu_api_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index dd5326a40d..72375d3155 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -10,6 +10,7 @@ use std::{ slice::from_ref, }; +use common::{bitops::IntegerExt, uninit_field_mut}; use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, @@ -27,7 +28,6 @@ use qemu_api::{ qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, - uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index 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 a65a7ce2fe..49003a4780 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -97,7 +97,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); ::qemu_api::module_init! { @@ -125,20 +125,20 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result::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 ::Wrapped) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); unsafe { ptr.as_ref() } } - pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { self.0.as_mut_ptr() } - pub const fn as_ptr(&self) -> *const ::Wrapped { + pub const fn as_ptr(&self) -> *const ::Wrapped { self.0.as_ptr() } @@ -146,7 +146,7 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result *mut ::Wrapped { + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { slot.cast() } } @@ -282,7 +282,7 @@ fn derive_device_or_error(input: DeriveInput) -> Result::ParentType> diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index c5ed78035b..2e0e204491 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -14,6 +14,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +common = { path = "../common" } qemu_api_macros = { path = "../qemu-api-macros" } anyhow = { workspace = true } foreign = { workspace = true } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 062009f161..64af3caef5 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -52,13 +52,9 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', - 'src/assertions.rs', 'src/bindings.rs', - 'src/bitops.rs', - 'src/callbacks.rs', 'src/cell.rs', 'src/chardev.rs', - 'src/errno.rs', 'src/error.rs', 'src/irq.rs', 'src/log.rs', @@ -69,23 +65,21 @@ _qemu_api_rs = static_library( 'src/qom.rs', 'src/sysbus.rs', 'src/timer.rs', - 'src/uninit.rs', 'src/vmstate.rs', - 'src/zeroable.rs', ], {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, + dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, qom, hwcore, chardev, migration], ) rust.test('rust-qemu-api-tests', _qemu_api_rs, suite: ['unit', 'rust']) -qemu_api = declare_dependency(link_with: [_qemu_api_rs], +qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) # Doctests are essentially integration tests, so they need the same dependencies. @@ -94,7 +88,7 @@ qemu_api = declare_dependency(link_with: [_qemu_api_rs], rust.doctest('rust-qemu-api-doctests', _qemu_api_rs, protocol: 'rust', - dependencies: qemu_api, + dependencies: [qemu_api_rs], suite: ['doc', 'rust']) test('rust-qemu-api-integration', @@ -104,7 +98,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [qemu_api]), + dependencies: [common_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs deleted file mode 100644 index e4fe23b674..0000000000 --- a/rust/qemu-api/src/assertions.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2024, Red Hat Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -#![doc(hidden)] -//! This module provides macros to check the equality of types and -//! the type of `struct` fields. This can be useful to ensure that -//! types match the expectations of C code. -//! -//! Documentation is hidden because it only exposes macros, which -//! are exported directly from `qemu_api`. - -// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 -// (stackoverflow answers are released under MIT license). - -#[doc(hidden)] -pub trait EqType { - type Itself; -} - -impl EqType for T { - type Itself = T; -} - -/// Assert that two types are the same. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::assert_same_type; -/// # use std::ops::Deref; -/// assert_same_type!(u32, u32); -/// assert_same_type!( as Deref>::Target, u32); -/// ``` -/// -/// Different types will cause a compile failure -/// -/// ```compile_fail -/// # use qemu_api::assert_same_type; -/// assert_same_type!(&Box, &u32); -/// ``` -#[macro_export] -macro_rules! assert_same_type { - ($t1:ty, $t2:ty) => { - const _: () = { - #[allow(unused)] - fn assert_same_type(v: $t1) { - fn types_must_be_equal(_: T) - where - T: $crate::assertions::EqType, - { - } - types_must_be_equal::<_, $t2>(v); - } - }; - }; -} - -/// Assert that a field of a struct has the given type. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::assert_field_type; -/// pub struct A { -/// field1: u32, -/// } -/// -/// assert_field_type!(A, field1, u32); -/// ``` -/// -/// Different types will cause a compile failure -/// -/// ```compile_fail -/// # use qemu_api::assert_field_type; -/// # pub struct A { field1: u32 } -/// assert_field_type!(A, field1, i32); -/// ``` -#[macro_export] -macro_rules! assert_field_type { - (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { - const _: () = { - #[allow(unused)] - const fn assert_field_type($param_name: &$t) { - const fn types_must_be_equal(_: &T) - where - T: $crate::assertions::EqType, - { - } - types_must_be_equal::<_, $ti>(&$($field)*); - } - }; - }; - - ($t:ty, $i:tt, $ti:ty) => { - $crate::assert_field_type!(@internal v, $ti, $t, v.$i); - }; -} - -/// Assert that an expression matches a pattern. This can also be -/// useful to compare enums that do not implement `Eq`. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::assert_match; -/// // JoinHandle does not implement `Eq`, therefore the result -/// // does not either. -/// let result: Result, u32> = Err(42); -/// assert_match!(result, Err(42)); -/// ``` -#[macro_export] -macro_rules! assert_match { - ($a:expr, $b:pat) => { - assert!( - match $a { - $b => true, - _ => false, - }, - "{} = {:?} does not match {}", - stringify!($a), - $a, - stringify!($b) - ); - }; -} - -/// Assert at compile time that an expression is true. This is similar -/// to `const { assert!(...); }` but it works outside functions, as well as -/// on versions of Rust before 1.79. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::static_assert; -/// static_assert!("abc".len() == 3); -/// ``` -/// -/// ```compile_fail -/// # use qemu_api::static_assert; -/// static_assert!("abc".len() == 2); // does not compile -/// ``` -#[macro_export] -macro_rules! static_assert { - ($x:expr) => { - const _: () = assert!($x); - }; -} 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/bitops.rs b/rust/qemu-api/src/bitops.rs deleted file mode 100644 index b1e3a530ab..0000000000 --- a/rust/qemu-api/src/bitops.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// 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, - Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, -}; - -/// Trait for extensions to integer types -pub trait IntegerExt: - Add + AddAssign + - BitAnd + BitAndAssign + - BitOr + BitOrAssign + - BitXor + BitXorAssign + - Copy + - Div + DivAssign + - Eq + - Mul + MulAssign + - Not + Ord + PartialOrd + - Rem + RemAssign + - Shl + ShlAssign + - Shl + ShlAssign + // add more as needed - Shr + ShrAssign + - Shr + ShrAssign // add more as needed -{ - const BITS: u32; - const MAX: Self; - const MIN: Self; - const ONE: Self; - const ZERO: Self; - - #[inline] - #[must_use] - fn bit(start: u32) -> Self - { - debug_assert!(start < Self::BITS); - - Self::ONE << start - } - - #[inline] - #[must_use] - fn mask(start: u32, length: u32) -> Self - { - /* FIXME: Implement a more elegant check with error handling support? */ - debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start); - - (Self::MAX >> (Self::BITS - length)) << start - } - - #[inline] - #[must_use] - fn deposit(self, start: u32, length: u32, - fieldval: U) -> Self - where Self: From - { - debug_assert!(length <= U::BITS); - - let mask = Self::mask(start, length); - (self & !mask) | ((Self::from(fieldval) << start) & mask) - } - - #[inline] - #[must_use] - fn extract(self, start: u32, length: u32) -> Self - { - let mask = Self::mask(start, length); - (self & mask) >> start - } -} - -macro_rules! impl_num_ext { - ($type:ty) => { - impl IntegerExt for $type { - const BITS: u32 = <$type>::BITS; - const MAX: Self = <$type>::MAX; - const MIN: Self = <$type>::MIN; - const ONE: Self = 1; - const ZERO: Self = 0; - } - }; -} - -impl_num_ext!(u8); -impl_num_ext!(u16); -impl_num_ext!(u32); -impl_num_ext!(u64); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_deposit() { - assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15); - assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15); - } - - #[test] - fn test_extract() { - assert_eq!(15u32.extract(2, 4), 3); - } - - #[test] - fn test_bit() { - assert_eq!(u8::bit(7), 128); - assert_eq!(u32::bit(16), 0x10000); - } - - #[test] - fn test_mask() { - assert_eq!(u8::mask(7, 1), 128); - assert_eq!(u32::mask(8, 8), 0xff00); - } -} diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs deleted file mode 100644 index dbe2305f50..0000000000 --- a/rust/qemu-api/src/callbacks.rs +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: MIT - -//! Utility functions to deal with callbacks from C to Rust. - -use std::{mem, ptr::NonNull}; - -/// Trait for functions (types implementing [`Fn`]) that can be used as -/// callbacks. These include both zero-capture closures and function pointers. -/// -/// In Rust, calling a function through the `Fn` trait normally requires a -/// `self` parameter, even though for zero-sized functions (including function -/// pointers) the type itself contains all necessary information to call the -/// function. This trait provides a `call` function that doesn't require `self`, -/// allowing zero-sized functions to be called using only their type. -/// -/// This enables zero-sized functions to be passed entirely through generic -/// parameters and resolved at compile-time. A typical use is a function -/// receiving an unused parameter of generic type `F` and calling it via -/// `F::call` or passing it to another function via `func::`. -/// -/// QEMU uses this trick to create wrappers to C callbacks. The wrappers -/// are needed to convert an opaque `*mut c_void` into a Rust reference, -/// but they only have a single opaque that they can use. The `FnCall` -/// trait makes it possible to use that opaque for `self` or any other -/// reference: -/// -/// ```ignore -/// // The compiler creates a new `rust_bh_cb` wrapper for each function -/// // passed to `qemu_bh_schedule_oneshot` below. -/// unsafe extern "C" fn rust_bh_cb FnCall<(&'a T,)>>( -/// opaque: *mut c_void, -/// ) { -/// // SAFETY: the opaque was passed as a reference to `T`. -/// F::call((unsafe { &*(opaque.cast::()) }, )) -/// } -/// -/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`. -/// // Using a reference allows usage in const context. -/// fn qemu_bh_schedule_oneshot FnCall<(&'a T,)>>(_f: &F, opaque: &T) { -/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; -/// unsafe { -/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void) -/// } -/// } -/// ``` -/// -/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore -/// compiled to a separate function ("monomorphization"). If you wanted -/// to pass `self` as the opaque value, the generic parameters would be -/// `rust_bh_cb::`. -/// -/// `Args` is a tuple type whose types are the arguments of the function, -/// while `R` is the returned type. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::callbacks::FnCall; -/// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { -/// F::call((s,)) -/// } -/// -/// let s: String = call_it(&str::to_owned, "hello world"); -/// assert_eq!(s, "hello world"); -/// ``` -/// -/// Note that the compiler will produce a different version of `call_it` for -/// each function that is passed to it. Therefore the argument is not really -/// used, except to decide what is `F` and what `F::call` does. -/// -/// Attempting to pass a non-zero-sized closure causes a compile-time failure: -/// -/// ```compile_fail -/// # use qemu_api::callbacks::FnCall; -/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { -/// # F::call((s,)) -/// # } -/// let x: &'static str = "goodbye world"; -/// call_it(&move |_| String::from(x), "hello workd"); -/// ``` -/// -/// `()` can be used to indicate "no function": -/// -/// ``` -/// # use qemu_api::callbacks::FnCall; -/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { -/// if F::IS_SOME { -/// Some(F::call((s,))) -/// } else { -/// None -/// } -/// } -/// -/// assert!(optional(&(), "hello world").is_none()); -/// ``` -/// -/// Invoking `F::call` will then be a run-time error. -/// -/// ```should_panic -/// # use qemu_api::callbacks::FnCall; -/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { -/// # F::call((s,)) -/// # } -/// let s: String = call_it(&(), "hello world"); // panics -/// ``` -/// -/// # Safety -/// -/// Because `Self` is a zero-sized type, all instances of the type are -/// equivalent. However, in addition to this, `Self` must have no invariants -/// that could be violated by creating a reference to it. -/// -/// This is always true for zero-capture closures and function pointers, as long -/// as the code is able to name the function in the first place. -pub unsafe trait FnCall: 'static + Sync + Sized { - /// `true` if `Self` is an actual function type and not `()`. - /// - /// # Examples - /// - /// You can use `IS_SOME` to catch this at compile time: - /// - /// ```compile_fail - /// # use qemu_api::callbacks::FnCall; - /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { - /// const { assert!(F::IS_SOME) } - /// F::call((s,)) - /// } - /// - /// let s: String = call_it((), "hello world"); // does not compile - /// ``` - const IS_SOME: bool; - - /// `false` if `Self` is an actual function type, `true` if it is `()`. - fn is_none() -> bool { - !Self::IS_SOME - } - - /// `true` if `Self` is an actual function type, `false` if it is `()`. - fn is_some() -> bool { - Self::IS_SOME - } - - /// Call the function with the arguments in args. - fn call(a: Args) -> R; -} - -/// `()` acts as a "null" callback. Using `()` and `function` is nicer -/// than `None` and `Some(function)`, because the compiler is unable to -/// infer the type of just `None`. Therefore, the trait itself acts as the -/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. -unsafe impl FnCall for () { - const IS_SOME: bool = false; - - /// Call the function with the arguments in args. - fn call(_a: Args) -> R { - panic!("callback not specified") - } -} - -macro_rules! impl_call { - ($($args:ident,)* ) => ( - // SAFETY: because each function is treated as a separate type, - // accessing `FnCall` is only possible in code that would be - // allowed to call the function. - unsafe impl FnCall<($($args,)*), R> for F - where - F: 'static + Sync + Sized + Fn($($args, )*) -> R, - { - const IS_SOME: bool = true; - - #[inline(always)] - fn call(a: ($($args,)*)) -> R { - const { assert!(mem::size_of::() == 0) }; - - // SAFETY: the safety of this method is the condition for implementing - // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, - // see https://github.com/rust-lang/libs-team/issues/292. - let f: &'static F = unsafe { &*NonNull::::dangling().as_ptr() }; - let ($($args,)*) = a; - f($($args,)*) - } - } - ) -} - -impl_call!(_1, _2, _3, _4, _5,); -impl_call!(_1, _2, _3, _4,); -impl_call!(_1, _2, _3,); -impl_call!(_1, _2,); -impl_call!(_1,); -impl_call!(); - -#[cfg(test)] -mod tests { - use super::*; - - // The `_f` parameter is unused but it helps the compiler infer `F`. - fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { - F::call(("hello world",)) - } - - #[test] - fn test_call() { - assert_eq!(do_test_call(&str::to_owned), "hello world") - } - - // The `_f` parameter is unused but it helps the compiler infer `F`. - fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { - assert!(F::is_some()); - } - - #[test] - fn test_is_some() { - do_test_is_some(&str::to_owned); - } -} diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 9943d7286b..d13848df20 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`] 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::::uninit(); -//! qemu_struct_init(state.as_mut_ptr()); -//! } -//! ``` -//! -//! [`Opaque`] will usually be wrapped one level further, so that -//! bridge methods can be added to the wrapper: -//! -//! ```ignore -//! pub struct MyStruct(Opaque); -//! -//! impl MyStruct { -//! fn new() -> Pin> { -//! 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`] 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`] 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`] is necessary for C interop, it should be used sparingly -//! and only at FFI boundaries. For QEMU-specific types that need interior -//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. use std::{ cell::{Cell, UnsafeCell}, cmp::Ordering, fmt, - marker::{PhantomData, PhantomPinned}, - mem::{self, MaybeUninit}, + marker::PhantomData, + mem, ops::{Deref, DerefMut}, ptr::NonNull, }; @@ -939,165 +870,3 @@ impl fmt::Display for BqlRefMut<'_, T> { (**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` 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` is `#[repr(transparent)]`, so that it matches the memory layout -/// of `T`. -#[repr(transparent)] -pub struct Opaque { - value: UnsafeCell>, - // PhantomPinned also allows multiple references to the `Opaque`, i.e. - // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; - // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. - _pin: PhantomPinned, -} - -impl Opaque { - /// 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::(); - // SAFETY: Self is a transparent wrapper over T - unsafe { ptr.as_ref() } - } - - /// Creates a new opaque object with uninitialized contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be initialized and pinned before - /// calling them. - pub const unsafe fn uninit() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::uninit()), - _pin: PhantomPinned, - } - } - - /// Creates a new opaque object with zeroed contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be pinned (and possibly initialized) - /// before calling them. - pub const unsafe fn zeroed() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::zeroed()), - _pin: PhantomPinned, - } - } - - /// Returns a raw mutable pointer to the opaque data. - pub const fn as_mut_ptr(&self) -> *mut T { - UnsafeCell::get(&self.value).cast() - } - - /// Returns a raw pointer to the opaque data. - pub const fn as_ptr(&self) -> *const T { - self.as_mut_ptr().cast_const() - } - - /// Returns a raw pointer to the opaque data that can be passed to a - /// C function as `void *`. - pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { - UnsafeCell::get(&self.value).cast() - } - - /// Converts a raw pointer to the wrapped type. - pub const fn raw_get(slot: *mut Self) -> *mut T { - // Compare with Linux's raw_get method, which goes through an UnsafeCell - // because it takes a *const Self instead. - slot.cast() - } -} - -impl fmt::Debug for Opaque { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut name: String = "Opaque<".to_string(); - name += std::any::type_name::(); - name += ">"; - f.debug_tuple(&name).field(&self.as_ptr()).finish() - } -} - -impl Opaque { - /// 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, -/// } -/// -/// 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 Wrapper for Opaque { - type Wrapped = T; -} diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index cb27be5256..5a351dcecb 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -18,10 +18,11 @@ use std::{ 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/errno.rs b/rust/qemu-api/src/errno.rs deleted file mode 100644 index 507850fe33..0000000000 --- a/rust/qemu-api/src/errno.rs +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Utility functions to convert `errno` to and from -//! [`io::Error`]/[`io::Result`] -//! -//! QEMU C functions often have a "positive success/negative `errno`" calling -//! convention. This module provides functions to portably convert an integer -//! into an [`io::Result`] and back. - -use std::{ - convert::{self, TryFrom}, - io::{self, ErrorKind}, -}; - -/// An `errno` value that can be converted into an [`io::Error`] -pub struct Errno(pub u16); - -// On Unix, from_raw_os_error takes an errno value and OS errors -// are printed using strerror. On Windows however it takes a -// GetLastError() value; therefore we need to convert errno values -// into io::Error by hand. This is the same mapping that the -// standard library uses to retrieve the kind of OS errors -// (`std::sys::pal::unix::decode_error_kind`). -impl From for ErrorKind { - fn from(value: Errno) -> ErrorKind { - use ErrorKind::*; - let Errno(errno) = value; - match i32::from(errno) { - libc::EPERM | libc::EACCES => PermissionDenied, - libc::ENOENT => NotFound, - libc::EINTR => Interrupted, - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - libc::ENOMEM => OutOfMemory, - libc::EEXIST => AlreadyExists, - libc::EINVAL => InvalidInput, - libc::EPIPE => BrokenPipe, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::ENOTCONN => NotConnected, - libc::ENOTSUP => Unsupported, - libc::ETIMEDOUT => TimedOut, - _ => Other, - } - } -} - -// This is used on Windows for all io::Errors, but also on Unix if the -// io::Error does not have a raw OS error. This is the reversed -// mapping of the above; EIO is returned for unknown ErrorKinds. -impl From for Errno { - fn from(value: io::ErrorKind) -> Errno { - use ErrorKind::*; - let errno = match value { - // can be both EPERM or EACCES :( pick one - PermissionDenied => libc::EPERM, - NotFound => libc::ENOENT, - Interrupted => libc::EINTR, - WouldBlock => libc::EAGAIN, - OutOfMemory => libc::ENOMEM, - AlreadyExists => libc::EEXIST, - InvalidInput => libc::EINVAL, - BrokenPipe => libc::EPIPE, - AddrInUse => libc::EADDRINUSE, - AddrNotAvailable => libc::EADDRNOTAVAIL, - ConnectionAborted => libc::ECONNABORTED, - ConnectionRefused => libc::ECONNREFUSED, - ConnectionReset => libc::ECONNRESET, - NotConnected => libc::ENOTCONN, - Unsupported => libc::ENOTSUP, - TimedOut => libc::ETIMEDOUT, - _ => libc::EIO, - }; - Errno(errno as u16) - } -} - -impl From for io::Error { - #[cfg(unix)] - fn from(value: Errno) -> io::Error { - let Errno(errno) = value; - io::Error::from_raw_os_error(errno.into()) - } - - #[cfg(windows)] - fn from(value: Errno) -> io::Error { - let error_kind: ErrorKind = value.into(); - error_kind.into() - } -} - -impl From for Errno { - fn from(value: io::Error) -> Errno { - if cfg!(unix) { - if let Some(errno) = value.raw_os_error() { - return Errno(u16::try_from(errno).unwrap()); - } - } - value.kind().into() - } -} - -impl From for Errno { - fn from(_value: convert::Infallible) -> Errno { - panic!("unreachable") - } -} - -/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] -/// for the "right" set of types. -mod traits { - use super::Errno; - - /// A signed type that can be converted into an - /// [`io::Result`](std::io::Result) - pub trait GetErrno { - /// Unsigned variant of `Self`, used as the type for the `Ok` case. - type Out; - - /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative - fn into_errno_result(self) -> Result; - } - - /// A type that can be taken out of an [`io::Result`](std::io::Result) and - /// converted into "positive success/negative `errno`" convention. - pub trait MergeErrno { - /// Signed variant of `Self`, used as the return type of - /// [`into_neg_errno`](super::into_neg_errno). - type Out: From + std::ops::Neg; - - /// Return `self`, asserting that it is in range - fn map_ok(self) -> Self::Out; - } - - macro_rules! get_errno { - ($t:ty, $out:ty) => { - impl GetErrno for $t { - type Out = $out; - fn into_errno_result(self) -> Result { - match self { - 0.. => Ok(self as $out), - -65535..=-1 => Err(Errno(-self as u16)), - _ => panic!("{self} is not a negative errno"), - } - } - } - }; - } - - get_errno!(i32, u32); - get_errno!(i64, u64); - get_errno!(isize, usize); - - macro_rules! merge_errno { - ($t:ty, $out:ty) => { - impl MergeErrno for $t { - type Out = $out; - fn map_ok(self) -> Self::Out { - self.try_into().unwrap() - } - } - }; - } - - merge_errno!(u8, i32); - merge_errno!(u16, i32); - merge_errno!(u32, i32); - merge_errno!(u64, i64); - - impl MergeErrno for () { - type Out = i32; - fn map_ok(self) -> i32 { - 0 - } - } -} - -use traits::{GetErrno, MergeErrno}; - -/// Convert an integer value into a [`io::Result`]. -/// -/// Positive values are turned into an `Ok` result; negative values -/// are interpreted as negated `errno` and turned into an `Err`. -/// -/// ``` -/// # use qemu_api::errno::into_io_result; -/// # use std::io::ErrorKind; -/// let ok = into_io_result(1i32).unwrap(); -/// assert_eq!(ok, 1u32); -/// -/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM -/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); -/// ``` -/// -/// # Panics -/// -/// Since the result is an unsigned integer, negative values must -/// be close to 0; values that are too far away are considered -/// likely overflows and will panic: -/// -/// ```should_panic -/// # use qemu_api::errno::into_io_result; -/// # #[allow(dead_code)] -/// let err = into_io_result(-0x1234_5678i32); // panic -/// ``` -pub fn into_io_result(value: T) -> io::Result { - value.into_errno_result().map_err(Into::into) -} - -/// Convert a [`Result`] into an integer value, using negative `errno` -/// values to report errors. -/// -/// ``` -/// # use qemu_api::errno::into_neg_errno; -/// # use std::io::{self, ErrorKind}; -/// let ok: io::Result<()> = Ok(()); -/// assert_eq!(into_neg_errno(ok), 0); -/// -/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); -/// assert_eq!(into_neg_errno(err), -22); // -EINVAL -/// ``` -/// -/// Since this module also provides the ability to convert [`io::Error`] -/// to an `errno` value, [`io::Result`] is the most commonly used type -/// for the argument of this function: -/// -/// # Panics -/// -/// Since the result is a signed integer, integer `Ok` values must remain -/// positive: -/// -/// ```should_panic -/// # use qemu_api::errno::into_neg_errno; -/// # use std::io; -/// let err: io::Result = Ok(0x8899_AABB); -/// into_neg_errno(err) // panic -/// # ; -/// ``` -pub fn into_neg_errno>(value: Result) -> T::Out { - match value { - Ok(x) => x.map_ok(), - Err(err) => -T::Out::from(err.into().0), - } -} - -#[cfg(test)] -mod tests { - use std::io::ErrorKind; - - use super::*; - use crate::assert_match; - - #[test] - pub fn test_from_u8() { - let ok: io::Result<_> = Ok(42u8); - assert_eq!(into_neg_errno(ok), 42); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_from_u16() { - let ok: io::Result<_> = Ok(1234u16); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_i32() { - assert_match!(into_io_result(1234i32), Ok(1234)); - - let err = into_io_result(-1i32).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(1)); - assert_match!(err.kind(), ErrorKind::PermissionDenied); - } - - #[test] - pub fn test_from_u32() { - let ok: io::Result<_> = Ok(1234u32); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_i64() { - assert_match!(into_io_result(1234i64), Ok(1234)); - - let err = into_io_result(-22i64).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(22)); - assert_match!(err.kind(), ErrorKind::InvalidInput); - } - - #[test] - pub fn test_from_u64() { - let ok: io::Result<_> = Ok(1234u64); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::InvalidInput.into()); - assert_eq!(into_neg_errno(err), -22); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(6)); - assert_eq!(into_neg_errno(os_err), -6); - } - } - - #[test] - pub fn test_isize() { - assert_match!(into_io_result(1234isize), Ok(1234)); - - let err = into_io_result(-4isize).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(4)); - assert_match!(err.kind(), ErrorKind::Interrupted); - } - - #[test] - pub fn test_from_unit() { - let ok: io::Result<_> = Ok(()); - assert_eq!(into_neg_errno(ok), 0); - - let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); - assert_eq!(into_neg_errno(err), -12); - - if cfg!(unix) { - let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); - assert_eq!(into_neg_errno(os_err), -2); - } - } -} 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 { 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 @@ use std::{ 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 daa2493bb6..6232e39e71 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,12 +13,8 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; -pub mod assertions; -pub mod bitops; -pub mod callbacks; pub mod cell; pub mod chardev; -pub mod errno; pub mod error; pub mod irq; pub mod log; @@ -28,9 +24,7 @@ pub mod qdev; pub mod qom; pub mod sysbus; pub mod timer; -pub mod uninit; pub mod vmstate; -pub mod zeroable; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs index a441b8c1f2..d07f6272dc 100644 --- a/rust/qemu-api/src/log.rs +++ b/rust/qemu-api/src/log.rs @@ -8,7 +8,9 @@ use std::{ 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 @@ use std::{ }; 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( 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::InterfaceType; 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 e2b4121cac..8be125fae4 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -10,11 +10,11 @@ use std::{ }; pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; +use common::{callbacks::FnCall, Opaque}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - callbacks::FnCall, - cell::{bql_locked, Opaque}, + cell::bql_locked, chardev::Chardev, error::{Error, Result}, impl_vmstate_c_struct, @@ -246,7 +246,7 @@ macro_rules! define_property { bitnr: $bitnr, set_default: true, defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { @@ -257,7 +257,7 @@ macro_rules! define_property { offset: ::std::mem::offset_of!($state, $field) as isize, set_default: true, defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { @@ -267,7 +267,7 @@ macro_rules! define_property { info: $prop, offset: ::std::mem::offset_of!($state, $field) as isize, set_default: false, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; } diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 12c6fc6752..49b4f03ccf 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -102,13 +102,14 @@ use std::{ }; pub use bindings::ObjectClass; +use common::Opaque; use crate::{ bindings::{ self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, - cell::{bql_locked, Opaque}, + cell::bql_locked, impl_vmstate_pointer, }; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 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 1e639eaf22..383e1a6e77 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -7,10 +7,10 @@ use std::{ 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/uninit.rs b/rust/qemu-api/src/uninit.rs deleted file mode 100644 index b0a829729d..0000000000 --- a/rust/qemu-api/src/uninit.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Access fields of a [`MaybeUninit`] - -use std::{ - mem::MaybeUninit, - ops::{Deref, DerefMut}, -}; - -pub struct MaybeUninitField<'a, T, U> { - parent: &'a mut MaybeUninit, - child: *mut U, -} - -impl<'a, T, U> MaybeUninitField<'a, T, U> { - #[doc(hidden)] - pub const fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { - MaybeUninitField { parent, child } - } - - /// Return a constant pointer to the containing object of the field. - /// - /// Because the `MaybeUninitField` remembers the containing object, - /// it is possible to use it in foreign APIs that initialize the - /// child. - pub const fn parent(f: &Self) -> *const T { - f.parent.as_ptr() - } - - /// Return a mutable pointer to the containing object. - /// - /// Because the `MaybeUninitField` remembers the containing object, - /// it is possible to use it in foreign APIs that initialize the - /// child. - pub const fn parent_mut(f: &mut Self) -> *mut T { - f.parent.as_mut_ptr() - } -} - -impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { - type Target = MaybeUninit; - - fn deref(&self) -> &MaybeUninit { - // SAFETY: self.child was obtained by dereferencing a valid mutable - // reference; the content of the memory may be invalid or uninitialized - // but MaybeUninit<_> makes no assumption on it - unsafe { &*(self.child.cast()) } - } -} - -impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { - fn deref_mut(&mut self) -> &mut MaybeUninit { - // SAFETY: self.child was obtained by dereferencing a valid mutable - // reference; the content of the memory may be invalid or uninitialized - // but MaybeUninit<_> makes no assumption on it - unsafe { &mut *(self.child.cast()) } - } -} - -/// ``` -/// #[derive(Debug)] -/// struct S { -/// x: u32, -/// y: u32, -/// } -/// -/// # use std::mem::MaybeUninit; -/// # use qemu_api::{assert_match, uninit_field_mut}; -/// -/// let mut s: MaybeUninit = MaybeUninit::zeroed(); -/// uninit_field_mut!(s, x).write(5); -/// let s = unsafe { s.assume_init() }; -/// assert_match!(s, S { x: 5, y: 0 }); -/// ``` -#[macro_export] -macro_rules! uninit_field_mut { - ($container:expr, $($field:tt)+) => {{ - let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container; - let container_ptr__ = container__.as_mut_ptr(); - - // SAFETY: the container is not used directly, only through a MaybeUninit<>, - // so the safety is delegated to the caller and to final invocation of - // assume_init() - let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) }; - $crate::uninit::MaybeUninitField::new(container__, target__) - }}; -} diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index ce42b031bc..06aac3a73f 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -35,14 +35,15 @@ use std::{ ptr::{addr_of, NonNull}, }; -pub use crate::bindings::{MigrationPriority, VMStateField}; -use crate::{ - bindings::{self, VMStateFlags}, +use common::{ callbacks::FnCall, errno::{into_neg_errno, Errno}, - zeroable::Zeroable, + Zeroable, }; +use crate::bindings::{self, VMStateFlags}; +pub use crate::bindings::{MigrationPriority, VMStateField}; + /// This macro is used to call a function with a generic argument bound /// to the type of a field. The function must take a /// [`PhantomData`]`` argument; `T` is the type of @@ -271,7 +272,7 @@ macro_rules! impl_vmstate_transparent { impl_vmstate_transparent!(std::cell::Cell where T: VMState); impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(std::pin::Pin where T: VMState); -impl_vmstate_transparent!(crate::cell::Opaque where T: VMState); +impl_vmstate_transparent!(::common::Opaque where T: VMState); #[macro_export] macro_rules! impl_vmstate_bitsized { @@ -324,7 +325,7 @@ macro_rules! impl_vmstate_c_struct { vmsd: ::std::ptr::addr_of!($vmsd), size: ::std::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..$crate::zeroable::Zeroable::ZERO + ..common::zeroable::Zeroable::ZERO }; } }; @@ -367,7 +368,7 @@ macro_rules! vmstate_unused { size: $size, info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, flags: $crate::bindings::VMStateFlags::VMS_BUFFER, - ..$crate::zeroable::Zeroable::ZERO + ..::common::Zeroable::ZERO } }}; } @@ -390,7 +391,7 @@ pub type VMSFieldExistCb = unsafe extern "C" fn( #[macro_export] macro_rules! vmstate_exist_fn { ($struct_name:ty, $test_fn:expr) => {{ - const fn test_cb_builder__ $crate::callbacks::FnCall<(&'a T, u8), bool>>( + const fn test_cb_builder__ ::common::callbacks::FnCall<(&'a T, u8), bool>>( _phantom: ::core::marker::PhantomData, ) -> $crate::vmstate::VMSFieldExistCb { const { assert!(F::IS_SOME) }; @@ -414,7 +415,7 @@ macro_rules! vmstate_fields { $($field),*, $crate::bindings::VMStateField { flags: $crate::bindings::VMStateFlags::VMS_END, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } ]; _FIELDS.as_ptr() @@ -433,7 +434,7 @@ macro_rules! vmstate_validate { | $crate::bindings::VMStateFlags::VMS_ARRAY.0, ), num: 0, // 0 elements: no data, only run test_fn callback - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; } @@ -455,7 +456,7 @@ macro_rules! impl_vmstate_struct { vmsd: ::core::ptr::addr_of!(*VMSD), size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..$crate::zeroable::Zeroable::ZERO + ..common::Zeroable::ZERO } }; } diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs deleted file mode 100644 index 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::::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` 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::::zeroed().assume_init() }; -} - -// bindgen does not derive Default here -#[allow(clippy::derivable_impls)] -impl Default for crate::bindings::VMStateFlags { - fn default() -> Self { - Self(0) - } -} - -unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::Property {} -unsafe impl Zeroable for crate::bindings::VMStateFlags {} -unsafe impl Zeroable for crate::bindings::VMStateField {} -unsafe impl Zeroable for crate::bindings::VMStateDescription {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} -unsafe impl Zeroable for crate::bindings::MemTxAttrs {} -unsafe impl Zeroable for crate::bindings::CharBackend {} diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 2c0670ba0e..d9e5bcc498 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -9,12 +9,13 @@ use std::{ slice, }; +use common::Opaque; use qemu_api::{ bindings::{ vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, }, - cell::{BqlCell, Opaque}, + cell::BqlCell, impl_vmstate_forward, impl_vmstate_struct, vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, -- cgit 1.4.1 From 59869b4d58854190f09a79c5392d60fdc0b55d45 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:50 +0200 Subject: rust: split "util" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-7-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 17 +- rust/Cargo.toml | 5 +- rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 7 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 6 +- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 2 +- rust/qemu-api-macros/src/tests.rs | 2 +- rust/qemu-api/Cargo.toml | 4 +- rust/qemu-api/build.rs | 50 +---- rust/qemu-api/meson.build | 14 +- rust/qemu-api/src/bindings.rs | 1 + rust/qemu-api/src/error.rs | 417 -------------------------------------- rust/qemu-api/src/lib.rs | 6 - rust/qemu-api/src/log.rs | 151 -------------- rust/qemu-api/src/module.rs | 43 ---- 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/timer.rs | 125 ------------ rust/qemu-api/src/vmstate.rs | 2 +- rust/qemu-api/tests/tests.rs | 2 +- rust/qemu-api/wrapper.h | 6 - rust/util/Cargo.toml | 23 +++ rust/util/build.rs | 49 +++++ rust/util/meson.build | 55 +++++ rust/util/src/bindings.rs | 25 +++ rust/util/src/error.rs | 416 +++++++++++++++++++++++++++++++++++++ rust/util/src/lib.rs | 9 + rust/util/src/log.rs | 151 ++++++++++++++ rust/util/src/module.rs | 43 ++++ rust/util/src/timer.rs | 125 ++++++++++++ rust/util/wrapper.h | 32 +++ 37 files changed, 969 insertions(+), 833 deletions(-) mode change 100644 => 120000 rust/qemu-api/build.rs delete mode 100644 rust/qemu-api/src/error.rs delete mode 100644 rust/qemu-api/src/log.rs delete mode 100644 rust/qemu-api/src/module.rs delete mode 100644 rust/qemu-api/src/timer.rs create mode 100644 rust/util/Cargo.toml create mode 100644 rust/util/build.rs create mode 100644 rust/util/meson.build create mode 100644 rust/util/src/bindings.rs create mode 100644 rust/util/src/error.rs create mode 100644 rust/util/src/lib.rs create mode 100644 rust/util/src/log.rs create mode 100644 rust/util/src/module.rs create mode 100644 rust/util/src/timer.rs create mode 100644 rust/util/wrapper.h (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index cee5a34206..3d7b47873f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3519,6 +3519,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/Cargo.lock b/rust/Cargo.lock index 71e8c7ed62..757c03cbde 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -73,6 +73,7 @@ dependencies = [ "common", "qemu_api", "qemu_api_macros", + "util", ] [[package]] @@ -100,6 +101,7 @@ dependencies = [ "common", "qemu_api", "qemu_api_macros", + "util", ] [[package]] @@ -138,11 +140,9 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ - "anyhow", "common", - "foreign", - "libc", "qemu_api_macros", + "util", ] [[package]] @@ -180,6 +180,17 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "util" +version = "0.1.0" +dependencies = [ + "anyhow", + "common", + "foreign", + "libc", + "qemu_api_macros", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c0426d4243..cfdd535e3b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,7 @@ members = [ "qemu-api", "hw/char/pl011", "hw/timer/hpet", + "util", ] [workspace.package] @@ -24,9 +25,7 @@ foreign = "~0.3.1" libc = "0.2.162" [workspace.lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = [ - 'cfg(MESON)', -] } +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] } # Occasionally, we may need to silence warnings and clippy lints that # were only introduced in newer Rust compiler versions. Do not croak diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 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 c4a9f531f7..8a931a4d03 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -9,6 +9,7 @@ _libpl011_rs = static_library( bits_rs, common_rs, qemu_api_rs, + util_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index b4aa6c45f8..ab38d57fc4 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -9,8 +9,6 @@ use qemu_api::{ chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, impl_vmstate_struct, irq::{IRQState, InterruptSource}, - log::Log, - log_mask_ln, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, @@ -19,6 +17,7 @@ use qemu_api::{ vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; +use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; @@ -172,7 +171,7 @@ impl ObjectImpl for PL011State { impl DeviceImpl for PL011State { const VMSTATE: Option> = Some(VMSTATE_PL011); - const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); + const REALIZE: Option util::Result<()>> = Some(Self::realize); } impl ResettablePhasesImpl for PL011State { @@ -623,7 +622,7 @@ impl PL011State { } } - 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 c91d330439..ca09660bf4 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -6,6 +6,7 @@ _libhpet_rs = static_library( dependencies: [ common_rs, qemu_api_rs, + util_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 72375d3155..2be180fded 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -27,10 +27,10 @@ use qemu_api::{ qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, - timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; +use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; @@ -728,7 +728,7 @@ impl HPETState { } } - fn realize(&self) -> qemu_api::Result<()> { + fn realize(&self) -> util::Result<()> { if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS { Err(format!( "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}" @@ -1013,7 +1013,7 @@ unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { impl DeviceImpl for HPETState { const VMSTATE: Option> = Some(VMSTATE_HPET); - const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); + const REALIZE: Option 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 49003a4780..67650a9a26 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -100,7 +100,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result::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 ef2806368d..8e71ac6e67 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -170,7 +170,7 @@ fn test_derive_object() { _unused, ::qemu_api::qom::ParentField<::ParentType> ); - ::qemu_api::module_init! { + ::util::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&::TYPE_INFO); } 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 deleted file mode 100644 index 5654d1d562..0000000000 --- a/rust/qemu-api/build.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_file; -#[cfg(windows)] -use std::os::windows::fs::symlink_file; -use std::{env, fs::remove_file, io::Result, path::Path}; - -fn main() -> Result<()> { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { - let sub = get_rust_subdir(manifest_dir).unwrap(); - format!("{root}/{sub}/bindings.inc.rs") - } else { - // Placing bindings.inc.rs in the source directory is supported - // but not documented or encouraged. - format!("{manifest_dir}/src/bindings.inc.rs") - }; - - let file = Path::new(&file); - if !Path::new(&file).exists() { - panic!(concat!( - "\n", - " No generated C bindings found! Maybe you wanted one of\n", - " `make clippy`, `make rustfmt`, `make rustdoc`?\n", - "\n", - " For other uses of `cargo`, start a subshell with\n", - " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", - " the top of the build tree." - )); - } - - let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = format!("{out_dir}/bindings.inc.rs"); - let dest_path = Path::new(&dest_path); - if dest_path.symlink_metadata().is_ok() { - remove_file(dest_path)?; - } - symlink_file(file, dest_path)?; - - println!("cargo:rerun-if-changed=build.rs"); - Ok(()) -} - -fn get_rust_subdir(path: &str) -> Option<&str> { - path.find("/rust").map(|index| &path[index + 1..]) -} diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs new file mode 120000 index 0000000000..71a3167885 --- /dev/null +++ b/rust/qemu-api/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 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 = std::result::Result; - -#[derive(Debug)] -pub struct Error { - msg: Option>, - /// Appends the print string of the error to the msg if not None - cause: Option, - 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 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 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>, cause: impl Into) -> 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( - result: Result, - 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( - result: Result, - errp: *mut *mut bindings::Error, - ) -> Option { - 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>( - c_error: *mut bindings::Error, - f: F, - ) -> Result { - // SAFETY: caller guarantees c_error is valid - let err = unsafe { Option::::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 { - // SAFETY: all arguments are controlled by this function - unsafe { - let err: *mut c_void = libc::malloc(std::mem::size_of::()); - 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::(), - 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::(), - 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 { - // 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::()); - 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::(), - 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::(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 -// 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); - -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 { - 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 { - 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 -// 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( dev: *mut bindings::DeviceState, - errp: *mut *mut bindings::Error, + errp: *mut *mut util::bindings::Error, ) { let state = NonNull::new(dev).unwrap().cast::(); 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 -// 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); - -unsafe impl Send for Timer {} -unsafe impl Sync for Timer {} - -#[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] -pub struct TimerListGroup(Opaque); - -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 FnCall<(&'a T,)>>( - opaque: *mut c_void, - ) { - // SAFETY: the opaque was passed as a reference to `T`. - F::call((unsafe { &*(opaque.cast::()) },)) - } - - let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::; - - // 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::().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" diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml new file mode 100644 index 0000000000..637df61060 --- /dev/null +++ b/rust/util/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "util" +version = "0.1.0" +description = "Rust bindings for QEMU/util" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +anyhow = { workspace = true } +foreign = { workspace = true } +libc = { workspace = true } +common = { path = "../common" } +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/util/build.rs b/rust/util/build.rs new file mode 100644 index 0000000000..5654d1d562 --- /dev/null +++ b/rust/util/build.rs @@ -0,0 +1,49 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_file; +#[cfg(windows)] +use std::os::windows::fs::symlink_file; +use std::{env, fs::remove_file, io::Result, path::Path}; + +fn main() -> Result<()> { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { + let sub = get_rust_subdir(manifest_dir).unwrap(); + format!("{root}/{sub}/bindings.inc.rs") + } else { + // Placing bindings.inc.rs in the source directory is supported + // but not documented or encouraged. + format!("{manifest_dir}/src/bindings.inc.rs") + }; + + let file = Path::new(&file); + if !Path::new(&file).exists() { + panic!(concat!( + "\n", + " No generated C bindings found! Maybe you wanted one of\n", + " `make clippy`, `make rustfmt`, `make rustdoc`?\n", + "\n", + " For other uses of `cargo`, start a subshell with\n", + " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", + " the top of the build tree." + )); + } + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = format!("{out_dir}/bindings.inc.rs"); + let dest_path = Path::new(&dest_path); + if dest_path.symlink_metadata().is_ok() { + remove_file(dest_path)?; + } + symlink_file(file, dest_path)?; + + println!("cargo:rerun-if-changed=build.rs"); + Ok(()) +} + +fn get_rust_subdir(path: &str) -> Option<&str> { + path.find("/rust").map(|index| &path[index + 1..]) +} diff --git a/rust/util/meson.build b/rust/util/meson.build new file mode 100644 index 0000000000..56e929349b --- /dev/null +++ b/rust/util/meson.build @@ -0,0 +1,55 @@ +_util_bindgen_args = [] +c_enums = [ + 'module_init_type', + 'QEMUClockType', +] +foreach enum : c_enums + _util_bindgen_args += ['--rustified-enum', enum] +endforeach + +# +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_util_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _util_bindgen_args, +) + +_util_rs = static_library( + 'util', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/error.rs', + 'src/log.rs', + 'src/module.rs', + 'src/timer.rs', + ], + {'.': _util_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_api_macros, qom, qemuutil], +) + +util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-util-rs-doctests', + _util_rs, + protocol: 'rust', + dependencies: util_rs, + suite: ['doc', 'rust'] +) diff --git a/rust/util/src/bindings.rs b/rust/util/src/bindings.rs new file mode 100644 index 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/util/src/error.rs b/rust/util/src/error.rs new file mode 100644 index 0000000000..bfa5a8685b --- /dev/null +++ b/rust/util/src/error.rs @@ -0,0 +1,416 @@ +// 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 +//! [`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. +//! +//! 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 [`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. + +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 = std::result::Result; + +#[derive(Debug)] +pub struct Error { + msg: Option>, + /// Appends the print string of the error to the msg if not None + cause: Option, + 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 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 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>, cause: impl Into) -> 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( + result: Result, + 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( + result: Result, + errp: *mut *mut bindings::Error, + ) -> Option { + 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>( + c_error: *mut bindings::Error, + f: F, + ) -> Result { + // SAFETY: caller guarantees c_error is valid + let err = unsafe { Option::::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 { + // SAFETY: all arguments are controlled by this function + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::()); + 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::(), + 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::(), + 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::*; + + #[track_caller] + fn error_for_test(msg: &CStr) -> OwnedPointer { + // 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::()); + 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::(), + 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::(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/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/util/src/log.rs b/rust/util/src/log.rs new file mode 100644 index 0000000000..af9a3e9123 --- /dev/null +++ b/rust/util/src/log.rs @@ -0,0 +1,151 @@ +// Copyright 2025 Bernhard Beschow +// 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); + +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 util::log::LogGuard; + /// # use std::io::Write; + /// if let Some(mut log) = LogGuard::new() { + /// writeln!(log, "test"); + /// } + /// ``` + pub fn new() -> Option { + 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 { + 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 util::{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 util::{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 _: $crate::log::Log = $mask; + + if unsafe { + ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 + } { + _ = $crate::log::LogGuard::log_fmt( + format_args!("{}\n", format_args!($fmt $($args)*))); + } + }}; +} diff --git a/rust/util/src/module.rs b/rust/util/src/module.rs new file mode 100644 index 0000000000..06c45fc142 --- /dev/null +++ b/rust/util/src/module.rs @@ -0,0 +1,43 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// 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) => { + ::util::module_init! { + $type => { unsafe { $body } } + } + }; +} diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs new file mode 100644 index 0000000000..383e1a6e77 --- /dev/null +++ b/rust/util/src/timer.rs @@ -0,0 +1,125 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// 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); + +unsafe impl Send for Timer {} +unsafe impl Sync for Timer {} + +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct TimerListGroup(Opaque); + +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 FnCall<(&'a T,)>>( + opaque: *mut c_void, + ) { + // SAFETY: the opaque was passed as a reference to `T`. + F::call((unsafe { &*(opaque.cast::()) },)) + } + + let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::; + + // 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::().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/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" -- cgit 1.4.1 From 4dff343d2312bfec25f95ab99ed1068511ddbebb Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:51 +0200 Subject: rust: split "migration" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-11-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 12 + rust/Cargo.toml | 1 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 13 +- rust/hw/char/pl011/src/registers.rs | 2 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 11 +- rust/meson.build | 1 + rust/migration/Cargo.toml | 21 + rust/migration/build.rs | 1 + rust/migration/meson.build | 53 +++ rust/migration/src/bindings.rs | 48 +++ rust/migration/src/lib.rs | 6 + rust/migration/src/vmstate.rs | 715 +++++++++++++++++++++++++++++++++++ rust/migration/wrapper.h | 51 +++ rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 12 +- rust/qemu-api/src/bindings.rs | 21 +- rust/qemu-api/src/cell.rs | 4 +- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/prelude.rs | 2 - rust/qemu-api/src/qdev.rs | 3 +- rust/qemu-api/src/qom.rs | 2 +- rust/qemu-api/src/vmstate.rs | 709 ---------------------------------- rust/qemu-api/tests/tests.rs | 2 +- rust/qemu-api/tests/vmstate_tests.rs | 4 +- rust/qemu-api/wrapper.h | 1 - 30 files changed, 943 insertions(+), 759 deletions(-) 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 create mode 100644 rust/migration/src/vmstate.rs create mode 100644 rust/migration/wrapper.h delete mode 100644 rust/qemu-api/src/vmstate.rs (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index 3d7b47873f..76dcf6ceb2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3516,6 +3516,7 @@ Rust M: Manos Pitsidianakis 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/Cargo.lock b/rust/Cargo.lock index 757c03cbde..048dd74757 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -71,6 +71,7 @@ name = "hpet" version = "0.1.0" dependencies = [ "common", + "migration", "qemu_api", "qemu_api_macros", "util", @@ -91,6 +92,15 @@ version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +[[package]] +name = "migration" +version = "0.1.0" +dependencies = [ + "common", + "qemu_api_macros", + "util", +] + [[package]] name = "pl011" version = "0.1.0" @@ -99,6 +109,7 @@ dependencies = [ "bilge-impl", "bits", "common", + "migration", "qemu_api", "qemu_api_macros", "util", @@ -141,6 +152,7 @@ name = "qemu_api" version = "0.1.0" dependencies = [ "common", + "migration", "qemu_api_macros", "util", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index cfdd535e3b..e0958ef28a 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 8a931a4d03..2198fcee9b 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -10,6 +10,7 @@ _libpl011_rs = static_library( common_rs, qemu_api_rs, util_rs, + migration_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index ab38d57fc4..225be34e08 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,17 +5,18 @@ use std::{ffi::CStr, mem::size_of}; use common::{static_assert, uninit_field_mut}; +use migration::{ + self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, + vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder, +}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, - impl_vmstate_forward, impl_vmstate_struct, irq::{IRQState, InterruptSource}, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField, ParentInit}, sysbus::{SysBusDevice, SysBusDeviceImpl}, - vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, - vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; use util::{log::Log, log_mask_ln}; @@ -458,10 +459,10 @@ impl PL011Registers { false } - pub fn post_load(&mut self) -> Result<(), vmstate::InvalidError> { + pub fn post_load(&mut self) -> Result<(), migration::InvalidError> { /* Sanity-check input state */ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { - return Err(vmstate::InvalidError); + return Err(migration::InvalidError); } if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 { @@ -640,7 +641,7 @@ impl PL011State { } } - pub fn post_load(&self, _version_id: u8) -> Result<(), vmstate::InvalidError> { + pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError> { self.regs.borrow_mut().post_load() } } diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index 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 ca09660bf4..8cd70091e6 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -7,6 +7,7 @@ _libhpet_rs = static_library( common_rs, qemu_api_rs, util_rs, + migration_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 2be180fded..1c2253466d 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -11,13 +11,16 @@ use std::{ }; use common::{bitops::IntegerExt, uninit_field_mut}; +use migration::{ + self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, + VMStateDescription, VMStateDescriptionBuilder, +}; use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize, }, cell::{BqlCell, BqlRefCell}, - impl_vmstate_struct, irq::InterruptSource, memory::{ hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, @@ -27,8 +30,6 @@ use qemu_api::{ qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, - vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, - vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; @@ -845,7 +846,7 @@ impl HPETState { } } - fn pre_save(&self) -> Result<(), vmstate::Infallible> { + fn pre_save(&self) -> Result<(), migration::Infallible> { if self.is_hpet_enabled() { self.counter.set(self.get_ticks()); } @@ -859,7 +860,7 @@ impl HPETState { Ok(()) } - fn post_load(&self, _version_id: u8) -> Result<(), vmstate::Infallible> { + fn post_load(&self, _version_id: u8) -> Result<(), migration::Infallible> { for timer in self.timers.iter().take(self.num_timers) { let mut t = timer.borrow_mut(); diff --git a/rust/meson.build b/rust/meson.build index 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..5e820d43f5 --- /dev/null +++ b/rust/migration/meson.build @@ -0,0 +1,53 @@ +_migration_bindgen_args = [] +c_bitfields = [ + 'MigrationPolicy', + 'MigrationPriority', + 'VMStateFlags', +] +foreach enum : c_bitfields + _migration_bindgen_args += ['--bitfield-enum', enum] +endforeach +# +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_migration_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _migration_bindgen_args, + ) + +_migration_rs = static_library( + 'migration', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/vmstate.rs', + ], + {'.' : _migration_bindings_inc_rs}, + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_util_rs], + dependencies: [common_rs], +) + +migration_rs = declare_dependency(link_with: [_migration_rs], + dependencies: [migration, qemuutil]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-migration-rs-doctests', + _migration_rs, + protocol: 'rust', + dependencies: migration_rs, + suite: ['doc', 'rust']) diff --git a/rust/migration/src/bindings.rs b/rust/migration/src/bindings.rs new file mode 100644 index 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/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs new file mode 100644 index 0000000000..537d54e436 --- /dev/null +++ b/rust/migration/src/vmstate.rs @@ -0,0 +1,715 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Helper macros to declare migration state for device models. +//! +//! This module includes four families of macros: +//! +//! * [`vmstate_unused!`](crate::vmstate_unused) and +//! [`vmstate_of!`](crate::vmstate_of), which are used to express the +//! migration format for a struct. This is based on the [`VMState`] trait, +//! which is defined by all migratable types. +//! +//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward), +//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and +//! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the +//! definition of the [`VMState`] trait (respectively for transparent structs, +//! nested structs and `bilge`-defined types) +//! +//! * helper macros to declare a device model state struct, in particular +//! [`vmstate_subsections`](crate::vmstate_subsections) and +//! [`vmstate_fields`](crate::vmstate_fields). +//! +//! * direct equivalents to the C macros declared in +//! `include/migration/vmstate.h`. These are not type-safe and only provide +//! functionality that is missing from `vmstate_of!`. + +pub use std::convert::Infallible; +use std::{ + error::Error, + ffi::{c_int, c_void, CStr}, + fmt, io, + marker::PhantomData, + mem, + ptr::{addr_of, NonNull}, +}; + +use common::{ + callbacks::FnCall, + errno::{into_neg_errno, Errno}, + Zeroable, +}; + +use crate::bindings::{self, VMStateFlags}; +pub use crate::bindings::{MigrationPriority, VMStateField}; + +/// This macro is used to call a function with a generic argument bound +/// to the type of a field. The function must take a +/// [`PhantomData`]`` argument; `T` is the type of +/// field `$field` in the `$typ` type. +/// +/// # Examples +/// +/// ``` +/// # use migration::call_func_with_field; +/// # use core::marker::PhantomData; +/// const fn size_of_field(_: PhantomData) -> usize { +/// std::mem::size_of::() +/// } +/// +/// struct Foo { +/// x: u16, +/// }; +/// // calls size_of_field::() +/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); +/// ``` +#[macro_export] +macro_rules! call_func_with_field { + // Based on the answer by user steffahn (Frank Steffahn) at + // https://users.rust-lang.org/t/inferring-type-of-field/122857 + // and used under MIT license + ($func:expr, $typ:ty, $($field:tt).+) => { + $func(loop { + #![allow(unreachable_code)] + const fn phantom__(_: &T) -> ::core::marker::PhantomData { ::core::marker::PhantomData } + // Unreachable code is exempt from checks on uninitialized values. + // Use that trick to infer the type of this PhantomData. + break ::core::marker::PhantomData; + break phantom__(&{ let value__: $typ; value__.$($field).+ }); + }) + }; +} + +/// A trait for types that can be included in a device's migration stream. It +/// provides the base contents of a `VMStateField` (minus the name and offset). +/// +/// # Safety +/// +/// The contents of this trait go straight into structs that are parsed by C +/// code and used to introspect into other structs. Generally, you don't need +/// to implement it except via macros that do it for you, such as +/// `impl_vmstate_bitsized!`. +pub unsafe trait VMState { + /// The base contents of a `VMStateField` (minus the name and offset) for + /// the type that is implementing the trait. + const BASE: VMStateField; + + /// A flag that is added to another field's `VMStateField` to specify the + /// length's type in a variable-sized array. If this is not a supported + /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it + /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a + /// compile-time error. + const VARRAY_FLAG: VMStateFlags = { + panic!("invalid type for variable-sized array"); + }; +} + +/// Internal utility function to retrieve a type's `VMStateField`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_base(_: PhantomData) -> VMStateField { + T::BASE +} + +/// Internal utility function to retrieve a type's `VMStateFlags` when it +/// is used as the element count of a `VMSTATE_VARRAY`; used by +/// [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags { + T::VARRAY_FLAG +} + +/// Return the `VMStateField` for a field of a struct. The field must be +/// visible in the current scope. +/// +/// Only a limited set of types is supported out of the box: +/// * scalar types (integer and `bool`) +/// * the C struct `QEMUTimer` +/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, +/// [`BqlCell`], [`BqlRefCell`]) +/// * a raw pointer to any of the above +/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above +/// * an array of any of the above +/// +/// In order to support other types, the trait `VMState` must be implemented +/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward), +/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and +/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. +/// +/// [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html +/// [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html +/// [`Owned`]: ../../qemu_api/qom/struct.Owned.html +#[macro_export] +macro_rules! vmstate_of { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + offset: ::std::mem::offset_of!($struct_name, $field_name), + $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? + $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? + // The calls to `call_func_with_field!` are the magic that + // computes most of the VMStateField from the type of the field. + ..$crate::call_func_with_field!( + $crate::vmstate::vmstate_base, + $struct_name, + $field_name + )$(.with_varray_flag($crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num)) + $(.with_varray_multiply($factor))?)? + } + }; +} + +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 + | VMStateFlags::VMS_VARRAY_UINT16.0 + | VMStateFlags::VMS_VARRAY_UINT32.0, + ); +} + +// Add a couple builder-style methods to VMStateField, allowing +// easy derivation of VMStateField constants from other types. +impl VMStateField { + #[must_use] + pub const fn with_version_id(mut self, version_id: i32) -> Self { + assert!(version_id >= 0); + self.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); + // 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::(); + } + 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 + } + + #[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); + self + } + + #[must_use] + pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self { + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); + self.flags = VMStateFlags(self.flags.0 | flag.0); + self.num = 0; // varray uses num_offset instead of num. + self + } + + #[must_use] + #[allow(unused_mut)] + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self { + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + self.with_varray_flag_unchecked(flag) + } + + #[must_use] + pub const fn with_varray_multiply(mut self, num: u32) -> Self { + assert!(num <= 0x7FFF_FFFFu32); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); + self.num = num as i32; + self + } +} + +/// This macro can be used (by just passing it a type) to forward the `VMState` +/// trait to the first field of a tuple. This is a workaround for lack of +/// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. +/// +/// # Examples +/// +/// ``` +/// # use migration::impl_vmstate_forward; +/// pub struct Fifo([u8; 16]); +/// impl_vmstate_forward!(Fifo); +/// ``` +#[macro_export] +macro_rules! impl_vmstate_forward { + // This is similar to impl_vmstate_transparent below, but it + // uses the same trick as vmstate_of! to obtain the type of + // the first field of the tuple + ($tuple:ty) => { + unsafe impl $crate::vmstate::VMState for $tuple { + const BASE: $crate::bindings::VMStateField = + $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); + } + }; +} + +// 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> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { + const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField { + size: mem::size_of::<$type>(), + ..<$base as $crate::vmstate::VMState>::BASE + }; + const VARRAY_FLAG: $crate::bindings::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG; + } + }; +} + +impl_vmstate_transparent!(std::cell::Cell where T: VMState); +impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); +impl_vmstate_transparent!(std::pin::Pin where T: VMState); +impl_vmstate_transparent!(common::Opaque where T: VMState); + +#[macro_export] +macro_rules! impl_vmstate_bitsized { + ($type:ty) => { + unsafe impl $crate::vmstate::VMState for $type { + const BASE: $crate::bindings::VMStateField = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::BASE; + const VARRAY_FLAG: $crate::bindings::VMStateFlags = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::VARRAY_FLAG; + } + }; +} + +// Scalar types using predefined VMStateInfos + +macro_rules! impl_vmstate_scalar { + ($info:ident, $type:ty$(, $varray_flag:ident)?) => { + unsafe impl $crate::vmstate::VMState for $type { + const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField { + info: addr_of!(bindings::$info), + size: mem::size_of::<$type>(), + flags: $crate::vmstate::VMStateFlags::VMS_SINGLE, + ..::common::zeroable::Zeroable::ZERO + }; + $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? + } + }; +} + +impl_vmstate_scalar!(vmstate_info_bool, bool); +impl_vmstate_scalar!(vmstate_info_int8, i8); +impl_vmstate_scalar!(vmstate_info_int16, i16); +impl_vmstate_scalar!(vmstate_info_int32, i32); +impl_vmstate_scalar!(vmstate_info_int64, i64); +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, util::timer::Timer); + +#[macro_export] +macro_rules! impl_vmstate_c_struct { + ($type:ty, $vmsd:expr) => { + unsafe impl $crate::vmstate::VMState for $type { + const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField { + vmsd: ::std::ptr::addr_of!($vmsd), + size: ::std::mem::size_of::<$type>(), + flags: $crate::bindings::VMStateFlags::VMS_STRUCT, + ..::common::zeroable::Zeroable::ZERO + }; + } + }; +} + +// Pointer types using the underlying type's VMState plus VMS_POINTER +// 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> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { + const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag(); + } + }; +} + +impl_vmstate_pointer!(*const T where T: VMState); +impl_vmstate_pointer!(*mut T where T: VMState); +impl_vmstate_pointer!(NonNull where T: VMState); + +// Unlike C pointers, Box is always non-null therefore there is no need +// to specify VMS_ALLOC. +impl_vmstate_pointer!(Box where T: VMState); + +// Arrays using the underlying type's VMState plus +// VMS_ARRAY/VMS_ARRAY_OF_POINTER + +unsafe impl VMState for [T; N] { + const BASE: VMStateField = ::BASE.with_array_flag(N); +} + +#[doc(alias = "VMSTATE_UNUSED")] +#[macro_export] +macro_rules! vmstate_unused { + ($size:expr) => {{ + $crate::bindings::VMStateField { + name: c"unused".as_ptr(), + size: $size, + info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, + flags: $crate::bindings::VMStateFlags::VMS_BUFFER, + ..::common::Zeroable::ZERO + } + }}; +} + +pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), bool>>( + opaque: *mut c_void, + version_id: c_int, +) -> bool { + // SAFETY: the function is used in T's implementation of VMState + let owner: &T = unsafe { &*(opaque.cast::()) }; + let version: u8 = version_id.try_into().unwrap(); + F::call((owner, version)) +} + +pub type VMSFieldExistCb = unsafe extern "C" fn( + opaque: *mut std::os::raw::c_void, + version_id: std::os::raw::c_int, +) -> bool; + +#[macro_export] +macro_rules! vmstate_exist_fn { + ($struct_name:ty, $test_fn:expr) => {{ + const fn test_cb_builder__ ::common::FnCall<(&'a T, u8), bool>>( + _phantom: ::core::marker::PhantomData, + ) -> $crate::vmstate::VMSFieldExistCb { + const { assert!(F::IS_SOME) }; + $crate::vmstate::rust_vms_test_field_exists:: + } + + const fn phantom__(_: &T) -> ::core::marker::PhantomData { + ::core::marker::PhantomData + } + Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) + }}; +} + +/// 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. +#[macro_export] +macro_rules! vmstate_fields { + ($($field:expr),*$(,)*) => {{ + static _FIELDS: &[$crate::bindings::VMStateField] = &[ + $($field),*, + $crate::bindings::VMStateField { + flags: $crate::bindings::VMStateFlags::VMS_END, + ..::common::zeroable::Zeroable::ZERO + } + ]; + _FIELDS.as_ptr() + }} +} + +#[doc(alias = "VMSTATE_VALIDATE")] +#[macro_export] +macro_rules! vmstate_validate { + ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { + $crate::bindings::VMStateField { + name: ::std::ffi::CStr::as_ptr($test_name), + field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn), + flags: $crate::bindings::VMStateFlags( + $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 + | $crate::bindings::VMStateFlags::VMS_ARRAY.0, + ), + num: 0, // 0 elements: no data, only run test_fn callback + ..::common::zeroable::Zeroable::ZERO + } + }; +} + +/// Helper macro to allow using a struct in [`vmstate_of!`] +/// +/// # Safety +/// +/// The [`VMStateDescription`] constant `$vmsd` must be an accurate +/// description of the struct. +#[macro_export] +macro_rules! impl_vmstate_struct { + ($type:ty, $vmsd:expr) => { + unsafe impl $crate::vmstate::VMState for $type { + const BASE: $crate::bindings::VMStateField = { + static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref(); + + $crate::bindings::VMStateField { + vmsd: ::core::ptr::addr_of!(*VMSD), + size: ::core::mem::size_of::<$type>(), + flags: $crate::bindings::VMStateFlags::VMS_STRUCT, + ..common::Zeroable::ZERO + } + }; + } + }; +} + +/// A transparent wrapper type for the `subsections` field of +/// [`VMStateDescription`]. +/// +/// This is necessary to be able to declare subsection descriptions as statics, +/// because the only way to implement `Sync` for a foreign type (and `*const` +/// pointers are foreign types in Rust) is to create a wrapper struct and +/// `unsafe impl Sync` for it. +/// +/// This struct is used in the +/// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation. +#[repr(transparent)] +pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]); + +unsafe impl Sync for VMStateSubsectionsWrapper {} + +/// Helper macro to declare a list of subsections ([`VMStateDescription`]) +/// into a static and return a pointer to the array of pointers it created. +#[macro_export] +macro_rules! vmstate_subsections { + ($($subsection:expr),*$(,)*) => {{ + static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ + $({ + static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get(); + ::core::ptr::addr_of!(_SUBSECTION) + }),*, + ::core::ptr::null() + ]); + &_SUBSECTIONS + }} +} + +pub struct VMStateDescription(bindings::VMStateDescription, PhantomData); + +// SAFETY: When a *const T is passed to the callbacks, the call itself +// is done in a thread-safe manner. The invocation is okay as long as +// T itself is `Sync`. +unsafe impl Sync for VMStateDescription {} + +#[derive(Clone)] +pub struct VMStateDescriptionBuilder(bindings::VMStateDescription, PhantomData); + +#[derive(Debug)] +pub struct InvalidError; + +impl Error for InvalidError {} + +impl std::fmt::Display for InvalidError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid migration data") + } +} + +impl From for Errno { + fn from(_value: InvalidError) -> Errno { + io::ErrorKind::InvalidInput.into() + } +} + +unsafe extern "C" fn vmstate_no_version_cb< + T, + F: for<'a> FnCall<(&'a T,), Result<(), impl Into>>, +>( + opaque: *mut c_void, +) -> c_int { + // SAFETY: the function is used in T's implementation of VMState + let result = F::call((unsafe { &*(opaque.cast::()) },)); + into_neg_errno(result) +} + +unsafe extern "C" fn vmstate_post_load_cb< + T, + F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into>>, +>( + opaque: *mut c_void, + version_id: c_int, +) -> c_int { + // SAFETY: the function is used in T's implementation of VMState + let owner: &T = unsafe { &*(opaque.cast::()) }; + let version: u8 = version_id.try_into().unwrap(); + let result = F::call((owner, version)); + into_neg_errno(result) +} + +unsafe extern "C" fn vmstate_needed_cb FnCall<(&'a T,), bool>>( + opaque: *mut c_void, +) -> bool { + // SAFETY: the function is used in T's implementation of VMState + F::call((unsafe { &*(opaque.cast::()) },)) +} + +unsafe extern "C" fn vmstate_dev_unplug_pending_cb FnCall<(&'a T,), bool>>( + opaque: *mut c_void, +) -> bool { + // SAFETY: the function is used in T's implementation of VMState + F::call((unsafe { &*(opaque.cast::()) },)) +} + +impl VMStateDescriptionBuilder { + #[must_use] + pub const fn name(mut self, name_str: &CStr) -> Self { + self.0.name = ::std::ffi::CStr::as_ptr(name_str); + self + } + + #[must_use] + pub const fn unmigratable(mut self) -> Self { + self.0.unmigratable = true; + self + } + + #[must_use] + pub const fn early_setup(mut self) -> Self { + self.0.early_setup = true; + self + } + + #[must_use] + pub const fn version_id(mut self, version: u8) -> Self { + self.0.version_id = version as c_int; + self + } + + #[must_use] + pub const fn minimum_version_id(mut self, min_version: u8) -> Self { + self.0.minimum_version_id = min_version as c_int; + self + } + + #[must_use] + pub const fn priority(mut self, priority: MigrationPriority) -> Self { + self.0.priority = priority; + self + } + + #[must_use] + pub const fn pre_load FnCall<(&'a T,), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.pre_load = if F::IS_SOME { + Some(vmstate_no_version_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn post_load FnCall<(&'a T, u8), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.post_load = if F::IS_SOME { + Some(vmstate_post_load_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn pre_save FnCall<(&'a T,), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.pre_save = if F::IS_SOME { + Some(vmstate_no_version_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn post_save FnCall<(&'a T,), Result<(), impl Into>>>( + mut self, + _f: &F, + ) -> Self { + self.0.post_save = if F::IS_SOME { + Some(vmstate_no_version_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn needed FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { + self.0.needed = if F::IS_SOME { + Some(vmstate_needed_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn unplug_pending FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { + self.0.dev_unplug_pending = if F::IS_SOME { + Some(vmstate_dev_unplug_pending_cb::) + } else { + None + }; + self + } + + #[must_use] + pub const fn fields(mut self, fields: *const VMStateField) -> Self { + self.0.fields = fields; + self + } + + #[must_use] + pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self { + self.0.subsections = subs.0.as_ptr(); + self + } + + #[must_use] + pub const fn build(self) -> VMStateDescription { + VMStateDescription::(self.0, PhantomData) + } + + #[must_use] + pub const fn new() -> Self { + Self(bindings::VMStateDescription::ZERO, PhantomData) + } +} + +impl Default for VMStateDescriptionBuilder { + fn default() -> Self { + Self::new() + } +} + +impl VMStateDescription { + pub const fn get(&self) -> bindings::VMStateDescription { + self.0 + } + + pub const fn as_ref(&self) -> &bindings::VMStateDescription { + &self.0 + } +} 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 + * + * 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/Cargo.toml b/rust/qemu-api/Cargo.toml index fbfb894421..7276e67aa9 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } +migration = { path = "../migration" } util = { path = "../util" } qemu_api_macros = { path = "../qemu-api-macros" } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 7734f656a2..a6b5772d19 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -11,8 +11,6 @@ c_enums = [ 'GpioPolarity', 'MachineInitPhase', 'MemoryDeviceInfoKind', - 'MigrationPolicy', - 'MigrationPriority', 'QEMUChrEvent', 'ResetType', 'device_endian', @@ -23,12 +21,13 @@ foreach enum : c_enums endforeach c_bitfields = [ 'ClockEvent', - 'VMStateFlags', ] foreach enum : c_bitfields _qemu_api_bindgen_args += ['--bitfield-enum', enum] endforeach +_qemu_api_bindgen_args += ['--blocklist-type', 'VMStateDescription'] + _qemu_api_bindgen_args += ['--blocklist-type', 'Error'] # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # @@ -60,15 +59,14 @@ _qemu_api_rs = static_library( 'src/qdev.rs', 'src/qom.rs', 'src/sysbus.rs', - 'src/vmstate.rs', ], {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, util_rs, - qom, hwcore, chardev, migration], + dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + util_rs, qom, hwcore, chardev], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], @@ -90,7 +88,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, util_rs, qemu_api_rs]), + dependencies: [common_rs, util_rs, migration_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 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 d13848df20..b80a0fd80b 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -152,7 +152,9 @@ use std::{ 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 54b252fb2c..55386f6697 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -20,7 +20,6 @@ pub mod memory; pub mod qdev; pub mod qom; pub mod sysbus; -pub mod vmstate; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 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::ObjectType; 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 d87479ce13..c81ae7cf45 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -11,17 +11,16 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use common::{callbacks::FnCall, Opaque}; +use migration::{impl_vmstate_c_struct, VMStateDescription}; use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, cell::bql_locked, chardev::Chardev, - impl_vmstate_c_struct, irq::InterruptSource, prelude::*, qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, - vmstate::VMStateDescription, }; /// A safe wrapper around [`bindings::Clock`]. diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 49b4f03ccf..7f2f7797e4 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -103,6 +103,7 @@ use std::{ pub use bindings::ObjectClass; use common::Opaque; +use migration::impl_vmstate_pointer; use crate::{ bindings::{ @@ -110,7 +111,6 @@ use crate::{ 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/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs deleted file mode 100644 index 37e47cc4c6..0000000000 --- a/rust/qemu-api/src/vmstate.rs +++ /dev/null @@ -1,709 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Helper macros to declare migration state for device models. -//! -//! This module includes four families of macros: -//! -//! * [`vmstate_unused!`](crate::vmstate_unused) and -//! [`vmstate_of!`](crate::vmstate_of), which are used to express the -//! migration format for a struct. This is based on the [`VMState`] trait, -//! which is defined by all migratable types. -//! -//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward), -//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and -//! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the -//! definition of the [`VMState`] trait (respectively for transparent structs, -//! nested structs and `bilge`-defined types) -//! -//! * helper macros to declare a device model state struct, in particular -//! [`vmstate_subsections`](crate::vmstate_subsections) and -//! [`vmstate_fields`](crate::vmstate_fields). -//! -//! * direct equivalents to the C macros declared in -//! `include/migration/vmstate.h`. These are not type-safe and only provide -//! functionality that is missing from `vmstate_of!`. - -pub use std::convert::Infallible; -use std::{ - error::Error, - ffi::{c_int, c_void, CStr}, - fmt, io, - marker::PhantomData, - mem, - ptr::{addr_of, NonNull}, -}; - -use common::{ - callbacks::FnCall, - errno::{into_neg_errno, Errno}, - Zeroable, -}; - -use crate::bindings::{self, VMStateFlags}; -pub use crate::bindings::{MigrationPriority, VMStateField}; - -/// This macro is used to call a function with a generic argument bound -/// to the type of a field. The function must take a -/// [`PhantomData`]`` argument; `T` is the type of -/// field `$field` in the `$typ` type. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::call_func_with_field; -/// # use core::marker::PhantomData; -/// const fn size_of_field(_: PhantomData) -> usize { -/// std::mem::size_of::() -/// } -/// -/// struct Foo { -/// x: u16, -/// }; -/// // calls size_of_field::() -/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); -/// ``` -#[macro_export] -macro_rules! call_func_with_field { - // Based on the answer by user steffahn (Frank Steffahn) at - // https://users.rust-lang.org/t/inferring-type-of-field/122857 - // and used under MIT license - ($func:expr, $typ:ty, $($field:tt).+) => { - $func(loop { - #![allow(unreachable_code)] - const fn phantom__(_: &T) -> ::core::marker::PhantomData { ::core::marker::PhantomData } - // Unreachable code is exempt from checks on uninitialized values. - // Use that trick to infer the type of this PhantomData. - break ::core::marker::PhantomData; - break phantom__(&{ let value__: $typ; value__.$($field).+ }); - }) - }; -} - -/// A trait for types that can be included in a device's migration stream. It -/// provides the base contents of a `VMStateField` (minus the name and offset). -/// -/// # Safety -/// -/// The contents of this trait go straight into structs that are parsed by C -/// code and used to introspect into other structs. Generally, you don't need -/// to implement it except via macros that do it for you, such as -/// `impl_vmstate_bitsized!`. -pub unsafe trait VMState { - /// The base contents of a `VMStateField` (minus the name and offset) for - /// the type that is implementing the trait. - const BASE: VMStateField; - - /// A flag that is added to another field's `VMStateField` to specify the - /// length's type in a variable-sized array. If this is not a supported - /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it - /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a - /// compile-time error. - const VARRAY_FLAG: VMStateFlags = { - panic!("invalid type for variable-sized array"); - }; -} - -/// Internal utility function to retrieve a type's `VMStateField`; -/// used by [`vmstate_of!`](crate::vmstate_of). -pub const fn vmstate_base(_: PhantomData) -> VMStateField { - T::BASE -} - -/// Internal utility function to retrieve a type's `VMStateFlags` when it -/// is used as the element count of a `VMSTATE_VARRAY`; used by -/// [`vmstate_of!`](crate::vmstate_of). -pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags { - T::VARRAY_FLAG -} - -/// Return the `VMStateField` for a field of a struct. The field must be -/// visible in the current scope. -/// -/// Only a limited set of types is supported out of the box: -/// * scalar types (integer and `bool`) -/// * the C struct `QEMUTimer` -/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, -/// [`BqlCell`](crate::cell::BqlCell), -/// [`BqlRefCell`](crate::cell::BqlRefCell)), -/// * a raw pointer to any of the above -/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of -/// the above -/// * an array of any of the above -/// -/// In order to support other types, the trait `VMState` must be implemented -/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward), -/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and -/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. -#[macro_export] -macro_rules! vmstate_of { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), "\0") - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - offset: ::std::mem::offset_of!($struct_name, $field_name), - $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? - $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? - // The calls to `call_func_with_field!` are the magic that - // computes most of the VMStateField from the type of the field. - ..$crate::call_func_with_field!( - $crate::vmstate::vmstate_base, - $struct_name, - $field_name - )$(.with_varray_flag($crate::call_func_with_field!( - $crate::vmstate::vmstate_varray_flag, - $struct_name, - $num)) - $(.with_varray_multiply($factor))?)? - } - }; -} - -impl VMStateFlags { - const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( - VMStateFlags::VMS_VARRAY_INT32.0 - | VMStateFlags::VMS_VARRAY_UINT8.0 - | VMStateFlags::VMS_VARRAY_UINT16.0 - | VMStateFlags::VMS_VARRAY_UINT32.0, - ); -} - -// Add a couple builder-style methods to VMStateField, allowing -// easy derivation of VMStateField constants from other types. -impl VMStateField { - #[must_use] - pub const fn with_version_id(mut self, version_id: i32) -> Self { - assert!(version_id >= 0); - self.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); - // 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::(); - } - 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 - } - - #[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); - 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. - 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); - self.with_varray_flag_unchecked(flag) - } - - #[must_use] - pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { - assert!(num <= 0x7FFF_FFFFu32); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); - self.num = num as i32; - self - } -} - -/// This macro can be used (by just passing it a type) to forward the `VMState` -/// trait to the first field of a tuple. This is a workaround for lack of -/// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::impl_vmstate_forward; -/// pub struct Fifo([u8; 16]); -/// impl_vmstate_forward!(Fifo); -/// ``` -#[macro_export] -macro_rules! impl_vmstate_forward { - // This is similar to impl_vmstate_transparent below, but it - // uses the same trick as vmstate_of! to obtain the type of - // the first field of the tuple - ($tuple:ty) => { - unsafe impl $crate::vmstate::VMState for $tuple { - const BASE: $crate::bindings::VMStateField = - $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); - } - }; -} - -// 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> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { - const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField { - size: mem::size_of::<$type>(), - ..<$base as $crate::vmstate::VMState>::BASE - }; - const VARRAY_FLAG: $crate::bindings::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG; - } - }; -} - -impl_vmstate_transparent!(std::cell::Cell where T: VMState); -impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); -impl_vmstate_transparent!(std::pin::Pin where T: VMState); -impl_vmstate_transparent!(::common::Opaque where T: VMState); - -#[macro_export] -macro_rules! impl_vmstate_bitsized { - ($type:ty) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::bindings::VMStateField = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::BASE; - const VARRAY_FLAG: $crate::bindings::VMStateFlags = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::VARRAY_FLAG; - } - }; -} - -// Scalar types using predefined VMStateInfos - -macro_rules! impl_vmstate_scalar { - ($info:ident, $type:ty$(, $varray_flag:ident)?) => { - unsafe impl VMState for $type { - const BASE: VMStateField = VMStateField { - info: addr_of!(bindings::$info), - size: mem::size_of::<$type>(), - flags: VMStateFlags::VMS_SINGLE, - ..Zeroable::ZERO - }; - $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? - } - }; -} - -impl_vmstate_scalar!(vmstate_info_bool, bool); -impl_vmstate_scalar!(vmstate_info_int8, i8); -impl_vmstate_scalar!(vmstate_info_int16, i16); -impl_vmstate_scalar!(vmstate_info_int32, i32); -impl_vmstate_scalar!(vmstate_info_int64, i64); -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, util::timer::Timer); - -#[macro_export] -macro_rules! impl_vmstate_c_struct { - ($type:ty, $vmsd:expr) => { - unsafe impl VMState for $type { - const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField { - vmsd: ::std::ptr::addr_of!($vmsd), - size: ::std::mem::size_of::<$type>(), - flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..common::zeroable::Zeroable::ZERO - }; - } - }; -} - -// Pointer types using the underlying type's VMState plus VMS_POINTER -// 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> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { - const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag(); - } - }; -} - -impl_vmstate_pointer!(*const T where T: VMState); -impl_vmstate_pointer!(*mut T where T: VMState); -impl_vmstate_pointer!(NonNull where T: VMState); - -// Unlike C pointers, Box is always non-null therefore there is no need -// to specify VMS_ALLOC. -impl_vmstate_pointer!(Box where T: VMState); - -// Arrays using the underlying type's VMState plus -// VMS_ARRAY/VMS_ARRAY_OF_POINTER - -unsafe impl VMState for [T; N] { - const BASE: VMStateField = ::BASE.with_array_flag(N); -} - -#[doc(alias = "VMSTATE_UNUSED")] -#[macro_export] -macro_rules! vmstate_unused { - ($size:expr) => {{ - $crate::bindings::VMStateField { - name: c"unused".as_ptr(), - size: $size, - info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, - flags: $crate::bindings::VMStateFlags::VMS_BUFFER, - ..::common::Zeroable::ZERO - } - }}; -} - -pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), bool>>( - opaque: *mut c_void, - version_id: c_int, -) -> bool { - // SAFETY: the function is used in T's implementation of VMState - let owner: &T = unsafe { &*(opaque.cast::()) }; - let version: u8 = version_id.try_into().unwrap(); - F::call((owner, version)) -} - -pub type VMSFieldExistCb = unsafe extern "C" fn( - opaque: *mut std::os::raw::c_void, - version_id: std::os::raw::c_int, -) -> bool; - -#[macro_export] -macro_rules! vmstate_exist_fn { - ($struct_name:ty, $test_fn:expr) => {{ - const fn test_cb_builder__ ::common::callbacks::FnCall<(&'a T, u8), bool>>( - _phantom: ::core::marker::PhantomData, - ) -> $crate::vmstate::VMSFieldExistCb { - const { assert!(F::IS_SOME) }; - $crate::vmstate::rust_vms_test_field_exists:: - } - - const fn phantom__(_: &T) -> ::core::marker::PhantomData { - ::core::marker::PhantomData - } - Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) - }}; -} - -/// 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. -#[macro_export] -macro_rules! vmstate_fields { - ($($field:expr),*$(,)*) => {{ - static _FIELDS: &[$crate::bindings::VMStateField] = &[ - $($field),*, - $crate::bindings::VMStateField { - flags: $crate::bindings::VMStateFlags::VMS_END, - ..::common::zeroable::Zeroable::ZERO - } - ]; - _FIELDS.as_ptr() - }} -} - -#[doc(alias = "VMSTATE_VALIDATE")] -#[macro_export] -macro_rules! vmstate_validate { - ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { - $crate::bindings::VMStateField { - name: ::std::ffi::CStr::as_ptr($test_name), - field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn), - flags: $crate::bindings::VMStateFlags( - $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 - | $crate::bindings::VMStateFlags::VMS_ARRAY.0, - ), - num: 0, // 0 elements: no data, only run test_fn callback - ..::common::zeroable::Zeroable::ZERO - } - }; -} - -/// Helper macro to allow using a struct in [`vmstate_of!`] -/// -/// # Safety -/// -/// The [`VMStateDescription`] constant `$vmsd` must be an accurate -/// description of the struct. -#[macro_export] -macro_rules! impl_vmstate_struct { - ($type:ty, $vmsd:expr) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::bindings::VMStateField = { - static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref(); - - $crate::bindings::VMStateField { - vmsd: ::core::ptr::addr_of!(*VMSD), - size: ::core::mem::size_of::<$type>(), - flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..common::Zeroable::ZERO - } - }; - } - }; -} - -/// A transparent wrapper type for the `subsections` field of -/// [`VMStateDescription`]. -/// -/// This is necessary to be able to declare subsection descriptions as statics, -/// because the only way to implement `Sync` for a foreign type (and `*const` -/// pointers are foreign types in Rust) is to create a wrapper struct and -/// `unsafe impl Sync` for it. -/// -/// This struct is used in the -/// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation. -#[repr(transparent)] -pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]); - -unsafe impl Sync for VMStateSubsectionsWrapper {} - -/// Helper macro to declare a list of subsections ([`VMStateDescription`]) -/// into a static and return a pointer to the array of pointers it created. -#[macro_export] -macro_rules! vmstate_subsections { - ($($subsection:expr),*$(,)*) => {{ - static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ - $({ - static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get(); - ::core::ptr::addr_of!(_SUBSECTION) - }),*, - ::core::ptr::null() - ]); - &_SUBSECTIONS - }} -} - -pub struct VMStateDescription(bindings::VMStateDescription, PhantomData); - -// SAFETY: When a *const T is passed to the callbacks, the call itself -// is done in a thread-safe manner. The invocation is okay as long as -// T itself is `Sync`. -unsafe impl Sync for VMStateDescription {} - -#[derive(Clone)] -pub struct VMStateDescriptionBuilder(bindings::VMStateDescription, PhantomData); - -#[derive(Debug)] -pub struct InvalidError; - -impl Error for InvalidError {} - -impl std::fmt::Display for InvalidError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid migration data") - } -} - -impl From for Errno { - fn from(_value: InvalidError) -> Errno { - io::ErrorKind::InvalidInput.into() - } -} - -unsafe extern "C" fn vmstate_no_version_cb< - T, - F: for<'a> FnCall<(&'a T,), Result<(), impl Into>>, ->( - opaque: *mut c_void, -) -> c_int { - // SAFETY: the function is used in T's implementation of VMState - let result = F::call((unsafe { &*(opaque.cast::()) },)); - into_neg_errno(result) -} - -unsafe extern "C" fn vmstate_post_load_cb< - T, - F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into>>, ->( - opaque: *mut c_void, - version_id: c_int, -) -> c_int { - // SAFETY: the function is used in T's implementation of VMState - let owner: &T = unsafe { &*(opaque.cast::()) }; - let version: u8 = version_id.try_into().unwrap(); - let result = F::call((owner, version)); - into_neg_errno(result) -} - -unsafe extern "C" fn vmstate_needed_cb FnCall<(&'a T,), bool>>( - opaque: *mut c_void, -) -> bool { - // SAFETY: the function is used in T's implementation of VMState - F::call((unsafe { &*(opaque.cast::()) },)) -} - -unsafe extern "C" fn vmstate_dev_unplug_pending_cb FnCall<(&'a T,), bool>>( - opaque: *mut c_void, -) -> bool { - // SAFETY: the function is used in T's implementation of VMState - F::call((unsafe { &*(opaque.cast::()) },)) -} - -impl VMStateDescriptionBuilder { - #[must_use] - pub const fn name(mut self, name_str: &CStr) -> Self { - self.0.name = ::std::ffi::CStr::as_ptr(name_str); - self - } - - #[must_use] - pub const fn unmigratable(mut self) -> Self { - self.0.unmigratable = true; - self - } - - #[must_use] - pub const fn early_setup(mut self) -> Self { - self.0.early_setup = true; - self - } - - #[must_use] - pub const fn version_id(mut self, version: u8) -> Self { - self.0.version_id = version as c_int; - self - } - - #[must_use] - pub const fn minimum_version_id(mut self, min_version: u8) -> Self { - self.0.minimum_version_id = min_version as c_int; - self - } - - #[must_use] - pub const fn priority(mut self, priority: MigrationPriority) -> Self { - self.0.priority = priority; - self - } - - #[must_use] - pub const fn pre_load FnCall<(&'a T,), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.pre_load = if F::IS_SOME { - Some(vmstate_no_version_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn post_load FnCall<(&'a T, u8), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.post_load = if F::IS_SOME { - Some(vmstate_post_load_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn pre_save FnCall<(&'a T,), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.pre_save = if F::IS_SOME { - Some(vmstate_no_version_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn post_save FnCall<(&'a T,), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.post_save = if F::IS_SOME { - Some(vmstate_no_version_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn needed FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { - self.0.needed = if F::IS_SOME { - Some(vmstate_needed_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn unplug_pending FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { - self.0.dev_unplug_pending = if F::IS_SOME { - Some(vmstate_dev_unplug_pending_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn fields(mut self, fields: *const VMStateField) -> Self { - self.0.fields = fields; - self - } - - #[must_use] - pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self { - self.0.subsections = subs.0.as_ptr(); - self - } - - #[must_use] - pub const fn build(self) -> VMStateDescription { - VMStateDescription::(self.0, PhantomData) - } - - #[must_use] - pub const fn new() -> Self { - Self(bindings::VMStateDescription::ZERO, PhantomData) - } -} - -impl Default for VMStateDescriptionBuilder { - fn default() -> Self { - Self::new() - } -} - -impl VMStateDescription { - pub const fn get(&self) -> bindings::VMStateDescription { - self.0 - } - - pub const fn as_ref(&self) -> &bindings::VMStateDescription { - &self.0 - } -} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 70ef4a80d5..92e3534d3c 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -4,13 +4,13 @@ use std::{ffi::CStr, ptr::addr_of}; +use migration::{VMStateDescription, VMStateDescriptionBuilder}; use qemu_api::{ cell::{self, BqlCell}, prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, - vmstate::{VMStateDescription, VMStateDescriptionBuilder}, }; use util::bindings::{module_call_init, module_init_type}; diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index d9e5bcc498..47fc15149b 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -10,16 +10,16 @@ use std::{ }; use common::Opaque; -use qemu_api::{ +use migration::{ bindings::{ vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, }, - cell::BqlCell, impl_vmstate_forward, impl_vmstate_struct, vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, }; +use qemu_api::cell::BqlCell; const FOO_ARRAY_MAX: usize = 3; 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" -- cgit 1.4.1 From f6b4f0dd9c57079b72ca9ff7569ce4d0dbdcc4d9 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:52 +0200 Subject: rust: split "bql" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Link: https://lore.kernel.org/r/20250827104147.717203-12-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + 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/bql/src/cell.rs | 850 ++++++++++++++++++++++++++++++++++ rust/bql/src/lib.rs | 29 ++ rust/bql/wrapper.h | 27 ++ rust/common/src/opaque.rs | 4 +- rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 1 + rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 2 +- rust/hw/timer/hpet/src/fw_cfg.rs | 5 +- rust/meson.build | 1 + rust/migration/src/vmstate.rs | 4 +- rust/qemu-api/Cargo.toml | 5 +- rust/qemu-api/meson.build | 18 +- rust/qemu-api/src/cell.rs | 874 ----------------------------------- 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 +- 32 files changed, 1060 insertions(+), 936 deletions(-) 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 create mode 100644 rust/bql/src/cell.rs create mode 100644 rust/bql/src/lib.rs create mode 100644 rust/bql/wrapper.h delete mode 100644 rust/qemu-api/src/cell.rs (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index 76dcf6ceb2..a55d5c95d7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3515,6 +3515,7 @@ F: include/hw/registerfields.h Rust M: Manos Pitsidianakis S: Maintained +F: rust/bql/ F: rust/common/ F: rust/migration/ F: rust/qemu-api diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 048dd74757..73ca9582a5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -44,6 +44,13 @@ dependencies = [ "qemu_api_macros", ] +[[package]] +name = "bql" +version = "0.1.0" +dependencies = [ + "migration", +] + [[package]] name = "common" version = "0.1.0" @@ -70,6 +77,7 @@ dependencies = [ name = "hpet" version = "0.1.0" dependencies = [ + "bql", "common", "migration", "qemu_api", @@ -108,6 +116,7 @@ dependencies = [ "bilge", "bilge-impl", "bits", + "bql", "common", "migration", "qemu_api", @@ -151,6 +160,7 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ + "bql", "common", "migration", "qemu_api_macros", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e0958ef28a..8be90da8ff 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..f369209dfd --- /dev/null +++ b/rust/bql/meson.build @@ -0,0 +1,52 @@ +_bql_cfg = run_command(rustc_args, + '--config-headers', config_host_h, '--features', files('Cargo.toml'), + capture: true, check: true).stdout().strip().splitlines() + +if get_option('debug_mutex') + _bql_cfg += ['--cfg', 'feature="debug_cell"'] +endif + +# +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_bql_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common, +) + +_bql_rs = static_library( + 'bql', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/cell.rs', + ], + {'.': _bql_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: _bql_cfg, + link_with: [_migration_rs], +) + +bql_rs = declare_dependency(link_with: [_bql_rs], + dependencies: [qemuutil]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-bql-rs-doctests', + _bql_rs, + protocol: 'rust', + dependencies: bql_rs, + suite: ['doc', 'rust']) diff --git a/rust/bql/src/bindings.rs b/rust/bql/src/bindings.rs new file mode 100644 index 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/bql/src/cell.rs b/rust/bql/src/cell.rs new file mode 100644 index 0000000000..25007427ed --- /dev/null +++ b/rust/bql/src/cell.rs @@ -0,0 +1,850 @@ +// SPDX-License-Identifier: MIT +// +// This file is based on library/core/src/cell.rs from +// Rust 1.82.0. +// +// 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. + +//! QEMU-specific mutable containers +//! +//! Rust memory safety is based on this rule: Given an object `T`, it is only +//! possible to have one of the following: +//! +//! - Having several immutable references (`&T`) to the object (also known as +//! **aliasing**). +//! - Having one mutable reference (`&mut T`) to the object (also known as +//! **mutability**). +//! +//! This is enforced by the Rust compiler. However, there are situations where +//! this rule is not flexible enough. Sometimes it is required to have multiple +//! references to an object and yet mutate it. In particular, QEMU objects +//! usually have their pointer shared with the "outside world very early in +//! their lifetime", for example when they create their +//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual +//! parts of a device must be made mutable in a controlled manner; this module +//! provides the tools to do so. +//! +//! ## Cell types +//! +//! [`BqlCell`] and [`BqlRefCell`] allow doing this via the Big QEMU Lock. +//! While they are essentially the same single-threaded primitives that are +//! available in `std::cell`, the BQL allows them to be used from a +//! multi-threaded context and to share references across threads, while +//! maintaining Rust's safety guarantees. For this reason, unlike +//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the +//! `Sync` trait. +//! +//! BQL checks are performed in debug builds but can be optimized away in +//! release builds, providing runtime safety during development with no overhead +//! in production. +//! +//! The two provide different ways of handling interior mutability. +//! `BqlRefCell` is best suited for data that is primarily accessed by the +//! device's own methods, where multiple reads and writes can be grouped within +//! a single borrow and a mutable reference can be passed around. Instead, +//! [`BqlCell`] is a better choice when sharing small pieces of data with +//! external code (especially C code), because it provides simple get/set +//! operations that can be used one at a time. +//! +//! Warning: While `BqlCell` and `BqlRefCell` are similar to their `std::cell` +//! counterparts, they are not interchangeable. Using `std::cell` types in +//! QEMU device implementations is usually incorrect and can lead to +//! thread-safety issues. +//! +//! ### Example +//! +//! ```ignore +//! # use bql::BqlRefCell; +//! # use qemu_api::prelude::*; +//! # use qemu_api::{irq::InterruptSource, irq::IRQState}; +//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; +//! # const N_GPIOS: usize = 8; +//! # struct PL061Registers { /* ... */ } +//! # unsafe impl ObjectType for PL061State { +//! # type Class = ::Class; +//! # const TYPE_NAME: &'static std::ffi::CStr = c"pl061"; +//! # } +//! struct PL061State { +//! parent_obj: ParentField, +//! +//! // Configuration is read-only after initialization +//! pullups: u32, +//! pulldowns: u32, +//! +//! // Single values shared with C code use BqlCell, in this case via InterruptSource +//! out: [InterruptSource; N_GPIOS], +//! interrupt: InterruptSource, +//! +//! // Larger state accessed by device methods uses BqlRefCell or Mutex +//! registers: BqlRefCell, +//! } +//! ``` +//! +//! ### `BqlCell` +//! +//! [`BqlCell`] implements interior mutability by moving values in and out of +//! the cell. That is, an `&mut T` to the inner value can never be obtained as +//! long as the cell is shared. The value itself cannot be directly obtained +//! without copying it, cloning it, or replacing it with something else. This +//! type provides the following methods, all of which can be called only while +//! the BQL is held: +//! +//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method +//! retrieves the current interior value by duplicating it. +//! - For types that implement [`Default`], the [`take`](BqlCell::take) method +//! replaces the current interior value with [`Default::default()`] and +//! returns the replaced value. +//! - All types have: +//! - [`replace`](BqlCell::replace): replaces the current interior value and +//! returns the replaced value. +//! - [`set`](BqlCell::set): this method replaces the interior value, +//! dropping the replaced value. +//! +//! ### `BqlRefCell` +//! +//! [`BqlRefCell`] uses Rust's lifetimes to implement "dynamic borrowing", a +//! process whereby one can claim temporary, exclusive, mutable access to the +//! inner value: +//! +//! ```ignore +//! fn clear_interrupts(&self, val: u32) { +//! // A mutable borrow gives read-write access to the registers +//! let mut regs = self.registers.borrow_mut(); +//! let old = regs.interrupt_status(); +//! regs.update_interrupt_status(old & !val); +//! } +//! ``` +//! +//! Borrows for `BqlRefCell`s are tracked at _runtime_, unlike Rust's native +//! reference types which are entirely tracked statically, at compile time. +//! 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, + fmt, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +use migration::impl_vmstate_transparent; + +/// A mutable memory location that is protected by the Big QEMU Lock. +/// +/// # Memory layout +/// +/// `BqlCell` has the same in-memory representation as its inner type `T`. +#[repr(transparent)] +pub struct BqlCell { + value: UnsafeCell, +} + +// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex, +// except it is stored out-of-line +unsafe impl Send for BqlCell {} +unsafe impl Sync for BqlCell {} + +impl Clone for BqlCell { + #[inline] + fn clone(&self) -> BqlCell { + BqlCell::new(self.get()) + } +} + +impl Default for BqlCell { + /// Creates a `BqlCell`, with the `Default` value for T. + #[inline] + fn default() -> BqlCell { + BqlCell::new(Default::default()) + } +} + +impl PartialEq for BqlCell { + #[inline] + fn eq(&self, other: &BqlCell) -> bool { + self.get() == other.get() + } +} + +impl Eq for BqlCell {} + +impl PartialOrd for BqlCell { + #[inline] + fn partial_cmp(&self, other: &BqlCell) -> Option { + self.get().partial_cmp(&other.get()) + } +} + +impl Ord for BqlCell { + #[inline] + fn cmp(&self, other: &BqlCell) -> Ordering { + self.get().cmp(&other.get()) + } +} + +impl From for BqlCell { + /// Creates a new `BqlCell` containing the given value. + fn from(t: T) -> BqlCell { + BqlCell::new(t) + } +} + +impl fmt::Debug for BqlCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl fmt::Display for BqlCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl BqlCell { + /// Creates a new `BqlCell` containing the given value. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlCell; + /// # bql::start_test(); + /// + /// let c = BqlCell::new(5); + /// ``` + #[inline] + pub const fn new(value: T) -> BqlCell { + BqlCell { + value: UnsafeCell::new(value), + } + } + + /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlCell; + /// # bql::start_test(); + /// + /// let c = BqlCell::new(5); + /// + /// c.set(10); + /// ``` + #[inline] + pub fn set(&self, val: T) { + self.replace(val); + } + + /// Replaces the contained value with `val`, and returns the old contained + /// value. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlCell; + /// # bql::start_test(); + /// + /// let cell = BqlCell::new(5); + /// assert_eq!(cell.get(), 5); + /// assert_eq!(cell.replace(10), 5); + /// assert_eq!(cell.get(), 10); + /// ``` + #[inline] + pub fn replace(&self, val: T) -> T { + 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. + mem::replace(unsafe { &mut *self.value.get() }, val) + } + + /// Unwraps the value, consuming the cell. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlCell; + /// # bql::start_test(); + /// + /// let c = BqlCell::new(5); + /// let five = c.into_inner(); + /// + /// assert_eq!(five, 5); + /// ``` + pub fn into_inner(self) -> T { + assert!(crate::is_locked()); + self.value.into_inner() + } +} + +impl BqlCell { + /// Returns a copy of the contained value. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlCell; + /// # bql::start_test(); + /// + /// let c = BqlCell::new(5); + /// + /// let five = c.get(); + /// ``` + #[inline] + pub fn get(&self) -> T { + 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. + unsafe { *self.value.get() } + } +} + +impl BqlCell { + /// Returns a raw pointer to the underlying data in this cell. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlCell; + /// # bql::start_test(); + /// + /// let c = BqlCell::new(5); + /// + /// let ptr = c.as_ptr(); + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut T { + self.value.get() + } +} + +impl BqlCell { + /// Takes the value of the cell, leaving `Default::default()` in its place. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlCell; + /// # bql::start_test(); + /// + /// let c = BqlCell::new(5); + /// let five = c.take(); + /// + /// assert_eq!(five, 5); + /// assert_eq!(c.into_inner(), 0); + /// ``` + pub fn take(&self) -> T { + self.replace(Default::default()) + } +} + +impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); + +/// A mutable memory location with dynamically checked borrow rules, +/// protected by the Big QEMU Lock. +/// +/// See the [module-level documentation](self) for more. +/// +/// # Memory layout +/// +/// `BqlRefCell` starts with the same in-memory representation as its +/// inner type `T`. +#[repr(C)] +pub struct BqlRefCell { + // It is important that this is the first field (which is not the case + // for std::cell::BqlRefCell), so that we can use offset_of! on it. + // UnsafeCell and repr(C) both prevent usage of niches. + value: UnsafeCell, + borrow: Cell, + // Stores the location of the earliest currently active borrow. + // This gets updated whenever we go from having zero borrows + // to having a single borrow. When a borrow occurs, this gets included + // in the panic message + #[cfg(feature = "debug_cell")] + borrowed_at: Cell>>, +} + +// Positive values represent the number of `BqlRef` active. Negative values +// represent the number of `BqlRefMut` active. Right now QEMU's implementation +// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping +// components of a `BqlRefCell` (e.g., different ranges of a slice). +// +// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely +// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of +// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or +// underflow. However, this is not a guarantee, as a pathological program could +// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all +// code must explicitly check for overflow and underflow in order to avoid +// unsafety, or at least behave correctly in the event that overflow or +// underflow happens (e.g., see BorrowRef::new). +type BorrowFlag = isize; +const UNUSED: BorrowFlag = 0; + +#[inline(always)] +const fn is_writing(x: BorrowFlag) -> bool { + x < UNUSED +} + +#[inline(always)] +const fn is_reading(x: BorrowFlag) -> bool { + x > UNUSED +} + +impl BqlRefCell { + /// Creates a new `BqlRefCell` containing `value`. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// ``` + #[inline] + pub const fn new(value: T) -> BqlRefCell { + BqlRefCell { + value: UnsafeCell::new(value), + borrow: Cell::new(UNUSED), + #[cfg(feature = "debug_cell")] + borrowed_at: Cell::new(None), + } + } +} + +// This ensures the panicking code is outlined from `borrow_mut` for +// `BqlRefCell`. +#[inline(never)] +#[cold] +#[cfg(feature = "debug_cell")] +fn panic_already_borrowed(source: &Cell>>) -> ! { + // If a borrow occurred, then we must already have an outstanding borrow, + // so `borrowed_at` will be `Some` + panic!("already borrowed at {:?}", source.take().unwrap()) +} + +#[inline(never)] +#[cold] +#[cfg(not(feature = "debug_cell"))] +fn panic_already_borrowed() -> ! { + panic!("already borrowed") +} + +impl BqlRefCell { + #[inline] + #[allow(clippy::unused_self)] + fn panic_already_borrowed(&self) -> ! { + #[cfg(feature = "debug_cell")] + { + panic_already_borrowed(&self.borrowed_at) + } + #[cfg(not(feature = "debug_cell"))] + { + panic_already_borrowed() + } + } + + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `BqlRef` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlRefCell; + /// # bql::start_test(); + /// + /// let c = BqlRefCell::new(5); + /// + /// let borrowed_five = c.borrow(); + /// let borrowed_five2 = c.borrow(); + /// ``` + /// + /// An example of panic: + /// + /// ```should_panic + /// use bql::BqlRefCell; + /// # bql::start_test(); + /// + /// let c = BqlRefCell::new(5); + /// + /// let m = c.borrow_mut(); + /// let b = c.borrow(); // this causes a panic + /// ``` + #[inline] + #[track_caller] + pub fn borrow(&self) -> BqlRef<'_, T> { + if let Some(b) = BorrowRef::new(&self.borrow) { + // `borrowed_at` is always the *first* active borrow + if b.borrow.get() == 1 { + #[cfg(feature = "debug_cell")] + self.borrowed_at.set(Some(std::panic::Location::caller())); + } + + crate::block_unlock(true); + + // SAFETY: `BorrowRef` ensures that there is only immutable access + // to the value while borrowed. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + BqlRef { value, borrow: b } + } else { + self.panic_already_borrowed() + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `BqlRefMut` or all `BqlRefMut`s + /// derived from it exit scope. The value cannot be borrowed while this + /// borrow is active. + /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlRefCell; + /// # bql::start_test(); + /// + /// let c = BqlRefCell::new("hello".to_owned()); + /// + /// *c.borrow_mut() = "bonjour".to_owned(); + /// + /// assert_eq!(&*c.borrow(), "bonjour"); + /// ``` + /// + /// An example of panic: + /// + /// ```should_panic + /// use bql::BqlRefCell; + /// # bql::start_test(); + /// + /// let c = BqlRefCell::new(5); + /// let m = c.borrow(); + /// + /// let b = c.borrow_mut(); // this causes a panic + /// ``` + #[inline] + #[track_caller] + pub fn borrow_mut(&self) -> BqlRefMut<'_, T> { + if let Some(b) = BorrowRefMut::new(&self.borrow) { + #[cfg(feature = "debug_cell")] + { + self.borrowed_at.set(Some(std::panic::Location::caller())); + } + + // SAFETY: this only adjusts a counter + crate::block_unlock(true); + + // SAFETY: `BorrowRefMut` guarantees unique access. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + BqlRefMut { + value, + _borrow: b, + marker: PhantomData, + } + } else { + self.panic_already_borrowed() + } + } + + /// Returns a raw pointer to the underlying data in this cell. + /// + /// # Examples + /// + /// ``` + /// use bql::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// + /// let ptr = c.as_ptr(); + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut T { + self.value.get() + } +} + +// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is +// stored out-of-line. Even though BqlRefCell includes Cells, they are +// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU +// Lock cannot be released while any borrows is active. +unsafe impl Send for BqlRefCell where T: Send {} +unsafe impl Sync for BqlRefCell {} + +impl Clone for BqlRefCell { + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone(&self) -> BqlRefCell { + BqlRefCell::new(self.borrow().clone()) + } + + /// # Panics + /// + /// Panics if `source` is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone_from(&mut self, source: &Self) { + self.value.get_mut().clone_from(&source.borrow()) + } +} + +impl Default for BqlRefCell { + /// Creates a `BqlRefCell`, with the `Default` value for T. + #[inline] + fn default() -> BqlRefCell { + BqlRefCell::new(Default::default()) + } +} + +impl PartialEq for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn eq(&self, other: &BqlRefCell) -> bool { + *self.borrow() == *other.borrow() + } +} + +impl Eq for BqlRefCell {} + +impl PartialOrd for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn partial_cmp(&self, other: &BqlRefCell) -> Option { + self.borrow().partial_cmp(&*other.borrow()) + } +} + +impl Ord for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn cmp(&self, other: &BqlRefCell) -> Ordering { + self.borrow().cmp(&*other.borrow()) + } +} + +impl From for BqlRefCell { + /// Creates a new `BqlRefCell` containing the given value. + fn from(t: T) -> BqlRefCell { + BqlRefCell::new(t) + } +} + +impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); + +struct BorrowRef<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRef<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + let b = borrow.get().wrapping_add(1); + if !is_reading(b) { + // Incrementing borrow can result in a non-reading value (<= 0) in these cases: + // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read + // borrow due to Rust's reference aliasing rules + // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed + // into isize::MIN (the max amount of writing borrows) so we can't allow an + // additional read borrow because isize can't represent so many read borrows + // (this can only happen if you mem::forget more than a small constant amount + // of `BqlRef`s, which is not good practice) + None + } else { + // Incrementing borrow can result in a reading value (> 0) in these cases: + // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read + // borrow + // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is + // large enough to represent having one more read borrow + borrow.set(b); + Some(BorrowRef { borrow }) + } + } +} + +impl Drop for BorrowRef<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + debug_assert!(is_reading(borrow)); + self.borrow.set(borrow - 1); + crate::block_unlock(false) + } +} + +impl Clone for BorrowRef<'_> { + #[inline] + fn clone(&self) -> Self { + BorrowRef::new(self.borrow).unwrap() + } +} + +/// Wraps a borrowed reference to a value in a `BqlRefCell` box. +/// A wrapper type for an immutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRef<'b, T: 'b> { + // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a + // `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops. + // `NonNull` is also covariant over `T`, just like we would have with `&T`. + value: NonNull, + borrow: BorrowRef<'b>, +} + +impl Deref for BqlRef<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl<'b, T> BqlRef<'b, T> { + /// Copies a `BqlRef`. + /// + /// The `BqlRefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `BqlRef::clone(...)`. A `Clone` implementation or a method would + /// interfere with the widespread use of `r.borrow().clone()` to clone + /// the contents of a `BqlRefCell`. + #[must_use] + #[inline] + #[allow(clippy::should_implement_trait)] + pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> { + BqlRef { + value: orig.value, + borrow: orig.borrow.clone(), + } + } +} + +impl fmt::Debug for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +struct BorrowRefMut<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRefMut<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + // There must currently be no existing references when borrow_mut() is + // called, so we explicitly only allow going from UNUSED to UNUSED - 1. + match borrow.get() { + UNUSED => { + borrow.set(UNUSED - 1); + Some(BorrowRefMut { borrow }) + } + _ => None, + } + } +} + +impl Drop for BorrowRefMut<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + debug_assert!(is_writing(borrow)); + self.borrow.set(borrow + 1); + crate::block_unlock(false) + } +} + +/// A wrapper type for a mutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRefMut<'b, T: 'b> { + // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a + // `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops. + value: NonNull, + _borrow: BorrowRefMut<'b>, + // `NonNull` is covariant over `T`, so we need to reintroduce invariance. + marker: PhantomData<&'b mut T>, +} + +impl Deref for BqlRefMut<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl DerefMut for BqlRefMut<'_, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_mut() } + } +} + +impl fmt::Debug for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} 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/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/common/src/opaque.rs b/rust/common/src/opaque.rs index d25a5f3ae1..97ed3e8452 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 2198fcee9b..8561c4c14a 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -11,6 +11,7 @@ _libpl011_rs = static_library( qemu_api_rs, util_rs, migration_rs, + bql_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 225be34e08..00ae432825 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -4,6 +4,7 @@ use std::{ffi::CStr, mem::size_of}; +use bql::BqlRefCell; use common::{static_assert, uninit_field_mut}; use migration::{ self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 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 8cd70091e6..43a62db0d0 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -8,6 +8,7 @@ _libhpet_rs = static_library( qemu_api_rs, util_rs, migration_rs, + bql_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 1c2253466d..9658e071c2 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -10,6 +10,7 @@ use std::{ slice::from_ref, }; +use bql::{BqlCell, BqlRefCell}; use common::{bitops::IntegerExt, uninit_field_mut}; use migration::{ self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, @@ -20,7 +21,6 @@ use qemu_api::{ 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 @@ pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { impl HPETFwConfig { pub(crate) fn assign_hpet_id() -> Result { - 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 @@ impl HPETFwConfig { } 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 537d54e436..d714aacb7e 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -135,8 +135,8 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and /// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. /// -/// [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html -/// [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html +/// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html +/// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html /// [`Owned`]: ../../qemu_api/qom/struct.Owned.html #[macro_export] macro_rules! vmstate_of { diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 7276e67aa9..6e9427f80c 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -17,11 +17,8 @@ rust-version.workspace = true common = { path = "../common" } migration = { path = "../migration" } util = { path = "../util" } +bql = { path = "../bql" } qemu_api_macros = { path = "../qemu-api-macros" } -[features] -default = ["debug_cell"] -debug_cell = [] - [lints] workspace = true diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a6b5772d19..a47ee6c1a3 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -2,10 +2,6 @@ _qemu_api_cfg = run_command(rustc_args, '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() -if get_option('debug_mutex') - _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] -endif - c_enums = [ 'DeviceCategory', 'GpioPolarity', @@ -51,7 +47,6 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', - 'src/cell.rs', 'src/chardev.rs', 'src/irq.rs', 'src/memory.rs', @@ -65,22 +60,13 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, util_rs, qom, hwcore, chardev], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) -# Doctests are essentially integration tests, so they need the same dependencies. -# Note that running them requires the object files for C code, so place them -# in a separate suite that is run by the "build" CI jobs rather than "check". -rust.doctest('rust-qemu-api-doctests', - _qemu_api_rs, - protocol: 'rust', - dependencies: [qemu_api_rs], - suite: ['doc', 'rust']) - test('rust-qemu-api-integration', executable( 'rust-qemu-api-integration', @@ -88,7 +74,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, util_rs, migration_rs, qemu_api_rs]), + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs deleted file mode 100644 index b80a0fd80b..0000000000 --- a/rust/qemu-api/src/cell.rs +++ /dev/null @@ -1,874 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// This file is based on library/core/src/cell.rs from -// Rust 1.82.0. -// -// 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. - -//! QEMU-specific mutable containers -//! -//! Rust memory safety is based on this rule: Given an object `T`, it is only -//! possible to have one of the following: -//! -//! - Having several immutable references (`&T`) to the object (also known as -//! **aliasing**). -//! - Having one mutable reference (`&mut T`) to the object (also known as -//! **mutability**). -//! -//! This is enforced by the Rust compiler. However, there are situations where -//! this rule is not flexible enough. Sometimes it is required to have multiple -//! references to an object and yet mutate it. In particular, QEMU objects -//! usually have their pointer shared with the "outside world very early in -//! their lifetime", for example when they create their -//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual -//! parts of a device must be made mutable in a controlled manner; this module -//! provides the tools to do so. -//! -//! ## Cell types -//! -//! [`BqlCell`] and [`BqlRefCell`] allow doing this via the Big QEMU Lock. -//! While they are essentially the same single-threaded primitives that are -//! available in `std::cell`, the BQL allows them to be used from a -//! multi-threaded context and to share references across threads, while -//! maintaining Rust's safety guarantees. For this reason, unlike -//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the -//! `Sync` trait. -//! -//! BQL checks are performed in debug builds but can be optimized away in -//! release builds, providing runtime safety during development with no overhead -//! in production. -//! -//! The two provide different ways of handling interior mutability. -//! `BqlRefCell` is best suited for data that is primarily accessed by the -//! device's own methods, where multiple reads and writes can be grouped within -//! a single borrow and a mutable reference can be passed around. Instead, -//! [`BqlCell`] is a better choice when sharing small pieces of data with -//! external code (especially C code), because it provides simple get/set -//! operations that can be used one at a time. -//! -//! Warning: While `BqlCell` and `BqlRefCell` are similar to their `std::cell` -//! counterparts, they are not interchangeable. Using `std::cell` types in -//! QEMU device implementations is usually incorrect and can lead to -//! thread-safety issues. -//! -//! ### Example -//! -//! ``` -//! # use qemu_api::prelude::*; -//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState}; -//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; -//! # const N_GPIOS: usize = 8; -//! # struct PL061Registers { /* ... */ } -//! # unsafe impl ObjectType for PL061State { -//! # type Class = ::Class; -//! # const TYPE_NAME: &'static std::ffi::CStr = c"pl061"; -//! # } -//! struct PL061State { -//! parent_obj: ParentField, -//! -//! // Configuration is read-only after initialization -//! pullups: u32, -//! pulldowns: u32, -//! -//! // Single values shared with C code use BqlCell, in this case via InterruptSource -//! out: [InterruptSource; N_GPIOS], -//! interrupt: InterruptSource, -//! -//! // Larger state accessed by device methods uses BqlRefCell or Mutex -//! registers: BqlRefCell, -//! } -//! ``` -//! -//! ### `BqlCell` -//! -//! [`BqlCell`] implements interior mutability by moving values in and out of -//! the cell. That is, an `&mut T` to the inner value can never be obtained as -//! long as the cell is shared. The value itself cannot be directly obtained -//! without copying it, cloning it, or replacing it with something else. This -//! type provides the following methods, all of which can be called only while -//! the BQL is held: -//! -//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method -//! retrieves the current interior value by duplicating it. -//! - For types that implement [`Default`], the [`take`](BqlCell::take) method -//! replaces the current interior value with [`Default::default()`] and -//! returns the replaced value. -//! - All types have: -//! - [`replace`](BqlCell::replace): replaces the current interior value and -//! returns the replaced value. -//! - [`set`](BqlCell::set): this method replaces the interior value, -//! dropping the replaced value. -//! -//! ### `BqlRefCell` -//! -//! [`BqlRefCell`] uses Rust's lifetimes to implement "dynamic borrowing", a -//! process whereby one can claim temporary, exclusive, mutable access to the -//! inner value: -//! -//! ```ignore -//! fn clear_interrupts(&self, val: u32) { -//! // A mutable borrow gives read-write access to the registers -//! let mut regs = self.registers.borrow_mut(); -//! let old = regs.interrupt_status(); -//! regs.update_interrupt_status(old & !val); -//! } -//! ``` -//! -//! Borrows for `BqlRefCell`s are tracked at _runtime_, unlike Rust's native -//! reference types which are entirely tracked statically, at compile time. -//! 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, - fmt, - marker::PhantomData, - mem, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; - -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 -/// -/// `BqlCell` has the same in-memory representation as its inner type `T`. -#[repr(transparent)] -pub struct BqlCell { - value: UnsafeCell, -} - -// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex, -// except it is stored out-of-line -unsafe impl Send for BqlCell {} -unsafe impl Sync for BqlCell {} - -impl Clone for BqlCell { - #[inline] - fn clone(&self) -> BqlCell { - BqlCell::new(self.get()) - } -} - -impl Default for BqlCell { - /// Creates a `BqlCell`, with the `Default` value for T. - #[inline] - fn default() -> BqlCell { - BqlCell::new(Default::default()) - } -} - -impl PartialEq for BqlCell { - #[inline] - fn eq(&self, other: &BqlCell) -> bool { - self.get() == other.get() - } -} - -impl Eq for BqlCell {} - -impl PartialOrd for BqlCell { - #[inline] - fn partial_cmp(&self, other: &BqlCell) -> Option { - self.get().partial_cmp(&other.get()) - } -} - -impl Ord for BqlCell { - #[inline] - fn cmp(&self, other: &BqlCell) -> Ordering { - self.get().cmp(&other.get()) - } -} - -impl From for BqlCell { - /// Creates a new `BqlCell` containing the given value. - fn from(t: T) -> BqlCell { - BqlCell::new(t) - } -} - -impl fmt::Debug for BqlCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } -} - -impl fmt::Display for BqlCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } -} - -impl BqlCell { - /// Creates a new `BqlCell` containing the given value. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlCell::new(5); - /// ``` - #[inline] - pub const fn new(value: T) -> BqlCell { - BqlCell { - value: UnsafeCell::new(value), - } - } - - /// Sets the contained value. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlCell::new(5); - /// - /// c.set(10); - /// ``` - #[inline] - pub fn set(&self, val: T) { - self.replace(val); - } - - /// Replaces the contained value with `val`, and returns the old contained - /// value. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let cell = BqlCell::new(5); - /// assert_eq!(cell.get(), 5); - /// assert_eq!(cell.replace(10), 5); - /// assert_eq!(cell.get(), 10); - /// ``` - #[inline] - pub fn replace(&self, val: T) -> T { - assert!(bql_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. - mem::replace(unsafe { &mut *self.value.get() }, val) - } - - /// Unwraps the value, consuming the cell. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlCell::new(5); - /// let five = c.into_inner(); - /// - /// assert_eq!(five, 5); - /// ``` - pub fn into_inner(self) -> T { - assert!(bql_locked()); - self.value.into_inner() - } -} - -impl BqlCell { - /// Returns a copy of the contained value. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlCell::new(5); - /// - /// let five = c.get(); - /// ``` - #[inline] - pub fn get(&self) -> T { - assert!(bql_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. - unsafe { *self.value.get() } - } -} - -impl BqlCell { - /// Returns a raw pointer to the underlying data in this cell. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlCell::new(5); - /// - /// let ptr = c.as_ptr(); - /// ``` - #[inline] - pub const fn as_ptr(&self) -> *mut T { - self.value.get() - } -} - -impl BqlCell { - /// Takes the value of the cell, leaving `Default::default()` in its place. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlCell::new(5); - /// let five = c.take(); - /// - /// assert_eq!(five, 5); - /// assert_eq!(c.into_inner(), 0); - /// ``` - pub fn take(&self) -> T { - self.replace(Default::default()) - } -} - -impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); - -/// A mutable memory location with dynamically checked borrow rules, -/// protected by the Big QEMU Lock. -/// -/// See the [module-level documentation](self) for more. -/// -/// # Memory layout -/// -/// `BqlRefCell` starts with the same in-memory representation as its -/// inner type `T`. -#[repr(C)] -pub struct BqlRefCell { - // It is important that this is the first field (which is not the case - // for std::cell::BqlRefCell), so that we can use offset_of! on it. - // UnsafeCell and repr(C) both prevent usage of niches. - value: UnsafeCell, - borrow: Cell, - // Stores the location of the earliest currently active borrow. - // This gets updated whenever we go from having zero borrows - // to having a single borrow. When a borrow occurs, this gets included - // in the panic message - #[cfg(feature = "debug_cell")] - borrowed_at: Cell>>, -} - -// Positive values represent the number of `BqlRef` active. Negative values -// represent the number of `BqlRefMut` active. Right now QEMU's implementation -// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping -// components of a `BqlRefCell` (e.g., different ranges of a slice). -// -// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely -// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of -// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or -// underflow. However, this is not a guarantee, as a pathological program could -// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all -// code must explicitly check for overflow and underflow in order to avoid -// unsafety, or at least behave correctly in the event that overflow or -// underflow happens (e.g., see BorrowRef::new). -type BorrowFlag = isize; -const UNUSED: BorrowFlag = 0; - -#[inline(always)] -const fn is_writing(x: BorrowFlag) -> bool { - x < UNUSED -} - -#[inline(always)] -const fn is_reading(x: BorrowFlag) -> bool { - x > UNUSED -} - -impl BqlRefCell { - /// Creates a new `BqlRefCell` containing `value`. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlRefCell; - /// - /// let c = BqlRefCell::new(5); - /// ``` - #[inline] - pub const fn new(value: T) -> BqlRefCell { - BqlRefCell { - value: UnsafeCell::new(value), - borrow: Cell::new(UNUSED), - #[cfg(feature = "debug_cell")] - borrowed_at: Cell::new(None), - } - } -} - -// This ensures the panicking code is outlined from `borrow_mut` for -// `BqlRefCell`. -#[inline(never)] -#[cold] -#[cfg(feature = "debug_cell")] -fn panic_already_borrowed(source: &Cell>>) -> ! { - // If a borrow occurred, then we must already have an outstanding borrow, - // so `borrowed_at` will be `Some` - panic!("already borrowed at {:?}", source.take().unwrap()) -} - -#[inline(never)] -#[cold] -#[cfg(not(feature = "debug_cell"))] -fn panic_already_borrowed() -> ! { - panic!("already borrowed") -} - -impl BqlRefCell { - #[inline] - #[allow(clippy::unused_self)] - fn panic_already_borrowed(&self) -> ! { - #[cfg(feature = "debug_cell")] - { - panic_already_borrowed(&self.borrowed_at) - } - #[cfg(not(feature = "debug_cell"))] - { - panic_already_borrowed() - } - } - - /// Immutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `BqlRef` exits scope. Multiple - /// immutable borrows can be taken out at the same time. - /// - /// # Panics - /// - /// Panics if the value is currently mutably borrowed. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlRefCell::new(5); - /// - /// let borrowed_five = c.borrow(); - /// let borrowed_five2 = c.borrow(); - /// ``` - /// - /// An example of panic: - /// - /// ```should_panic - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlRefCell::new(5); - /// - /// let m = c.borrow_mut(); - /// let b = c.borrow(); // this causes a panic - /// ``` - #[inline] - #[track_caller] - pub fn borrow(&self) -> BqlRef<'_, T> { - if let Some(b) = BorrowRef::new(&self.borrow) { - // `borrowed_at` is always the *first* active borrow - if b.borrow.get() == 1 { - #[cfg(feature = "debug_cell")] - self.borrowed_at.set(Some(std::panic::Location::caller())); - } - - bql_block_unlock(true); - - // SAFETY: `BorrowRef` ensures that there is only immutable access - // to the value while borrowed. - let value = unsafe { NonNull::new_unchecked(self.value.get()) }; - BqlRef { value, borrow: b } - } else { - self.panic_already_borrowed() - } - } - - /// Mutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `BqlRefMut` or all `BqlRefMut`s - /// derived from it exit scope. The value cannot be borrowed while this - /// borrow is active. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlRefCell::new("hello".to_owned()); - /// - /// *c.borrow_mut() = "bonjour".to_owned(); - /// - /// assert_eq!(&*c.borrow(), "bonjour"); - /// ``` - /// - /// An example of panic: - /// - /// ```should_panic - /// use qemu_api::cell::BqlRefCell; - /// # qemu_api::cell::bql_start_test(); - /// - /// let c = BqlRefCell::new(5); - /// let m = c.borrow(); - /// - /// let b = c.borrow_mut(); // this causes a panic - /// ``` - #[inline] - #[track_caller] - pub fn borrow_mut(&self) -> BqlRefMut<'_, T> { - if let Some(b) = BorrowRefMut::new(&self.borrow) { - #[cfg(feature = "debug_cell")] - { - self.borrowed_at.set(Some(std::panic::Location::caller())); - } - - // SAFETY: this only adjusts a counter - bql_block_unlock(true); - - // SAFETY: `BorrowRefMut` guarantees unique access. - let value = unsafe { NonNull::new_unchecked(self.value.get()) }; - BqlRefMut { - value, - _borrow: b, - marker: PhantomData, - } - } else { - self.panic_already_borrowed() - } - } - - /// Returns a raw pointer to the underlying data in this cell. - /// - /// # Examples - /// - /// ``` - /// use qemu_api::cell::BqlRefCell; - /// - /// let c = BqlRefCell::new(5); - /// - /// let ptr = c.as_ptr(); - /// ``` - #[inline] - pub const fn as_ptr(&self) -> *mut T { - self.value.get() - } -} - -// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is -// stored out-of-line. Even though BqlRefCell includes Cells, they are -// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU -// Lock cannot be released while any borrows is active. -unsafe impl Send for BqlRefCell where T: Send {} -unsafe impl Sync for BqlRefCell {} - -impl Clone for BqlRefCell { - /// # Panics - /// - /// Panics if the value is currently mutably borrowed. - #[inline] - #[track_caller] - fn clone(&self) -> BqlRefCell { - BqlRefCell::new(self.borrow().clone()) - } - - /// # Panics - /// - /// Panics if `source` is currently mutably borrowed. - #[inline] - #[track_caller] - fn clone_from(&mut self, source: &Self) { - self.value.get_mut().clone_from(&source.borrow()) - } -} - -impl Default for BqlRefCell { - /// Creates a `BqlRefCell`, with the `Default` value for T. - #[inline] - fn default() -> BqlRefCell { - BqlRefCell::new(Default::default()) - } -} - -impl PartialEq for BqlRefCell { - /// # Panics - /// - /// Panics if the value in either `BqlRefCell` is currently mutably - /// borrowed. - #[inline] - fn eq(&self, other: &BqlRefCell) -> bool { - *self.borrow() == *other.borrow() - } -} - -impl Eq for BqlRefCell {} - -impl PartialOrd for BqlRefCell { - /// # Panics - /// - /// Panics if the value in either `BqlRefCell` is currently mutably - /// borrowed. - #[inline] - fn partial_cmp(&self, other: &BqlRefCell) -> Option { - self.borrow().partial_cmp(&*other.borrow()) - } -} - -impl Ord for BqlRefCell { - /// # Panics - /// - /// Panics if the value in either `BqlRefCell` is currently mutably - /// borrowed. - #[inline] - fn cmp(&self, other: &BqlRefCell) -> Ordering { - self.borrow().cmp(&*other.borrow()) - } -} - -impl From for BqlRefCell { - /// Creates a new `BqlRefCell` containing the given value. - fn from(t: T) -> BqlRefCell { - BqlRefCell::new(t) - } -} - -impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); - -struct BorrowRef<'b> { - borrow: &'b Cell, -} - -impl<'b> BorrowRef<'b> { - #[inline] - fn new(borrow: &'b Cell) -> Option> { - let b = borrow.get().wrapping_add(1); - if !is_reading(b) { - // Incrementing borrow can result in a non-reading value (<= 0) in these cases: - // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read - // borrow due to Rust's reference aliasing rules - // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed - // into isize::MIN (the max amount of writing borrows) so we can't allow an - // additional read borrow because isize can't represent so many read borrows - // (this can only happen if you mem::forget more than a small constant amount - // of `BqlRef`s, which is not good practice) - None - } else { - // Incrementing borrow can result in a reading value (> 0) in these cases: - // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read - // borrow - // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is - // large enough to represent having one more read borrow - borrow.set(b); - Some(BorrowRef { borrow }) - } - } -} - -impl Drop for BorrowRef<'_> { - #[inline] - fn drop(&mut self) { - let borrow = self.borrow.get(); - debug_assert!(is_reading(borrow)); - self.borrow.set(borrow - 1); - bql_block_unlock(false) - } -} - -impl Clone for BorrowRef<'_> { - #[inline] - fn clone(&self) -> Self { - BorrowRef::new(self.borrow).unwrap() - } -} - -/// Wraps a borrowed reference to a value in a `BqlRefCell` box. -/// A wrapper type for an immutably borrowed value from a `BqlRefCell`. -/// -/// See the [module-level documentation](self) for more. -pub struct BqlRef<'b, T: 'b> { - // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a - // `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops. - // `NonNull` is also covariant over `T`, just like we would have with `&T`. - value: NonNull, - borrow: BorrowRef<'b>, -} - -impl Deref for BqlRef<'_, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - // SAFETY: the value is accessible as long as we hold our borrow. - unsafe { self.value.as_ref() } - } -} - -impl<'b, T> BqlRef<'b, T> { - /// Copies a `BqlRef`. - /// - /// The `BqlRefCell` is already immutably borrowed, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `BqlRef::clone(...)`. A `Clone` implementation or a method would - /// interfere with the widespread use of `r.borrow().clone()` to clone - /// the contents of a `BqlRefCell`. - #[must_use] - #[inline] - #[allow(clippy::should_implement_trait)] - pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> { - BqlRef { - value: orig.value, - borrow: orig.borrow.clone(), - } - } -} - -impl fmt::Debug for BqlRef<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl fmt::Display for BqlRef<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -struct BorrowRefMut<'b> { - borrow: &'b Cell, -} - -impl<'b> BorrowRefMut<'b> { - #[inline] - fn new(borrow: &'b Cell) -> Option> { - // There must currently be no existing references when borrow_mut() is - // called, so we explicitly only allow going from UNUSED to UNUSED - 1. - match borrow.get() { - UNUSED => { - borrow.set(UNUSED - 1); - Some(BorrowRefMut { borrow }) - } - _ => None, - } - } -} - -impl Drop for BorrowRefMut<'_> { - #[inline] - fn drop(&mut self) { - let borrow = self.borrow.get(); - debug_assert!(is_writing(borrow)); - self.borrow.set(borrow + 1); - bql_block_unlock(false) - } -} - -/// A wrapper type for a mutably borrowed value from a `BqlRefCell`. -/// -/// See the [module-level documentation](self) for more. -pub struct BqlRefMut<'b, T: 'b> { - // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a - // `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops. - value: NonNull, - _borrow: BorrowRefMut<'b>, - // `NonNull` is covariant over `T`, so we need to reintroduce invariance. - marker: PhantomData<&'b mut T>, -} - -impl Deref for BqlRefMut<'_, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - // SAFETY: the value is accessible as long as we hold our borrow. - unsafe { self.value.as_ref() } - } -} - -impl DerefMut for BqlRefMut<'_, T> { - #[inline] - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the value is accessible as long as we hold our borrow. - unsafe { self.value.as_mut() } - } -} - -impl fmt::Debug for BqlRefMut<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl fmt::Display for BqlRefMut<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 5a351dcecb..2ec90cc0b2 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -18,13 +18,10 @@ use std::{ 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 { - 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 @@ impl Write for BqlRefMut<'_, bindings::CharBackend> { } 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 @@ impl CharBackend { /// 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 @@ use std::{ 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 55386f6697..6cd9e5b990 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,7 +13,6 @@ pub mod bindings; #[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 c81ae7cf45..74a82b8710 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -16,7 +16,6 @@ use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - cell::bql_locked, chardev::Chardev, irq::InterruptSource, prelude::*, @@ -322,7 +321,7 @@ impl DeviceState { cb: Option, events: ClockEvent, ) -> Owned { - assert!(bql_locked()); + assert!(bql::is_locked()); // SAFETY: the clock is heap allocated, but qdev_init_clock_in() // does not gift the reference to its caller; so use Owned::from to @@ -393,7 +392,7 @@ where Self::Target: IsA, { fn prop_set_chr(&self, propname: &str, chr: &Owned) { - 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 7f2f7797e4..032701af65 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -105,12 +105,9 @@ pub use bindings::ObjectClass; 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 ObjectDeref for Owned {} impl Drop for Owned { 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` and its clones. @@ -897,7 +894,7 @@ impl> fmt::Debug for Owned { pub trait ObjectClassMethods: IsA { /// Return a new reference counted instance of this class fn new() -> Owned { - 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 common::Opaque; use crate::{ bindings, - cell::bql_locked, irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, @@ -56,7 +55,7 @@ where /// 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 @@ where /// 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 @@ where // TODO: do we want a type like GuestAddress here? fn mmio_addr(&self, id: u32) -> Option { - 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. let sbd = unsafe { *self.upcast().as_ptr() }; @@ -89,7 +88,7 @@ where // 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 @@ where // object_property_set_link) adds a reference to the IRQState, // which can prolong its life fn connect_irq(&self, id: u32, irq: &Owned) { - assert!(bql_locked()); + assert!(bql::is_locked()); let id: i32 = id.try_into().unwrap(); let irq: &IRQState = irq; unsafe { @@ -110,7 +109,7 @@ where 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 92e3534d3c..e72ba08aef 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -4,9 +4,9 @@ use std::{ffi::CStr, ptr::addr_of}; +use bql::BqlCell; use migration::{VMStateDescription, VMStateDescriptionBuilder}; use qemu_api::{ - cell::{self, BqlCell}, prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, qom::{ObjectImpl, ParentField}, @@ -94,7 +94,7 @@ impl DummyChildClass { fn init_qom() { static ONCE: BqlCell = 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 47fc15149b..fa9bbd6a12 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -9,6 +9,7 @@ use std::{ slice, }; +use bql::BqlCell; use common::Opaque; use migration::{ bindings::{ @@ -19,7 +20,6 @@ use migration::{ vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, }; -use qemu_api::cell::BqlCell; const FOO_ARRAY_MAX: usize = 3; -- cgit 1.4.1 From fcf4c00b4d73185db9239b1a6f03289f6211e142 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:53 +0200 Subject: rust: split "qom" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250827104147.717203-13-marcandre.lureau@redhat.com Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 14 + rust/Cargo.toml | 1 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 3 +- rust/meson.build | 1 + rust/migration/src/vmstate.rs | 2 +- rust/qemu-api-macros/src/lib.rs | 4 +- rust/qemu-api-macros/src/tests.rs | 4 +- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 15 +- rust/qemu-api/src/bindings.rs | 1 + rust/qemu-api/src/chardev.rs | 3 +- rust/qemu-api/src/irq.rs | 10 +- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/memory.rs | 7 +- rust/qemu-api/src/prelude.rs | 11 - rust/qemu-api/src/qdev.rs | 5 +- rust/qemu-api/src/qom.rs | 951 -------------------------------------- rust/qemu-api/src/sysbus.rs | 4 +- rust/qemu-api/tests/tests.rs | 3 +- rust/qom/Cargo.toml | 23 + rust/qom/build.rs | 1 + rust/qom/meson.build | 43 ++ rust/qom/src/bindings.rs | 25 + rust/qom/src/lib.rs | 11 + rust/qom/src/prelude.rs | 12 + rust/qom/src/qom.rs | 951 ++++++++++++++++++++++++++++++++++++++ rust/qom/wrapper.h | 27 ++ 33 files changed, 1148 insertions(+), 993 deletions(-) delete mode 100644 rust/qemu-api/src/qom.rs 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 create mode 100644 rust/qom/src/qom.rs create mode 100644 rust/qom/wrapper.h (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index a55d5c95d7..c7bd02aef1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3520,6 +3520,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/Cargo.lock b/rust/Cargo.lock index 73ca9582a5..442eadf08f 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -82,6 +82,7 @@ dependencies = [ "migration", "qemu_api", "qemu_api_macros", + "qom", "util", ] @@ -121,6 +122,7 @@ dependencies = [ "migration", "qemu_api", "qemu_api_macros", + "qom", "util", ] @@ -164,6 +166,7 @@ dependencies = [ "common", "migration", "qemu_api_macros", + "qom", "util", ] @@ -176,6 +179,17 @@ dependencies = [ "syn", ] +[[package]] +name = "qom" +version = "0.1.0" +dependencies = [ + "bql", + "common", + "migration", + "qemu_api_macros", + "util", +] + [[package]] name = "quote" version = "1.0.36" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8be90da8ff..0516c16591 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 8561c4c14a..af9393c9da 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -13,6 +13,7 @@ _libpl011_rs = static_library( migration_rs, bql_rs, qemu_api_macros, + qom_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 00ae432825..63651b9dcd 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -16,9 +16,9 @@ use qemu_api::{ memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, Owned, ParentField, ParentInit}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; +use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 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 43a62db0d0..50ccdee4a9 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -10,6 +10,7 @@ _libhpet_rs = static_library( migration_rs, bql_rs, qemu_api_macros, + qom_rs, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 9658e071c2..404569aa2d 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -27,10 +27,9 @@ use qemu_api::{ }, 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 d714aacb7e..c05c4a1fd6 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -137,7 +137,7 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> 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 67650a9a26..e643e57ebd 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -98,11 +98,11 @@ fn derive_object_or_error(input: DeriveInput) -> Result::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 8e71ac6e67..76e6c57479 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -168,11 +168,11 @@ fn test_derive_object() { ::common::assert_field_type!( Foo, _unused, - ::qemu_api::qom::ParentField<::ParentType> + ::qom::ParentField<::ParentType> ); ::util::module_init! { MODULE_INIT_QOM => unsafe { - ::qemu_api::bindings::type_register_static(&::TYPE_INFO); + ::qom::type_register_static(&::TYPE_INFO); } } } diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 6e9427f80c..9d11becb28 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -18,6 +18,7 @@ common = { path = "../common" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } +qom = { path = "../qom" } qemu_api_macros = { path = "../qemu-api-macros" } [lints] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a47ee6c1a3..11e43bb646 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -22,9 +22,15 @@ foreach enum : c_bitfields _qemu_api_bindgen_args += ['--bitfield-enum', enum] endforeach -_qemu_api_bindgen_args += ['--blocklist-type', 'VMStateDescription'] +blocked_type = [ + 'ObjectClass', + 'VMStateDescription', + 'Error', +] +foreach type: blocked_type + _qemu_api_bindgen_args += ['--blocklist-type', type] +endforeach -_qemu_api_bindgen_args += ['--blocklist-type', 'Error'] # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # # Rust bindings generation with `bindgen` might fail in some cases where the @@ -52,7 +58,6 @@ _qemu_api_rs = static_library( 'src/memory.rs', 'src/prelude.rs', 'src/qdev.rs', - 'src/qom.rs', 'src/sysbus.rs', ], {'.' : _qemu_api_bindings_inc_rs}, @@ -61,7 +66,7 @@ _qemu_api_rs = static_library( rust_abi: 'rust', rust_args: _qemu_api_cfg, dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - util_rs, qom, hwcore, chardev], + qom_rs, util_rs, hwcore, chardev], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], @@ -74,7 +79,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api_rs]), + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 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 2ec90cc0b2..072d806e4a 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -20,8 +20,9 @@ use std::{ 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 std::{ 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 @@ pub struct IRQState(Opaque); /// /// 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 6cd9e5b990..0541050e66 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -17,7 +17,6 @@ pub mod chardev; pub mod irq; pub mod memory; pub mod qdev; -pub mod qom; pub mod sysbus; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index f790cb5fd2..ecbbd9b604 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -11,11 +11,9 @@ use std::{ 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( 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 74a82b8710..3daf9dda2b 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -12,14 +12,13 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use common::{callbacks::FnCall, Opaque}; use migration::{impl_vmstate_c_struct, VMStateDescription}; +use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, chardev::Chardev, irq::InterruptSource, - prelude::*, - qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, }; /// A safe wrapper around [`bindings::Clock`]. @@ -291,6 +290,7 @@ unsafe impl ObjectType for DeviceState { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } + qom_isa!(DeviceState: Object); /// Initialization methods take a [`ParentInit`] and can be called as @@ -453,6 +453,7 @@ unsafe impl ObjectType for Clock { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; } + qom_isa!(Clock: Object); impl_vmstate_c_struct!(Clock, bindings::vmstate_clock); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs deleted file mode 100644 index 032701af65..0000000000 --- a/rust/qemu-api/src/qom.rs +++ /dev/null @@ -1,951 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings to access QOM functionality from Rust. -//! -//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU -//! devices. This module makes QOM's features available in Rust through three -//! main mechanisms: -//! -//! * Automatic creation and registration of `TypeInfo` for classes that are -//! written in Rust, as well as mapping between Rust traits and QOM vtables. -//! -//! * Type-safe casting between parent and child classes, through the [`IsA`] -//! trait and methods such as [`upcast`](ObjectCast::upcast) and -//! [`downcast`](ObjectCast::downcast). -//! -//! * Automatic delegation of parent class methods to child classes. When a -//! trait uses [`IsA`] as a bound, its contents become available to all child -//! classes through blanket implementations. This works both for class methods -//! and for instance methods accessed through references or smart pointers. -//! -//! # Structure of a class -//! -//! A leaf class only needs a struct holding instance state. The struct must -//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` -//! traits that exist for its superclasses. -//! -//! If a class has subclasses, it will also provide a struct for instance data, -//! with the same characteristics as for concrete classes, but it also needs -//! additional components to support virtual methods: -//! -//! * a struct for class data, for example `DeviceClass`. This corresponds to -//! the C "class struct" and holds the vtable that is used by instances of the -//! class and its subclasses. It must start with its parent's class struct. -//! -//! * a trait for virtual method implementations, for example `DeviceImpl`. -//! Child classes implement this trait to provide their own behavior for -//! virtual methods. The trait's methods take `&self` to access instance data. -//! The traits have the appropriate specialization of `IsA<>` as a supertrait, -//! for example `IsA` for `DeviceImpl`. -//! -//! * a trait for instance methods, for example `DeviceMethods`. This trait is -//! automatically implemented for any reference or smart pointer to a device -//! instance. It calls into the vtable provides access across all subclasses -//! to methods defined for the class. -//! -//! * optionally, a trait for class methods, for example `DeviceClassMethods`. -//! This provides access to class-wide functionality that doesn't depend on -//! instance data. Like instance methods, these are automatically inherited by -//! child classes. -//! -//! # Class structures -//! -//! Each QOM class that has virtual methods describes them in a -//! _class struct_. Class structs include a parent field corresponding -//! to the vtable of the parent class, all the way up to [`ObjectClass`]. -//! -//! As mentioned above, virtual methods are defined via traits such as -//! `DeviceImpl`. Class structs do not define any trait but, conventionally, -//! all of them have a `class_init` method to initialize the virtual methods -//! based on the trait and then call the same method on the superclass. -//! -//! ```ignore -//! impl YourSubclassClass -//! { -//! pub fn class_init(&mut self) { -//! ... -//! klass.parent_class::class_init(); -//! } -//! } -//! ``` -//! -//! If a class implements a QOM interface. In that case, the function must -//! contain, for each interface, an extra forwarding call as follows: -//! -//! ```ignore -//! ResettableClass::cast::(self).class_init::(); -//! ``` -//! -//! These `class_init` functions are methods on the class rather than a trait, -//! because the bound on `T` (`DeviceImpl` in this case), will change for every -//! class struct. The functions are pointed to by the -//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default -//! implementation, in most cases it will be enough to write it as follows: -//! -//! ```ignore -//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::; -//! ``` -//! -//! This design incurs a small amount of code duplication but, by not using -//! traits, it allows the flexibility of implementing bindings in any crate, -//! without incurring into violations of orphan rules for traits. - -use std::{ - ffi::{c_void, CStr}, - fmt, - marker::PhantomData, - mem::{ManuallyDrop, MaybeUninit}, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; - -pub use bindings::ObjectClass; -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, -}; - -/// A safe wrapper around [`bindings::Object`]. -#[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] -pub struct Object(Opaque); - -unsafe impl Send for Object {} -unsafe impl Sync for Object {} - -/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct -/// or indirect parent of `Self`). -/// -/// # Safety -/// -/// The struct `Self` must be `#[repr(C)]` and must begin, directly or -/// indirectly, with a field of type `P`. This ensures that invalid casts, -/// which rely on `IsA<>` for static checking, are rejected at compile time. -pub unsafe trait IsA: ObjectType {} - -// SAFETY: it is always safe to cast to your own type -unsafe impl IsA for T {} - -/// Macro to mark superclasses of QOM classes. This enables type-safe -/// up- and downcasting. -/// -/// # Safety -/// -/// This macro is a thin wrapper around the [`IsA`] trait and performs -/// no checking whatsoever of what is declared. It is the caller's -/// responsibility to have $struct begin, directly or indirectly, with -/// a field of type `$parent`. -#[macro_export] -macro_rules! qom_isa { - ($struct:ty : $($parent:ty),* ) => { - $( - // SAFETY: it is the caller responsibility to have $parent as the - // first field - unsafe impl $crate::qom::IsA<$parent> for $struct {} - - impl AsRef<$parent> for $struct { - fn as_ref(&self) -> &$parent { - // SAFETY: follows the same rules as for IsA, which is - // declared above. - let ptr: *const Self = self; - unsafe { &*ptr.cast::<$parent>() } - } - } - )* - }; -} - -/// This is the same as [`ManuallyDrop`](std::mem::ManuallyDrop), though -/// it hides the standard methods of `ManuallyDrop`. -/// -/// The first field of an `ObjectType` must be of type `ParentField`. -/// (Technically, this is only necessary if there is at least one Rust -/// superclass in the hierarchy). This is to ensure that the parent field is -/// dropped after the subclass; this drop order is enforced by the C -/// `object_deinit` function. -/// -/// # Examples -/// -/// ```ignore -/// #[repr(C)] -/// #[derive(qemu_api_macros::Object)] -/// pub struct MyDevice { -/// parent: ParentField, -/// ... -/// } -/// ``` -#[derive(Debug)] -#[repr(transparent)] -pub struct ParentField(std::mem::ManuallyDrop); - -impl Deref for ParentField { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for ParentField { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl fmt::Display for ParentField { - #[inline(always)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0.fmt(f) - } -} - -/// This struct knows that the superclasses of the object have already been -/// initialized. -/// -/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a -/// technique that is found in several crates, the main ones probably being -/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) -/// and `generativity`. -/// -/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to -/// the lifetime argument `'init`. This, together with the `for<'...>` in -/// `[ParentInit::with]`, block any attempt of the compiler to be creative when -/// operating on types of type `ParentInit` and to extend their lifetimes. In -/// particular, it ensures that the `ParentInit` cannot be made to outlive the -/// `rust_instance_init()` function that creates it, and therefore that the -/// `&'init T` reference is valid. -/// -/// This implementation of the same concept, without the QOM baggage, can help -/// understanding the effect: -/// -/// ``` -/// use std::marker::PhantomData; -/// -/// #[derive(PartialEq, Eq)] -/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); -/// -/// impl<'closure, T: Copy> Jail<'closure, T> { -/// fn get(&self) -> T { -/// *self.0 -/// } -/// -/// #[inline] -/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { -/// let parent_init = Jail(&v, PhantomData); -/// f(parent_init) -/// } -/// } -/// ``` -/// -/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the -/// closure: -/// -/// ```ignore -/// let x = 42; -/// let escape = Jail::with(&x, |token1| { -/// println!("{}", token1.get()); -/// // fails to compile... -/// token1 -/// }); -/// // ... so you cannot do this: -/// println!("{}", escape.get()); -/// ``` -/// -/// Likewise, in the QOM case the `ParentInit` cannot be moved out of -/// `instance_init()`. Without this trick it would be possible to stash a -/// `ParentInit` and use it later to access uninitialized memory. -/// -/// Here is another example, showing how separately-created "identities" stay -/// isolated: -/// -/// ```ignore -/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { -/// fn clone(&self) -> Jail<'closure, T> { -/// Jail(self.0, PhantomData) -/// } -/// } -/// -/// fn main() { -/// Jail::with(42, |token1| { -/// // this works and returns true: the clone has the same "identity" -/// println!("{}", token1 == token1.clone()); -/// Jail::with(42, |token2| { -/// // here the outer token remains accessible... -/// println!("{}", token1.get()); -/// // ... but the two are separate: this fails to compile: -/// println!("{}", token1 == token2); -/// }); -/// }); -/// } -/// ``` -pub struct ParentInit<'init, T>( - &'init mut MaybeUninit, - PhantomData &'init ()>, -); - -impl<'init, T> ParentInit<'init, T> { - #[inline] - pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { - let parent_init = ParentInit(obj, PhantomData); - f(parent_init) - } -} - -impl ParentInit<'_, T> { - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// Fields beyond `Object` could be uninitialized and it's your - /// responsibility to avoid that they're used when the pointer is - /// dereferenced, either directly or through a cast. - pub const fn as_object_mut_ptr(&self) -> *mut bindings::Object { - self.as_object_ptr().cast_mut() - } - - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// Fields beyond `Object` could be uninitialized and it's your - /// responsibility to avoid that they're used when the pointer is - /// dereferenced, either directly or through a cast. - pub const fn as_object_ptr(&self) -> *const bindings::Object { - self.0.as_ptr().cast() - } -} - -impl<'a, T: ObjectImpl> ParentInit<'a, T> { - /// Convert from a derived type to one of its parent types, which - /// have already been initialized. - /// - /// # Safety - /// - /// Structurally this is always a safe operation; the [`IsA`] trait - /// provides static verification trait that `Self` dereferences to `U` or - /// a child of `U`, and only parent types of `T` are allowed. - /// - /// However, while the fields of the resulting reference are initialized, - /// calls might use uninitialized fields of the subclass. It is your - /// responsibility to avoid this. - pub const unsafe fn upcast(&self) -> &'a U - where - T::ParentType: IsA, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait; - // the parent has been initialized before `instance_init `is called - unsafe { &*(self.0.as_ptr().cast::()) } - } - - /// Convert from a derived type to one of its parent types, which - /// have already been initialized. - /// - /// # Safety - /// - /// Structurally this is always a safe operation; the [`IsA`] trait - /// provides static verification trait that `Self` dereferences to `U` or - /// a child of `U`, and only parent types of `T` are allowed. - /// - /// However, while the fields of the resulting reference are initialized, - /// calls might use uninitialized fields of the subclass. It is your - /// responsibility to avoid this. - pub unsafe fn upcast_mut(&mut self) -> &'a mut U - where - T::ParentType: IsA, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait; - // the parent has been initialized before `instance_init `is called - unsafe { &mut *(self.0.as_mut_ptr().cast::()) } - } -} - -impl Deref for ParentInit<'_, T> { - type Target = MaybeUninit; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl DerefMut for ParentInit<'_, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} - -unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { - let mut state = NonNull::new(obj).unwrap().cast::>(); - - // SAFETY: obj is an instance of T, since rust_instance_init - // is called from QOM core as the instance_init function - // for class T - unsafe { - ParentInit::with(state.as_mut(), |parent_init| { - T::INSTANCE_INIT.unwrap()(parent_init); - }); - } -} - -unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings::Object) { - let state = NonNull::new(obj).unwrap().cast::(); - // SAFETY: obj is an instance of T, since rust_instance_post_init - // is called from QOM core as the instance_post_init function - // for class T - T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); -} - -unsafe extern "C" fn rust_class_init( - klass: *mut ObjectClass, - _data: *const c_void, -) { - let mut klass = NonNull::new(klass) - .unwrap() - .cast::<::Class>(); - // SAFETY: klass is a T::Class, since rust_class_init - // is called from QOM core as the class_init function - // for class T - ::CLASS_INIT(unsafe { klass.as_mut() }) -} - -unsafe extern "C" fn drop_object(obj: *mut bindings::Object) { - // SAFETY: obj is an instance of T, since drop_object is called - // from the QOM core function object_deinit() as the instance_finalize - // function for class T. Note that while object_deinit() will drop the - // superclass field separately after this function returns, `T` must - // implement the unsafe trait ObjectType; the safety rules for the - // trait mandate that the parent field is manually dropped. - unsafe { std::ptr::drop_in_place(obj.cast::()) } -} - -/// Trait exposed by all structs corresponding to QOM objects. -/// -/// # Safety -/// -/// For classes declared in C: -/// -/// - `Class` and `TYPE` must match the data in the `TypeInfo`; -/// -/// - the first field of the struct must be of the instance type corresponding -/// to the superclass, as declared in the `TypeInfo` -/// -/// - likewise, the first field of the `Class` struct must be of the class type -/// corresponding to the superclass -/// -/// For classes declared in Rust and implementing [`ObjectImpl`]: -/// -/// - the struct must be `#[repr(C)]`; -/// -/// - the first field of the struct must be of type -/// [`ParentField`](ParentField), where `T` is the parent type -/// [`ObjectImpl::ParentType`] -/// -/// - the first field of the `Class` must be of the class struct corresponding -/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` -/// is not needed here. -/// -/// In both cases, having a separate class type is not necessary if the subclass -/// does not add any field. -pub unsafe trait ObjectType: Sized { - /// The QOM class object corresponding to this struct. This is used - /// to automatically generate a `class_init` method. - type Class; - - /// The name of the type, which can be passed to `object_new()` to - /// generate an instance of this type. - const TYPE_NAME: &'static CStr; - - /// Return the receiver as an Object. This is always safe, even - /// if this type represents an interface. - fn as_object(&self) -> &Object { - unsafe { &*self.as_ptr().cast() } - } - - /// Return the receiver as a const raw pointer to Object. - /// This is preferable to `as_object_mut_ptr()` if a C - /// function only needs a `const Object *`. - fn as_object_ptr(&self) -> *const bindings::Object { - self.as_object().as_ptr() - } - - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// This cast is always safe, but because the result is mutable - /// and the incoming reference is not, this should only be used - /// for calls to C functions, and only if needed. - unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object { - self.as_object().as_mut_ptr() - } -} - -/// Trait exposed by all structs corresponding to QOM interfaces. -/// Unlike `ObjectType`, it is implemented on the class type (which provides -/// the vtable for the interfaces). -/// -/// # Safety -/// -/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; -/// right now, interfaces can only be declared in C. -pub unsafe trait InterfaceType: Sized { - /// The name of the type, which can be passed to - /// `object_class_dynamic_cast()` to obtain the pointer to the vtable - /// for this interface. - const TYPE_NAME: &'static CStr; - - /// Return the vtable for the interface; `U` is the type that - /// lists the interface in its `TypeInfo`. - /// - /// # Examples - /// - /// This function is usually called by a `class_init` method in `U::Class`. - /// For example, `DeviceClass::class_init` initializes its `Resettable` - /// interface as follows: - /// - /// ```ignore - /// ResettableClass::cast::(self).class_init::(); - /// ``` - /// - /// where `T` is the concrete subclass that is being initialized. - /// - /// # Panics - /// - /// Panic if the incoming argument if `T` does not implement the interface. - fn cast(klass: &mut U::Class) -> &mut Self { - unsafe { - // SAFETY: upcasting to ObjectClass is always valid, and the - // return type is either NULL or the argument itself - let result: *mut Self = object_class_dynamic_cast( - (klass as *mut U::Class).cast(), - Self::TYPE_NAME.as_ptr(), - ) - .cast(); - result.as_mut().unwrap() - } - } -} - -/// This trait provides safe casting operations for QOM objects to raw pointers, -/// to be used for example for FFI. The trait can be applied to any kind of -/// reference or smart pointers, and enforces correctness through the [`IsA`] -/// trait. -pub trait ObjectDeref: Deref -where - Self::Target: ObjectType, -{ - /// Convert to a const Rust pointer, to be used for example for FFI. - /// The target pointer type must be the type of `self` or a superclass - fn as_ptr(&self) -> *const U - where - Self::Target: IsA, - { - let ptr: *const Self::Target = self.deref(); - ptr.cast::() - } - - /// Convert to a mutable Rust pointer, to be used for example for FFI. - /// The target pointer type must be the type of `self` or a superclass. - /// Used to implement interior mutability for objects. - /// - /// # Safety - /// - /// This method is safe because only the actual dereference of the pointer - /// has to be unsafe. Bindings to C APIs will use it a lot, but care has - /// to be taken because it overrides the const-ness of `&self`. - fn as_mut_ptr(&self) -> *mut U - where - Self::Target: IsA, - { - #[allow(clippy::as_ptr_cast_mut)] - { - self.as_ptr::().cast_mut() - } - } -} - -/// Trait that adds extra functionality for `&T` where `T` is a QOM -/// object type. Allows conversion to/from C objects in generic code. -pub trait ObjectCast: ObjectDeref + Copy -where - Self::Target: ObjectType, -{ - /// Safely convert from a derived type to one of its parent types. - /// - /// This is always safe; the [`IsA`] trait provides static verification - /// trait that `Self` dereferences to `U` or a child of `U`. - fn upcast<'a, U: ObjectType>(self) -> &'a U - where - Self::Target: IsA, - Self: 'a, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait - unsafe { self.unsafe_cast::() } - } - - /// Attempt to convert to a derived type. - /// - /// Returns `None` if the object is not actually of type `U`. This is - /// verified at runtime by checking the object's type information. - fn downcast<'a, U: IsA>(self) -> Option<&'a U> - where - Self: 'a, - { - self.dynamic_cast::() - } - - /// Attempt to convert between any two types in the QOM hierarchy. - /// - /// Returns `None` if the object is not actually of type `U`. This is - /// verified at runtime by checking the object's type information. - fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> - where - Self: 'a, - { - unsafe { - // SAFETY: upcasting to Object is always valid, and the - // return type is either NULL or the argument itself - let result: *const U = - object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); - - result.as_ref() - } - } - - /// Convert to any QOM type without verification. - /// - /// # Safety - /// - /// What safety? You need to know yourself that the cast is correct; only - /// use when performance is paramount. It is still better than a raw - /// pointer `cast()`, which does not even check that you remain in the - /// realm of QOM `ObjectType`s. - /// - /// `unsafe_cast::()` is always safe. - unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U - where - Self: 'a, - { - unsafe { &*(self.as_ptr::().cast::()) } - } -} - -impl ObjectDeref for &T {} -impl ObjectCast for &T {} - -impl ObjectDeref for &mut T {} - -/// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + IsA { - /// The parent of the type. This should match the first field of the - /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. - type ParentType: ObjectType; - - /// Whether the object can be instantiated - const ABSTRACT: bool = false; - - /// Function that is called to initialize an object. The parent class will - /// have already been initialized so the type is only responsible for - /// initializing its own members. - /// - /// FIXME: The argument is not really a valid reference. `&mut - /// MaybeUninit` would be a better description. - const INSTANCE_INIT: Option)> = None; - - /// Function that is called to finish initialization of an object, once - /// `INSTANCE_INIT` functions have been called. - const INSTANCE_POST_INIT: Option = None; - - /// Called on descendant classes after all parent class initialization - /// has occurred, but before the class itself is initialized. This - /// is only useful if a class is not a leaf, and can be used to undo - /// the effects of copying the contents of the parent's class struct - /// to the descendants. - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *const c_void), - > = None; - - const TYPE_INFO: TypeInfo = TypeInfo { - name: Self::TYPE_NAME.as_ptr(), - parent: Self::ParentType::TYPE_NAME.as_ptr(), - instance_size: core::mem::size_of::(), - instance_align: core::mem::align_of::(), - instance_init: match Self::INSTANCE_INIT { - None => None, - Some(_) => Some(rust_instance_init::), - }, - instance_post_init: match Self::INSTANCE_POST_INIT { - None => None, - Some(_) => Some(rust_instance_post_init::), - }, - instance_finalize: Some(drop_object::), - abstract_: Self::ABSTRACT, - class_size: core::mem::size_of::(), - class_init: Some(rust_class_init::), - class_base_init: Self::CLASS_BASE_INIT, - class_data: core::ptr::null(), - interfaces: core::ptr::null(), - }; - - // methods on ObjectClass - const UNPARENT: Option = None; - - /// Store into the argument the virtual method implementations - /// for `Self`. On entry, the virtual method pointers are set to - /// the default values coming from the parent classes; the function - /// can change them to override virtual methods of a parent class. - /// - /// Usually defined simply as `Self::Class::class_init::`; - /// however a default implementation cannot be included here, because the - /// bounds that the `Self::Class::class_init` method places on `Self` are - /// not known in advance. - /// - /// # Safety - /// - /// While `klass`'s parent class is initialized on entry, the other fields - /// are all zero; it is therefore assumed that all fields in `T` can be - /// zeroed, otherwise it would not be possible to provide the class as a - /// `&mut T`. TODO: it may be possible to add an unsafe trait that checks - /// that all fields *after the parent class* (but not the parent class - /// itself) are Zeroable. This unsafe trait can be added via a derive - /// macro. - const CLASS_INIT: fn(&mut Self::Class); -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_unparent_fn(dev: *mut bindings::Object) { - let state = NonNull::new(dev).unwrap().cast::(); - T::UNPARENT.unwrap()(unsafe { state.as_ref() }); -} - -impl ObjectClass { - /// Fill in the virtual methods of `ObjectClass` based on the definitions in - /// the `ObjectImpl` trait. - pub fn class_init(&mut self) { - if ::UNPARENT.is_some() { - self.unparent = Some(rust_unparent_fn::); - } - } -} - -unsafe impl ObjectType for Object { - type Class = ObjectClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; -} - -/// A reference-counted pointer to a QOM object. -/// -/// `Owned` wraps `T` with automatic reference counting. It increases the -/// reference count when created via [`Owned::from`] or cloned, and decreases -/// it when dropped. This ensures that the reference count remains elevated -/// as long as any `Owned` references to it exist. -/// -/// `Owned` can be used for two reasons: -/// * because the lifetime of the QOM object is unknown and someone else could -/// take a reference (similar to `Arc`, for example): in this case, the -/// object can escape and outlive the Rust struct that contains the `Owned` -/// field; -/// -/// * to ensure that the object stays alive until after `Drop::drop` is called -/// on the Rust struct: in this case, the object will always die together with -/// the Rust struct that contains the `Owned` field. -/// -/// Child properties are an example of the second case: in C, an object that -/// is created with `object_initialize_child` will die *before* -/// `instance_finalize` is called, whereas Rust expects the struct to have valid -/// contents when `Drop::drop` is called. Therefore Rust structs that have -/// child properties need to keep a reference to the child object. Right now -/// this can be done with `Owned`; in the future one might have a separate -/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like -/// `Owned`, but does not allow cloning. -/// -/// Note that dropping an `Owned` requires the big QEMU lock to be taken. -#[repr(transparent)] -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Owned(NonNull); - -// The following rationale for safety is taken from Linux's kernel::sync::Arc. - -// SAFETY: It is safe to send `Owned` to another thread when the underlying -// `T` is `Sync` because it effectively means sharing `&T` (which is safe -// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any -// thread that has an `Owned` may ultimately access `T` using a -// mutable reference when the reference count reaches zero and `T` is dropped. -unsafe impl Send for Owned {} - -// SAFETY: It is safe to send `&Owned` to another thread when the underlying -// `T` is `Sync` because it effectively means sharing `&T` (which is safe -// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any -// thread that has a `&Owned` may clone it and get an `Owned` on that -// thread, so the thread may ultimately access `T` using a mutable reference -// when the reference count reaches zero and `T` is dropped. -unsafe impl Sync for Owned {} - -impl Owned { - /// Convert a raw C pointer into an owned reference to the QOM - /// object it points to. The object's reference count will be - /// decreased when the `Owned` is dropped. - /// - /// # Panics - /// - /// Panics if `ptr` is NULL. - /// - /// # Safety - /// - /// The caller must indeed own a reference to the QOM object. - /// The object must not be embedded in another unless the outer - /// object is guaranteed to have a longer lifetime. - /// - /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed - /// back to `from_raw()` (assuming the original `Owned` was valid!), - /// since the owned reference remains there between the calls to - /// `into_raw()` and `from_raw()`. - pub unsafe fn from_raw(ptr: *const T) -> Self { - // SAFETY NOTE: while NonNull requires a mutable pointer, only - // Deref is implemented so the pointer passed to from_raw - // remains const - Owned(NonNull::new(ptr.cast_mut()).unwrap()) - } - - /// Obtain a raw C pointer from a reference. `src` is consumed - /// and the reference is leaked. - #[allow(clippy::missing_const_for_fn)] - pub fn into_raw(src: Owned) -> *mut T { - let src = ManuallyDrop::new(src); - src.0.as_ptr() - } - - /// Increase the reference count of a QOM object and return - /// a new owned reference to it. - /// - /// # Safety - /// - /// The object must not be embedded in another, unless the outer - /// object is guaranteed to have a longer lifetime. - pub unsafe fn from(obj: &T) -> Self { - unsafe { - object_ref(obj.as_object_mut_ptr().cast::()); - - // SAFETY NOTE: while NonNull requires a mutable pointer, only - // Deref is implemented so the reference passed to from_raw - // remains shared - Owned(NonNull::new_unchecked(obj.as_mut_ptr())) - } - } -} - -impl Clone for Owned { - fn clone(&self) -> Self { - // SAFETY: creation method is unsafe; whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - unsafe { Owned::from(self.deref()) } - } -} - -impl Deref for Owned { - type Target = T; - - fn deref(&self) -> &Self::Target { - // SAFETY: creation method is unsafe; whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - // With that guarantee, reference counting ensures that - // the object remains alive. - unsafe { &*self.0.as_ptr() } - } -} -impl ObjectDeref for Owned {} - -impl Drop for Owned { - fn drop(&mut self) { - 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` and its clones. - unsafe { - object_unref(self.as_object_mut_ptr().cast::()); - } - } -} - -impl> fmt::Debug for Owned { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.deref().debug_fmt(f) - } -} - -/// Trait for class methods exposed by the Object class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA` -pub trait ObjectClassMethods: IsA { - /// Return a new reference counted instance of this class - fn new() -> Owned { - assert!(bql::is_locked()); - // SAFETY: the object created by object_new is allocated on - // the heap and has a reference count of 1 - unsafe { - let raw_obj = object_new(Self::TYPE_NAME.as_ptr()); - let obj = Object::from_raw(raw_obj).unsafe_cast::(); - Owned::from_raw(obj) - } - } -} - -/// Trait for methods exposed by the Object class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA` -pub trait ObjectMethods: ObjectDeref -where - Self::Target: IsA, -{ - /// Return the name of the type of `self` - fn typename(&self) -> std::borrow::Cow<'_, str> { - let obj = self.upcast::(); - // SAFETY: safety of this is the requirement for implementing IsA - // The result of the C API has static lifetime - unsafe { - let p = object_get_typename(obj.as_mut_ptr()); - CStr::from_ptr(p).to_string_lossy() - } - } - - fn get_class(&self) -> &'static ::Class { - let obj = self.upcast::(); - - // SAFETY: all objects can call object_get_class; the actual class - // type is guaranteed by the implementation of `ObjectType` and - // `ObjectImpl`. - let klass: &'static ::Class = - unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; - - klass - } - - /// Convenience function for implementing the Debug trait - fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple(&self.typename()) - .field(&(self as *const Self)) - .finish() - } -} - -impl ObjectClassMethods for T where T: IsA {} -impl ObjectMethods for R where R::Target: IsA {} - -impl_vmstate_pointer!(Owned where T: VMState + ObjectType); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index b21883246e..b883d7eaf1 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -8,14 +8,13 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; use common::Opaque; +use qom::{prelude::*, Owned}; use crate::{ bindings, irq::{IRQState, InterruptSource}, memory::MemoryRegion, - prelude::*, qdev::{DeviceImpl, DeviceState}, - qom::Owned, }; /// A safe wrapper around [`bindings::SysBusDevice`]. @@ -31,6 +30,7 @@ unsafe impl ObjectType for SysBusDevice { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; } + qom_isa!(SysBusDevice: DeviceState, Object); // TODO: add virtual methods diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index e72ba08aef..f2e5eb9f4f 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -7,11 +7,10 @@ use std::{ffi::CStr, ptr::addr_of}; use bql::BqlCell; use migration::{VMStateDescription, VMStateDescriptionBuilder}; use qemu_api::{ - prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, - qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, }; +use qom::{prelude::*, ObjectImpl, ParentField}; use util::bindings::{module_call_init, module_init_type}; mod vmstate_tests; diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml new file mode 100644 index 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..84a65cb737 --- /dev/null +++ b/rust/qom/meson.build @@ -0,0 +1,43 @@ +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_qom_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common, +) + +_qom_rs = static_library( + 'qom', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/prelude.rs', + 'src/qom.rs', + ], + {'.': _qom_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs], + dependencies: [common_rs, qemu_api_macros], +) + +qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_api_macros, qom]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-qom-rs-doctests', + _qom_rs, + protocol: 'rust', + dependencies: qom_rs, + suite: ['doc', 'rust']) diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs new file mode 100644 index 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..204c6fea2f --- /dev/null +++ b/rust/qom/src/lib.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; + +// preserve one-item-per-"use" syntax, it is clearer +// for prelude-like modules +#[rustfmt::skip] +pub mod prelude; + +mod qom; +pub use qom::*; diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs new file mode 100644 index 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/qom/src/qom.rs b/rust/qom/src/qom.rs new file mode 100644 index 0000000000..3ea1ad9c5b --- /dev/null +++ b/rust/qom/src/qom.rs @@ -0,0 +1,951 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings to access QOM functionality from Rust. +//! +//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU +//! devices. This module makes QOM's features available in Rust through three +//! main mechanisms: +//! +//! * Automatic creation and registration of `TypeInfo` for classes that are +//! written in Rust, as well as mapping between Rust traits and QOM vtables. +//! +//! * Type-safe casting between parent and child classes, through the [`IsA`] +//! trait and methods such as [`upcast`](ObjectCast::upcast) and +//! [`downcast`](ObjectCast::downcast). +//! +//! * Automatic delegation of parent class methods to child classes. When a +//! trait uses [`IsA`] as a bound, its contents become available to all child +//! classes through blanket implementations. This works both for class methods +//! and for instance methods accessed through references or smart pointers. +//! +//! # Structure of a class +//! +//! A leaf class only needs a struct holding instance state. The struct must +//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` +//! traits that exist for its superclasses. +//! +//! If a class has subclasses, it will also provide a struct for instance data, +//! with the same characteristics as for concrete classes, but it also needs +//! additional components to support virtual methods: +//! +//! * a struct for class data, for example `DeviceClass`. This corresponds to +//! the C "class struct" and holds the vtable that is used by instances of the +//! class and its subclasses. It must start with its parent's class struct. +//! +//! * a trait for virtual method implementations, for example `DeviceImpl`. +//! Child classes implement this trait to provide their own behavior for +//! virtual methods. The trait's methods take `&self` to access instance data. +//! The traits have the appropriate specialization of `IsA<>` as a supertrait, +//! for example `IsA` for `DeviceImpl`. +//! +//! * a trait for instance methods, for example `DeviceMethods`. This trait is +//! automatically implemented for any reference or smart pointer to a device +//! instance. It calls into the vtable provides access across all subclasses +//! to methods defined for the class. +//! +//! * optionally, a trait for class methods, for example `DeviceClassMethods`. +//! This provides access to class-wide functionality that doesn't depend on +//! instance data. Like instance methods, these are automatically inherited by +//! child classes. +//! +//! # Class structures +//! +//! Each QOM class that has virtual methods describes them in a +//! _class struct_. Class structs include a parent field corresponding +//! to the vtable of the parent class, all the way up to [`ObjectClass`]. +//! +//! As mentioned above, virtual methods are defined via traits such as +//! `DeviceImpl`. Class structs do not define any trait but, conventionally, +//! all of them have a `class_init` method to initialize the virtual methods +//! based on the trait and then call the same method on the superclass. +//! +//! ```ignore +//! impl YourSubclassClass +//! { +//! pub fn class_init(&mut self) { +//! ... +//! klass.parent_class::class_init(); +//! } +//! } +//! ``` +//! +//! If a class implements a QOM interface. In that case, the function must +//! contain, for each interface, an extra forwarding call as follows: +//! +//! ```ignore +//! ResettableClass::cast::(self).class_init::(); +//! ``` +//! +//! These `class_init` functions are methods on the class rather than a trait, +//! because the bound on `T` (`DeviceImpl` in this case), will change for every +//! class struct. The functions are pointed to by the +//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default +//! implementation, in most cases it will be enough to write it as follows: +//! +//! ```ignore +//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::; +//! ``` +//! +//! This design incurs a small amount of code duplication but, by not using +//! traits, it allows the flexibility of implementing bindings in any crate, +//! without incurring into violations of orphan rules for traits. + +use std::{ + ffi::{c_void, CStr}, + fmt, + marker::PhantomData, + mem::{ManuallyDrop, MaybeUninit}, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +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, +}; +pub use crate::bindings::{type_register_static, ObjectClass}; + +/// A safe wrapper around [`bindings::Object`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Object(Opaque); + +unsafe impl Send for Object {} +unsafe impl Sync for Object {} + +/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct +/// or indirect parent of `Self`). +/// +/// # Safety +/// +/// The struct `Self` must be `#[repr(C)]` and must begin, directly or +/// indirectly, with a field of type `P`. This ensures that invalid casts, +/// which rely on `IsA<>` for static checking, are rejected at compile time. +pub unsafe trait IsA: ObjectType {} + +// SAFETY: it is always safe to cast to your own type +unsafe impl IsA for T {} + +/// Macro to mark superclasses of QOM classes. This enables type-safe +/// up- and downcasting. +/// +/// # Safety +/// +/// This macro is a thin wrapper around the [`IsA`] trait and performs +/// no checking whatsoever of what is declared. It is the caller's +/// responsibility to have $struct begin, directly or indirectly, with +/// a field of type `$parent`. +#[macro_export] +macro_rules! qom_isa { + ($struct:ty : $($parent:ty),* ) => { + $( + // SAFETY: it is the caller responsibility to have $parent as the + // first field + unsafe impl $crate::IsA<$parent> for $struct {} + + impl AsRef<$parent> for $struct { + fn as_ref(&self) -> &$parent { + // SAFETY: follows the same rules as for IsA, which is + // declared above. + let ptr: *const Self = self; + unsafe { &*ptr.cast::<$parent>() } + } + } + )* + }; +} + +/// This is the same as [`ManuallyDrop`](std::mem::ManuallyDrop), though +/// it hides the standard methods of `ManuallyDrop`. +/// +/// The first field of an `ObjectType` must be of type `ParentField`. +/// (Technically, this is only necessary if there is at least one Rust +/// superclass in the hierarchy). This is to ensure that the parent field is +/// dropped after the subclass; this drop order is enforced by the C +/// `object_deinit` function. +/// +/// # Examples +/// +/// ```ignore +/// #[repr(C)] +/// #[derive(qemu_api_macros::Object)] +/// pub struct MyDevice { +/// parent: ParentField, +/// ... +/// } +/// ``` +#[derive(Debug)] +#[repr(transparent)] +pub struct ParentField(std::mem::ManuallyDrop); + +impl Deref for ParentField { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ParentField { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Display for ParentField { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// This struct knows that the superclasses of the object have already been +/// initialized. +/// +/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a +/// technique that is found in several crates, the main ones probably being +/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) +/// and `generativity`. +/// +/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to +/// the lifetime argument `'init`. This, together with the `for<'...>` in +/// `[ParentInit::with]`, block any attempt of the compiler to be creative when +/// operating on types of type `ParentInit` and to extend their lifetimes. In +/// particular, it ensures that the `ParentInit` cannot be made to outlive the +/// `rust_instance_init()` function that creates it, and therefore that the +/// `&'init T` reference is valid. +/// +/// This implementation of the same concept, without the QOM baggage, can help +/// understanding the effect: +/// +/// ``` +/// use std::marker::PhantomData; +/// +/// #[derive(PartialEq, Eq)] +/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); +/// +/// impl<'closure, T: Copy> Jail<'closure, T> { +/// fn get(&self) -> T { +/// *self.0 +/// } +/// +/// #[inline] +/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { +/// let parent_init = Jail(&v, PhantomData); +/// f(parent_init) +/// } +/// } +/// ``` +/// +/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the +/// closure: +/// +/// ```ignore +/// let x = 42; +/// let escape = Jail::with(&x, |token1| { +/// println!("{}", token1.get()); +/// // fails to compile... +/// token1 +/// }); +/// // ... so you cannot do this: +/// println!("{}", escape.get()); +/// ``` +/// +/// Likewise, in the QOM case the `ParentInit` cannot be moved out of +/// `instance_init()`. Without this trick it would be possible to stash a +/// `ParentInit` and use it later to access uninitialized memory. +/// +/// Here is another example, showing how separately-created "identities" stay +/// isolated: +/// +/// ```ignore +/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { +/// fn clone(&self) -> Jail<'closure, T> { +/// Jail(self.0, PhantomData) +/// } +/// } +/// +/// fn main() { +/// Jail::with(42, |token1| { +/// // this works and returns true: the clone has the same "identity" +/// println!("{}", token1 == token1.clone()); +/// Jail::with(42, |token2| { +/// // here the outer token remains accessible... +/// println!("{}", token1.get()); +/// // ... but the two are separate: this fails to compile: +/// println!("{}", token1 == token2); +/// }); +/// }); +/// } +/// ``` +pub struct ParentInit<'init, T>( + &'init mut MaybeUninit, + PhantomData &'init ()>, +); + +impl<'init, T> ParentInit<'init, T> { + #[inline] + pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { + let parent_init = ParentInit(obj, PhantomData); + f(parent_init) + } +} + +impl ParentInit<'_, T> { + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub const fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object_ptr().cast_mut() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub const fn as_object_ptr(&self) -> *const bindings::Object { + self.0.as_ptr().cast() + } +} + +impl<'a, T: ObjectImpl> ParentInit<'a, T> { + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub const unsafe fn upcast(&self) -> &'a U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &*(self.0.as_ptr().cast::()) } + } + + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast_mut(&mut self) -> &'a mut U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &mut *(self.0.as_mut_ptr().cast::()) } + } +} + +impl Deref for ParentInit<'_, T> { + type Target = MaybeUninit; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl DerefMut for ParentInit<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { + let mut state = NonNull::new(obj).unwrap().cast::>(); + + // SAFETY: obj is an instance of T, since rust_instance_init + // is called from QOM core as the instance_init function + // for class T + unsafe { + ParentInit::with(state.as_mut(), |parent_init| { + T::INSTANCE_INIT.unwrap()(parent_init); + }); + } +} + +unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings::Object) { + let state = NonNull::new(obj).unwrap().cast::(); + // SAFETY: obj is an instance of T, since rust_instance_post_init + // is called from QOM core as the instance_post_init function + // for class T + T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); +} + +unsafe extern "C" fn rust_class_init( + klass: *mut ObjectClass, + _data: *const c_void, +) { + let mut klass = NonNull::new(klass) + .unwrap() + .cast::<::Class>(); + // SAFETY: klass is a T::Class, since rust_class_init + // is called from QOM core as the class_init function + // for class T + ::CLASS_INIT(unsafe { klass.as_mut() }) +} + +unsafe extern "C" fn drop_object(obj: *mut bindings::Object) { + // SAFETY: obj is an instance of T, since drop_object is called + // from the QOM core function object_deinit() as the instance_finalize + // function for class T. Note that while object_deinit() will drop the + // superclass field separately after this function returns, `T` must + // implement the unsafe trait ObjectType; the safety rules for the + // trait mandate that the parent field is manually dropped. + unsafe { std::ptr::drop_in_place(obj.cast::()) } +} + +/// Trait exposed by all structs corresponding to QOM objects. +/// +/// # Safety +/// +/// For classes declared in C: +/// +/// - `Class` and `TYPE` must match the data in the `TypeInfo`; +/// +/// - the first field of the struct must be of the instance type corresponding +/// to the superclass, as declared in the `TypeInfo` +/// +/// - likewise, the first field of the `Class` struct must be of the class type +/// corresponding to the superclass +/// +/// For classes declared in Rust and implementing [`ObjectImpl`]: +/// +/// - the struct must be `#[repr(C)]`; +/// +/// - the first field of the struct must be of type +/// [`ParentField`](ParentField), where `T` is the parent type +/// [`ObjectImpl::ParentType`] +/// +/// - the first field of the `Class` must be of the class struct corresponding +/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` +/// is not needed here. +/// +/// In both cases, having a separate class type is not necessary if the subclass +/// does not add any field. +pub unsafe trait ObjectType: Sized { + /// The QOM class object corresponding to this struct. This is used + /// to automatically generate a `class_init` method. + type Class; + + /// The name of the type, which can be passed to `object_new()` to + /// generate an instance of this type. + const TYPE_NAME: &'static CStr; + + /// Return the receiver as an Object. This is always safe, even + /// if this type represents an interface. + fn as_object(&self) -> &Object { + unsafe { &*self.as_ptr().cast() } + } + + /// Return the receiver as a const raw pointer to Object. + /// This is preferable to `as_object_mut_ptr()` if a C + /// function only needs a `const Object *`. + fn as_object_ptr(&self) -> *const bindings::Object { + self.as_object().as_ptr() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// This cast is always safe, but because the result is mutable + /// and the incoming reference is not, this should only be used + /// for calls to C functions, and only if needed. + unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object().as_mut_ptr() + } +} + +/// Trait exposed by all structs corresponding to QOM interfaces. +/// Unlike `ObjectType`, it is implemented on the class type (which provides +/// the vtable for the interfaces). +/// +/// # Safety +/// +/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; +/// right now, interfaces can only be declared in C. +pub unsafe trait InterfaceType: Sized { + /// The name of the type, which can be passed to + /// `object_class_dynamic_cast()` to obtain the pointer to the vtable + /// for this interface. + const TYPE_NAME: &'static CStr; + + /// Return the vtable for the interface; `U` is the type that + /// lists the interface in its `TypeInfo`. + /// + /// # Examples + /// + /// This function is usually called by a `class_init` method in `U::Class`. + /// For example, `DeviceClass::class_init` initializes its `Resettable` + /// interface as follows: + /// + /// ```ignore + /// ResettableClass::cast::(self).class_init::(); + /// ``` + /// + /// where `T` is the concrete subclass that is being initialized. + /// + /// # Panics + /// + /// Panic if the incoming argument if `T` does not implement the interface. + fn cast(klass: &mut U::Class) -> &mut Self { + unsafe { + // SAFETY: upcasting to ObjectClass is always valid, and the + // return type is either NULL or the argument itself + let result: *mut Self = object_class_dynamic_cast( + (klass as *mut U::Class).cast(), + Self::TYPE_NAME.as_ptr(), + ) + .cast(); + result.as_mut().unwrap() + } + } +} + +/// This trait provides safe casting operations for QOM objects to raw pointers, +/// to be used for example for FFI. The trait can be applied to any kind of +/// reference or smart pointers, and enforces correctness through the [`IsA`] +/// trait. +pub trait ObjectDeref: Deref +where + Self::Target: ObjectType, +{ + /// Convert to a const Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass + fn as_ptr(&self) -> *const U + where + Self::Target: IsA, + { + let ptr: *const Self::Target = self.deref(); + ptr.cast::() + } + + /// Convert to a mutable Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass. + /// Used to implement interior mutability for objects. + /// + /// # Safety + /// + /// This method is safe because only the actual dereference of the pointer + /// has to be unsafe. Bindings to C APIs will use it a lot, but care has + /// to be taken because it overrides the const-ness of `&self`. + fn as_mut_ptr(&self) -> *mut U + where + Self::Target: IsA, + { + #[allow(clippy::as_ptr_cast_mut)] + { + self.as_ptr::().cast_mut() + } + } +} + +/// Trait that adds extra functionality for `&T` where `T` is a QOM +/// object type. Allows conversion to/from C objects in generic code. +pub trait ObjectCast: ObjectDeref + Copy +where + Self::Target: ObjectType, +{ + /// Safely convert from a derived type to one of its parent types. + /// + /// This is always safe; the [`IsA`] trait provides static verification + /// trait that `Self` dereferences to `U` or a child of `U`. + fn upcast<'a, U: ObjectType>(self) -> &'a U + where + Self::Target: IsA, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast::() } + } + + /// Attempt to convert to a derived type. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn downcast<'a, U: IsA>(self) -> Option<&'a U> + where + Self: 'a, + { + self.dynamic_cast::() + } + + /// Attempt to convert between any two types in the QOM hierarchy. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> + where + Self: 'a, + { + unsafe { + // SAFETY: upcasting to Object is always valid, and the + // return type is either NULL or the argument itself + let result: *const U = + object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); + + result.as_ref() + } + } + + /// Convert to any QOM type without verification. + /// + /// # Safety + /// + /// What safety? You need to know yourself that the cast is correct; only + /// use when performance is paramount. It is still better than a raw + /// pointer `cast()`, which does not even check that you remain in the + /// realm of QOM `ObjectType`s. + /// + /// `unsafe_cast::()` is always safe. + unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U + where + Self: 'a, + { + unsafe { &*(self.as_ptr::().cast::()) } + } +} + +impl ObjectDeref for &T {} +impl ObjectCast for &T {} + +impl ObjectDeref for &mut T {} + +/// Trait a type must implement to be registered with QEMU. +pub trait ObjectImpl: ObjectType + IsA { + /// The parent of the type. This should match the first field of the + /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. + type ParentType: ObjectType; + + /// Whether the object can be instantiated + const ABSTRACT: bool = false; + + /// Function that is called to initialize an object. The parent class will + /// have already been initialized so the type is only responsible for + /// initializing its own members. + /// + /// FIXME: The argument is not really a valid reference. `&mut + /// MaybeUninit` would be a better description. + const INSTANCE_INIT: Option)> = None; + + /// Function that is called to finish initialization of an object, once + /// `INSTANCE_INIT` functions have been called. + const INSTANCE_POST_INIT: Option = None; + + /// Called on descendant classes after all parent class initialization + /// has occurred, but before the class itself is initialized. This + /// is only useful if a class is not a leaf, and can be used to undo + /// the effects of copying the contents of the parent's class struct + /// to the descendants. + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *const c_void), + > = None; + + const TYPE_INFO: TypeInfo = TypeInfo { + name: Self::TYPE_NAME.as_ptr(), + parent: Self::ParentType::TYPE_NAME.as_ptr(), + instance_size: core::mem::size_of::(), + instance_align: core::mem::align_of::(), + instance_init: match Self::INSTANCE_INIT { + None => None, + Some(_) => Some(rust_instance_init::), + }, + instance_post_init: match Self::INSTANCE_POST_INIT { + None => None, + Some(_) => Some(rust_instance_post_init::), + }, + instance_finalize: Some(drop_object::), + abstract_: Self::ABSTRACT, + class_size: core::mem::size_of::(), + class_init: Some(rust_class_init::), + class_base_init: Self::CLASS_BASE_INIT, + class_data: core::ptr::null(), + interfaces: core::ptr::null(), + }; + + // methods on ObjectClass + const UNPARENT: Option = None; + + /// Store into the argument the virtual method implementations + /// for `Self`. On entry, the virtual method pointers are set to + /// the default values coming from the parent classes; the function + /// can change them to override virtual methods of a parent class. + /// + /// Usually defined simply as `Self::Class::class_init::`; + /// however a default implementation cannot be included here, because the + /// bounds that the `Self::Class::class_init` method places on `Self` are + /// not known in advance. + /// + /// # Safety + /// + /// While `klass`'s parent class is initialized on entry, the other fields + /// are all zero; it is therefore assumed that all fields in `T` can be + /// zeroed, otherwise it would not be possible to provide the class as a + /// `&mut T`. TODO: it may be possible to add an unsafe trait that checks + /// that all fields *after the parent class* (but not the parent class + /// itself) are Zeroable. This unsafe trait can be added via a derive + /// macro. + const CLASS_INIT: fn(&mut Self::Class); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_unparent_fn(dev: *mut bindings::Object) { + let state = NonNull::new(dev).unwrap().cast::(); + T::UNPARENT.unwrap()(unsafe { state.as_ref() }); +} + +impl ObjectClass { + /// Fill in the virtual methods of `ObjectClass` based on the definitions in + /// the `ObjectImpl` trait. + pub fn class_init(&mut self) { + if ::UNPARENT.is_some() { + self.unparent = Some(rust_unparent_fn::); + } + } +} + +unsafe impl ObjectType for Object { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; +} + +/// A reference-counted pointer to a QOM object. +/// +/// `Owned` wraps `T` with automatic reference counting. It increases the +/// reference count when created via [`Owned::from`] or cloned, and decreases +/// it when dropped. This ensures that the reference count remains elevated +/// as long as any `Owned` references to it exist. +/// +/// `Owned` can be used for two reasons: +/// * because the lifetime of the QOM object is unknown and someone else could +/// take a reference (similar to `Arc`, for example): in this case, the +/// object can escape and outlive the Rust struct that contains the `Owned` +/// field; +/// +/// * to ensure that the object stays alive until after `Drop::drop` is called +/// on the Rust struct: in this case, the object will always die together with +/// the Rust struct that contains the `Owned` field. +/// +/// Child properties are an example of the second case: in C, an object that +/// is created with `object_initialize_child` will die *before* +/// `instance_finalize` is called, whereas Rust expects the struct to have valid +/// contents when `Drop::drop` is called. Therefore Rust structs that have +/// child properties need to keep a reference to the child object. Right now +/// this can be done with `Owned`; in the future one might have a separate +/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like +/// `Owned`, but does not allow cloning. +/// +/// Note that dropping an `Owned` requires the big QEMU lock to be taken. +#[repr(transparent)] +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Owned(NonNull); + +// The following rationale for safety is taken from Linux's kernel::sync::Arc. + +// SAFETY: It is safe to send `Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has an `Owned` may ultimately access `T` using a +// mutable reference when the reference count reaches zero and `T` is dropped. +unsafe impl Send for Owned {} + +// SAFETY: It is safe to send `&Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has a `&Owned` may clone it and get an `Owned` on that +// thread, so the thread may ultimately access `T` using a mutable reference +// when the reference count reaches zero and `T` is dropped. +unsafe impl Sync for Owned {} + +impl Owned { + /// Convert a raw C pointer into an owned reference to the QOM + /// object it points to. The object's reference count will be + /// decreased when the `Owned` is dropped. + /// + /// # Panics + /// + /// Panics if `ptr` is NULL. + /// + /// # Safety + /// + /// The caller must indeed own a reference to the QOM object. + /// The object must not be embedded in another unless the outer + /// object is guaranteed to have a longer lifetime. + /// + /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed + /// back to `from_raw()` (assuming the original `Owned` was valid!), + /// since the owned reference remains there between the calls to + /// `into_raw()` and `from_raw()`. + pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the pointer passed to from_raw + // remains const + Owned(NonNull::new(ptr.cast_mut()).unwrap()) + } + + /// Obtain a raw C pointer from a reference. `src` is consumed + /// and the reference is leaked. + #[allow(clippy::missing_const_for_fn)] + pub fn into_raw(src: Owned) -> *mut T { + let src = ManuallyDrop::new(src); + src.0.as_ptr() + } + + /// Increase the reference count of a QOM object and return + /// a new owned reference to it. + /// + /// # Safety + /// + /// The object must not be embedded in another, unless the outer + /// object is guaranteed to have a longer lifetime. + pub unsafe fn from(obj: &T) -> Self { + unsafe { + object_ref(obj.as_object_mut_ptr().cast::()); + + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the reference passed to from_raw + // remains shared + Owned(NonNull::new_unchecked(obj.as_mut_ptr())) + } + } +} + +impl Clone for Owned { + fn clone(&self) -> Self { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + unsafe { Owned::from(self.deref()) } + } +} + +impl Deref for Owned { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + // With that guarantee, reference counting ensures that + // the object remains alive. + unsafe { &*self.0.as_ptr() } + } +} +impl ObjectDeref for Owned {} + +impl Drop for Owned { + fn drop(&mut self) { + 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` and its clones. + unsafe { + object_unref(self.as_object_mut_ptr().cast::()); + } + } +} + +impl> fmt::Debug for Owned { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.deref().debug_fmt(f) + } +} + +/// Trait for class methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectClassMethods: IsA { + /// Return a new reference counted instance of this class + fn new() -> Owned { + assert!(bql::is_locked()); + // SAFETY: the object created by object_new is allocated on + // the heap and has a reference count of 1 + unsafe { + let raw_obj = object_new(Self::TYPE_NAME.as_ptr()); + let obj = Object::from_raw(raw_obj).unsafe_cast::(); + Owned::from_raw(obj) + } + } +} + +/// Trait for methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Return the name of the type of `self` + fn typename(&self) -> std::borrow::Cow<'_, str> { + let obj = self.upcast::(); + // SAFETY: safety of this is the requirement for implementing IsA + // The result of the C API has static lifetime + unsafe { + let p = object_get_typename(obj.as_mut_ptr()); + CStr::from_ptr(p).to_string_lossy() + } + } + + fn get_class(&self) -> &'static ::Class { + let obj = self.upcast::(); + + // SAFETY: all objects can call object_get_class; the actual class + // type is guaranteed by the implementation of `ObjectType` and + // `ObjectImpl`. + let klass: &'static ::Class = + unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; + + klass + } + + /// Convenience function for implementing the Debug trait + fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple(&self.typename()) + .field(&(self as *const Self)) + .finish() + } +} + +impl ObjectClassMethods for T where T: IsA {} +impl ObjectMethods for R where R::Target: IsA {} + +impl_vmstate_pointer!(Owned where T: VMState + ObjectType); 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" -- cgit 1.4.1 From fef932ef09c82c3831ff3336d1b2d566cd6ccae4 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:54 +0200 Subject: rust: split "chardev" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-14-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 14 +++ rust/chardev/Cargo.toml | 24 ++++ rust/chardev/build.rs | 1 + rust/chardev/meson.build | 41 ++++++ rust/chardev/src/bindings.rs | 36 ++++++ rust/chardev/src/chardev.rs | 261 +++++++++++++++++++++++++++++++++++++++ rust/chardev/src/lib.rs | 6 + rust/chardev/wrapper.h | 28 +++++ rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 2 +- rust/meson.build | 1 + rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 9 +- rust/qemu-api/src/bindings.rs | 9 +- rust/qemu-api/src/chardev.rs | 261 --------------------------------------- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/qdev.rs | 6 +- rust/qemu-api/wrapper.h | 2 - 20 files changed, 425 insertions(+), 281 deletions(-) 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 create mode 100644 rust/chardev/src/chardev.rs create mode 100644 rust/chardev/src/lib.rs create mode 100644 rust/chardev/wrapper.h delete mode 100644 rust/qemu-api/src/chardev.rs (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index c7bd02aef1..cac6dcdc65 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3516,6 +3516,7 @@ Rust M: Manos Pitsidianakis S: Maintained F: rust/bql/ +F: rust/chardev/ F: rust/common/ F: rust/migration/ F: rust/qemu-api diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 442eadf08f..ae852c5550 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -51,6 +51,18 @@ dependencies = [ "migration", ] +[[package]] +name = "chardev" +version = "0.1.0" +dependencies = [ + "bql", + "common", + "migration", + "qemu_api_macros", + "qom", + "util", +] + [[package]] name = "common" version = "0.1.0" @@ -118,6 +130,7 @@ dependencies = [ "bilge-impl", "bits", "bql", + "chardev", "common", "migration", "qemu_api", @@ -163,6 +176,7 @@ name = "qemu_api" version = "0.1.0" dependencies = [ "bql", + "chardev", "common", "migration", "qemu_api_macros", diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml new file mode 100644 index 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..5d333e232b --- /dev/null +++ b/rust/chardev/meson.build @@ -0,0 +1,41 @@ +c_enums = [ + 'QEMUChrEvent', +] +_chardev_bindgen_args = [] +foreach enum : c_enums + _chardev_bindgen_args += ['--rustified-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_chardev_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _chardev_bindgen_args, +) + +_chardev_rs = static_library( + 'chardev', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/chardev.rs', + ], + {'.': _chardev_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], + dependencies: [common_rs, qemu_api_macros], +) + +chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev, qemuutil]) diff --git a/rust/chardev/src/bindings.rs b/rust/chardev/src/bindings.rs new file mode 100644 index 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/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs new file mode 100644 index 0000000000..072d806e4a --- /dev/null +++ b/rust/chardev/src/chardev.rs @@ -0,0 +1,261 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for character devices +//! +//! Character devices in QEMU can run under the big QEMU lock or in a separate +//! `GMainContext`. Here we only support the former, because the bindings +//! enforce that the BQL is taken whenever the functions in [`CharBackend`] are +//! called. + +use std::{ + ffi::{c_int, c_void, CStr}, + fmt::{self, Debug}, + io::{self, ErrorKind, Write}, + marker::PhantomPinned, + ptr::addr_of_mut, + slice, +}; + +use bql::{BqlRefCell, BqlRefMut}; +use common::{callbacks::FnCall, errno, Opaque}; +use qom::prelude::*; + +use crate::bindings; + +/// A safe wrapper around [`bindings::Chardev`]. +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct Chardev(Opaque); + +pub type ChardevClass = bindings::ChardevClass; +pub type Event = bindings::QEMUChrEvent; + +/// A safe wrapper around [`bindings::CharBackend`], denoting the character +/// back-end that is used for example by a device. Compared to the +/// underlying C struct it adds BQL protection, and is marked as pinned +/// because the QOM object ([`bindings::Chardev`]) contains a pointer to +/// the `CharBackend`. +pub struct CharBackend { + inner: BqlRefCell, + _pin: PhantomPinned, +} + +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 { + 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) }; + errno::into_io_result(r).map(|cnt| cnt as usize) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + 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) }; + errno::into_io_result(r).and_then(|cnt| { + if cnt as usize == buf.len() { + Ok(()) + } else { + Err(ErrorKind::WriteZero.into()) + } + }) + } +} + +impl Debug for CharBackend { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: accessed just to print the values + let chr = self.inner.as_ptr(); + Debug::fmt(unsafe { &*chr }, f) + } +} + +// FIXME: use something like PinnedDrop from the pinned_init crate +impl Drop for CharBackend { + fn drop(&mut self) { + self.disable_handlers(); + } +} + +impl CharBackend { + /// Enable the front-end's character device handlers, if there is an + /// associated `Chardev`. + pub fn enable_handlers< + 'chardev, + 'owner: 'chardev, + T, + CanReceiveFn: for<'a> FnCall<(&'a T,), u32>, + ReceiveFn: for<'a, 'b> FnCall<(&'a T, &'b [u8])>, + EventFn: for<'a> FnCall<(&'a T, Event)>, + >( + // When "self" is dropped, the handlers are automatically disabled. + // However, this is not necessarily true if the owner is dropped. + // So require the owner to outlive the character device. + &'chardev self, + owner: &'owner T, + _can_receive: CanReceiveFn, + _receive: ReceiveFn, + _event: EventFn, + ) { + unsafe extern "C" fn rust_can_receive_cb FnCall<(&'a T,), u32>>( + opaque: *mut c_void, + ) -> c_int { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + let r = F::call((owner,)); + r.try_into().unwrap() + } + + unsafe extern "C" fn rust_receive_cb FnCall<(&'a T, &'b [u8])>>( + opaque: *mut c_void, + buf: *const u8, + size: c_int, + ) { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) }; + F::call((owner, buf)) + } + + unsafe extern "C" fn rust_event_cb FnCall<(&'a T, Event)>>( + opaque: *mut c_void, + event: Event, + ) { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + F::call((owner, event)) + } + + const { assert!(CanReceiveFn::IS_SOME) }; + let receive_cb: Option = + if ReceiveFn::is_some() { + Some(rust_receive_cb::) + } else { + None + }; + let event_cb: Option = if EventFn::is_some() { + Some(rust_event_cb::) + } else { + None + }; + + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { + bindings::qemu_chr_fe_set_handlers( + addr_of_mut!(*chr), + Some(rust_can_receive_cb::), + receive_cb, + event_cb, + None, + (owner as *const T).cast_mut().cast::(), + core::ptr::null_mut(), + true, + ); + } + } + + /// Disable the front-end's character device handlers. + pub fn disable_handlers(&self) { + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { + bindings::qemu_chr_fe_set_handlers( + addr_of_mut!(*chr), + None, + None, + None, + None, + core::ptr::null_mut(), + core::ptr::null_mut(), + true, + ); + } + } + + /// Notify that the frontend is ready to receive data. + pub fn accept_input(&self) { + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { bindings::qemu_chr_fe_accept_input(addr_of_mut!(*chr)) } + } + + /// Temporarily borrow the character device, allowing it to be used + /// as an implementor of `Write`. Note that it is not valid to drop + /// 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 + '_ { + CharBackendMut(self.inner.borrow_mut()) + } + + /// Send a continuous stream of zero bits on the line if `enabled` is + /// true, or a short stream if `enabled` is false. + pub fn send_break(&self, long: bool) -> io::Result<()> { + let mut chr = self.inner.borrow_mut(); + let mut duration: c_int = long.into(); + // SAFETY: the borrow promises that the BQL is taken + let r = unsafe { + bindings::qemu_chr_fe_ioctl( + addr_of_mut!(*chr), + bindings::CHR_IOCTL_SERIAL_SET_BREAK as i32, + addr_of_mut!(duration).cast::(), + ) + }; + + errno::into_io_result(r).map(|_| ()) + } + + /// Write data to a character backend from the front end. This function + /// will send data from the front end to the back end. Unlike + /// `write`, this function will block if the back end cannot + /// consume all of the data attempted to be written. + /// + /// Returns the number of bytes consumed (0 if no associated Chardev) or an + /// error. + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = buf.len().try_into().unwrap(); + // SAFETY: qemu_chr_fe_write is thread-safe + let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) }; + errno::into_io_result(r).map(|cnt| cnt as usize) + } + + /// Write data to a character backend from the front end. This function + /// will send data from the front end to the back end. Unlike + /// `write`, this function will block if the back end cannot + /// consume all of the data attempted to be written. + /// + /// Returns the number of bytes consumed (0 if no associated Chardev) or an + /// error. + pub fn write_all(&self, buf: &[u8]) -> io::Result<()> { + let len = buf.len().try_into().unwrap(); + // SAFETY: qemu_chr_fe_write_all is thread-safe + let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) }; + errno::into_io_result(r).and_then(|cnt| { + if cnt as usize == buf.len() { + Ok(()) + } else { + Err(ErrorKind::WriteZero.into()) + } + }) + } +} + +unsafe impl ObjectType for Chardev { + type Class = ChardevClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CHARDEV) }; +} +qom_isa!(Chardev: Object); 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/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/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 af9393c9da..aaf911c5f4 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -14,6 +14,7 @@ _libpl011_rs = static_library( bql_rs, qemu_api_macros, qom_rs, + chardev_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 63651b9dcd..bc64061fb3 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,13 +5,13 @@ use std::{ffi::CStr, mem::size_of}; use bql::BqlRefCell; +use chardev::{CharBackend, Chardev, Event}; use common::{static_assert, uninit_field_mut}; use migration::{ self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder, }; use qemu_api::{ - chardev::{CharBackend, Chardev, Event}, irq::{IRQState, InterruptSource}, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, diff --git a/rust/meson.build b/rust/meson.build index 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 9d11becb28..3bf2dafa6d 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 11e43bb646..a47f178b69 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -7,7 +7,6 @@ c_enums = [ 'GpioPolarity', 'MachineInitPhase', 'MemoryDeviceInfoKind', - 'QEMUChrEvent', 'ResetType', 'device_endian', ] @@ -23,9 +22,10 @@ foreach enum : c_bitfields endforeach blocked_type = [ + 'Chardev', + 'Error', 'ObjectClass', 'VMStateDescription', - 'Error', ] foreach type: blocked_type _qemu_api_bindgen_args += ['--blocklist-type', type] @@ -53,7 +53,6 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', - 'src/chardev.rs', 'src/irq.rs', 'src/memory.rs', 'src/prelude.rs', @@ -65,8 +64,8 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - qom_rs, util_rs, hwcore, chardev], + dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + qom_rs, util_rs, hwcore], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 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 @@ include!("bindings.inc.rs"); #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); -// SAFETY: these are implemented in C; the bindings need to assert that the -// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. -// When bindings for character devices are introduced, this can be -// moved to the Opaque<> wrapper in src/chardev.rs. -unsafe impl Send for CharBackend {} -unsafe impl Sync for CharBackend {} - // SAFETY: this is a pure data struct unsafe impl Send for CoalescedMemoryRange {} unsafe impl Sync for CoalescedMemoryRange {} @@ -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/chardev.rs b/rust/qemu-api/src/chardev.rs deleted file mode 100644 index 072d806e4a..0000000000 --- a/rust/qemu-api/src/chardev.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for character devices -//! -//! Character devices in QEMU can run under the big QEMU lock or in a separate -//! `GMainContext`. Here we only support the former, because the bindings -//! enforce that the BQL is taken whenever the functions in [`CharBackend`] are -//! called. - -use std::{ - ffi::{c_int, c_void, CStr}, - fmt::{self, Debug}, - io::{self, ErrorKind, Write}, - marker::PhantomPinned, - ptr::addr_of_mut, - slice, -}; - -use bql::{BqlRefCell, BqlRefMut}; -use common::{callbacks::FnCall, errno, Opaque}; -use qom::prelude::*; - -use crate::bindings; - -/// A safe wrapper around [`bindings::Chardev`]. -#[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] -pub struct Chardev(Opaque); - -pub type ChardevClass = bindings::ChardevClass; -pub type Event = bindings::QEMUChrEvent; - -/// A safe wrapper around [`bindings::CharBackend`], denoting the character -/// back-end that is used for example by a device. Compared to the -/// underlying C struct it adds BQL protection, and is marked as pinned -/// because the QOM object ([`bindings::Chardev`]) contains a pointer to -/// the `CharBackend`. -pub struct CharBackend { - inner: BqlRefCell, - _pin: PhantomPinned, -} - -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 { - 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) }; - errno::into_io_result(r).map(|cnt| cnt as usize) - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - 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) }; - errno::into_io_result(r).and_then(|cnt| { - if cnt as usize == buf.len() { - Ok(()) - } else { - Err(ErrorKind::WriteZero.into()) - } - }) - } -} - -impl Debug for CharBackend { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // SAFETY: accessed just to print the values - let chr = self.inner.as_ptr(); - Debug::fmt(unsafe { &*chr }, f) - } -} - -// FIXME: use something like PinnedDrop from the pinned_init crate -impl Drop for CharBackend { - fn drop(&mut self) { - self.disable_handlers(); - } -} - -impl CharBackend { - /// Enable the front-end's character device handlers, if there is an - /// associated `Chardev`. - pub fn enable_handlers< - 'chardev, - 'owner: 'chardev, - T, - CanReceiveFn: for<'a> FnCall<(&'a T,), u32>, - ReceiveFn: for<'a, 'b> FnCall<(&'a T, &'b [u8])>, - EventFn: for<'a> FnCall<(&'a T, Event)>, - >( - // When "self" is dropped, the handlers are automatically disabled. - // However, this is not necessarily true if the owner is dropped. - // So require the owner to outlive the character device. - &'chardev self, - owner: &'owner T, - _can_receive: CanReceiveFn, - _receive: ReceiveFn, - _event: EventFn, - ) { - unsafe extern "C" fn rust_can_receive_cb FnCall<(&'a T,), u32>>( - opaque: *mut c_void, - ) -> c_int { - // SAFETY: the values are safe according to the contract of - // enable_handlers() and qemu_chr_fe_set_handlers() - let owner: &T = unsafe { &*(opaque.cast::()) }; - let r = F::call((owner,)); - r.try_into().unwrap() - } - - unsafe extern "C" fn rust_receive_cb FnCall<(&'a T, &'b [u8])>>( - opaque: *mut c_void, - buf: *const u8, - size: c_int, - ) { - // SAFETY: the values are safe according to the contract of - // enable_handlers() and qemu_chr_fe_set_handlers() - let owner: &T = unsafe { &*(opaque.cast::()) }; - let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) }; - F::call((owner, buf)) - } - - unsafe extern "C" fn rust_event_cb FnCall<(&'a T, Event)>>( - opaque: *mut c_void, - event: Event, - ) { - // SAFETY: the values are safe according to the contract of - // enable_handlers() and qemu_chr_fe_set_handlers() - let owner: &T = unsafe { &*(opaque.cast::()) }; - F::call((owner, event)) - } - - const { assert!(CanReceiveFn::IS_SOME) }; - let receive_cb: Option = - if ReceiveFn::is_some() { - Some(rust_receive_cb::) - } else { - None - }; - let event_cb: Option = if EventFn::is_some() { - Some(rust_event_cb::) - } else { - None - }; - - let mut chr = self.inner.borrow_mut(); - // SAFETY: the borrow promises that the BQL is taken - unsafe { - bindings::qemu_chr_fe_set_handlers( - addr_of_mut!(*chr), - Some(rust_can_receive_cb::), - receive_cb, - event_cb, - None, - (owner as *const T).cast_mut().cast::(), - core::ptr::null_mut(), - true, - ); - } - } - - /// Disable the front-end's character device handlers. - pub fn disable_handlers(&self) { - let mut chr = self.inner.borrow_mut(); - // SAFETY: the borrow promises that the BQL is taken - unsafe { - bindings::qemu_chr_fe_set_handlers( - addr_of_mut!(*chr), - None, - None, - None, - None, - core::ptr::null_mut(), - core::ptr::null_mut(), - true, - ); - } - } - - /// Notify that the frontend is ready to receive data. - pub fn accept_input(&self) { - let mut chr = self.inner.borrow_mut(); - // SAFETY: the borrow promises that the BQL is taken - unsafe { bindings::qemu_chr_fe_accept_input(addr_of_mut!(*chr)) } - } - - /// Temporarily borrow the character device, allowing it to be used - /// as an implementor of `Write`. Note that it is not valid to drop - /// 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 + '_ { - CharBackendMut(self.inner.borrow_mut()) - } - - /// Send a continuous stream of zero bits on the line if `enabled` is - /// true, or a short stream if `enabled` is false. - pub fn send_break(&self, long: bool) -> io::Result<()> { - let mut chr = self.inner.borrow_mut(); - let mut duration: c_int = long.into(); - // SAFETY: the borrow promises that the BQL is taken - let r = unsafe { - bindings::qemu_chr_fe_ioctl( - addr_of_mut!(*chr), - bindings::CHR_IOCTL_SERIAL_SET_BREAK as i32, - addr_of_mut!(duration).cast::(), - ) - }; - - errno::into_io_result(r).map(|_| ()) - } - - /// Write data to a character backend from the front end. This function - /// will send data from the front end to the back end. Unlike - /// `write`, this function will block if the back end cannot - /// consume all of the data attempted to be written. - /// - /// Returns the number of bytes consumed (0 if no associated Chardev) or an - /// error. - pub fn write(&self, buf: &[u8]) -> io::Result { - let len = buf.len().try_into().unwrap(); - // SAFETY: qemu_chr_fe_write is thread-safe - let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) }; - errno::into_io_result(r).map(|cnt| cnt as usize) - } - - /// Write data to a character backend from the front end. This function - /// will send data from the front end to the back end. Unlike - /// `write`, this function will block if the back end cannot - /// consume all of the data attempted to be written. - /// - /// Returns the number of bytes consumed (0 if no associated Chardev) or an - /// error. - pub fn write_all(&self, buf: &[u8]) -> io::Result<()> { - let len = buf.len().try_into().unwrap(); - // SAFETY: qemu_chr_fe_write_all is thread-safe - let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) }; - errno::into_io_result(r).and_then(|cnt| { - if cnt as usize == buf.len() { - Ok(()) - } else { - Err(ErrorKind::WriteZero.into()) - } - }) - } -} - -unsafe impl ObjectType for Chardev { - type Class = ChardevClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CHARDEV) }; -} -qom_isa!(Chardev: Object); diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 0541050e66..d96096899d 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,7 +13,6 @@ pub mod bindings; #[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 3daf9dda2b..7efc796f50 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -10,6 +10,7 @@ use std::{ }; pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; +use chardev::Chardev; use common::{callbacks::FnCall, Opaque}; use migration::{impl_vmstate_c_struct, VMStateDescription}; use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; @@ -17,7 +18,6 @@ use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - chardev::Chardev, irq::InterruptSource, }; @@ -137,8 +137,8 @@ unsafe impl QDevProp for u64 { const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 }; } -/// Use [`bindings::qdev_prop_chr`] for [`crate::chardev::CharBackend`]. -unsafe impl QDevProp for crate::chardev::CharBackend { +/// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`]. +unsafe impl QDevProp for chardev::CharBackend { const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr }; } 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" -- cgit 1.4.1 From ee4ffbf239cbd9de8c6b6cc33283b7a64a95a956 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:55 +0200 Subject: rust: split "system" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-15-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + 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/memory.rs | 200 --------------------------------------- rust/qemu-api/src/sysbus.rs | 2 +- rust/qemu-api/wrapper.h | 3 - rust/system/Cargo.toml | 22 +++++ rust/system/build.rs | 1 + rust/system/meson.build | 42 ++++++++ rust/system/src/bindings.rs | 41 ++++++++ rust/system/src/lib.rs | 6 ++ rust/system/src/memory.rs | 200 +++++++++++++++++++++++++++++++++++++++ rust/system/wrapper.h | 29 ++++++ 25 files changed, 376 insertions(+), 232 deletions(-) delete mode 100644 rust/qemu-api/src/memory.rs 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 create mode 100644 rust/system/src/memory.rs create mode 100644 rust/system/wrapper.h (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index cac6dcdc65..432ed51354 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3523,6 +3523,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/Cargo.lock b/rust/Cargo.lock index ae852c5550..e6b75f30be 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -95,6 +95,7 @@ dependencies = [ "qemu_api", "qemu_api_macros", "qom", + "system", "util", ] @@ -136,6 +137,7 @@ dependencies = [ "qemu_api", "qemu_api_macros", "qom", + "system", "util", ] @@ -181,6 +183,7 @@ dependencies = [ "migration", "qemu_api_macros", "qom", + "system", "util", ] @@ -224,6 +227,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system" +version = "0.1.0" +dependencies = [ + "common", + "qemu_api_macros", + "qom", + "util", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0516c16591..8e210d277a 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 25007427ed..24ab294b60 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 aaf911c5f4..fae6e1b9c9 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 bc64061fb3..c65db5a517 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -13,12 +13,12 @@ use migration::{ }; use qemu_api::{ irq::{IRQState, InterruptSource}, - memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; +use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}; use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 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 50ccdee4a9..c4ffe020f6 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -11,6 +11,7 @@ _libhpet_rs = static_library( bql_rs, qemu_api_macros, qom_rs, + system_rs, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 404569aa2d..841c2ba337 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -17,19 +17,17 @@ use migration::{ VMStateDescription, VMStateDescriptionBuilder, }; use qemu_api::{ - bindings::{ - address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, - qdev_prop_uint32, qdev_prop_usize, - }, + bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, irq::InterruptSource, - memory::{ - hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, - }, prelude::*, qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; +use system::{ + bindings::{address_space_memory, address_space_stl_le, hwaddr}, + MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, +}; use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; diff --git a/rust/meson.build b/rust/meson.build index 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 3bf2dafa6d..2884c1d460 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -20,6 +20,7 @@ migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } qom = { path = "../qom" } +system = { path = "../system" } qemu_api_macros = { path = "../qemu-api-macros" } [lints] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a47f178b69..92e2581a64 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -8,7 +8,6 @@ c_enums = [ 'MachineInitPhase', 'MemoryDeviceInfoKind', 'ResetType', - 'device_endian', ] _qemu_api_bindgen_args = [] foreach enum : c_enums @@ -24,8 +23,11 @@ endforeach blocked_type = [ 'Chardev', 'Error', + 'MemTxAttrs', + 'MemoryRegion', 'ObjectClass', 'VMStateDescription', + 'device_endian', ] foreach type: blocked_type _qemu_api_bindgen_args += ['--blocklist-type', type] @@ -54,7 +56,6 @@ _qemu_api_rs = static_library( 'src/lib.rs', 'src/bindings.rs', 'src/irq.rs', - 'src/memory.rs', 'src/prelude.rs', 'src/qdev.rs', 'src/sysbus.rs', @@ -65,7 +66,7 @@ _qemu_api_rs = static_library( rust_abi: 'rust', rust_args: _qemu_api_cfg, dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - qom_rs, util_rs, hwcore], + qom_rs, system_rs, util_rs, hwcore], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 526bcf8e31..63b805c76e 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -24,6 +24,7 @@ use chardev::bindings::Chardev; 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 @@ include!("bindings.inc.rs"); #[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 d96096899d..8d57440478 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -14,7 +14,6 @@ pub mod bindings; pub mod prelude; pub mod irq; -pub mod memory; pub mod qdev; pub mod sysbus; diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs deleted file mode 100644 index ecbbd9b604..0000000000 --- a/rust/qemu-api/src/memory.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` - -use std::{ - ffi::{c_uint, c_void, CStr, CString}, - 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 struct MemoryRegionOps( - bindings::MemoryRegionOps, - // Note: quite often you'll see PhantomData mentioned when discussing - // covariance and contravariance; you don't need any of those to understand - // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* - // holds callbacks that take an argument of type &T, except the type is erased - // before the callback is stored in the bindings::MemoryRegionOps field. - // The argument of PhantomData is a function pointer in order to represent - // that relationship; while that will also provide desirable and safe variance - // for T, variance is not the point but just a consequence. - PhantomData, -); - -// SAFETY: When a *const T is passed to the callbacks, the call itself -// is done in a thread-safe manner. The invocation is okay as long as -// T itself is `Sync`. -unsafe impl Sync for MemoryRegionOps {} - -#[derive(Clone)] -pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); - -unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( - opaque: *mut c_void, - addr: hwaddr, - size: c_uint, -) -> u64 { - F::call((unsafe { &*(opaque.cast::()) }, addr, size)) -} - -unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( - opaque: *mut c_void, - addr: hwaddr, - data: u64, - size: c_uint, -) { - F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) -} - -impl MemoryRegionOpsBuilder { - #[must_use] - pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { - self.0.read = Some(memory_region_ops_read_cb::); - self - } - - #[must_use] - pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { - self.0.write = Some(memory_region_ops_write_cb::); - self - } - - #[must_use] - pub const fn big_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; - self - } - - #[must_use] - pub const fn little_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; - self - } - - #[must_use] - pub const fn native_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; - self - } - - #[must_use] - pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { - self.0.valid.min_access_size = min; - self.0.valid.max_access_size = max; - self - } - - #[must_use] - pub const fn valid_unaligned(mut self) -> Self { - self.0.valid.unaligned = true; - self - } - - #[must_use] - pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { - self.0.impl_.min_access_size = min; - self.0.impl_.max_access_size = max; - self - } - - #[must_use] - pub const fn impl_unaligned(mut self) -> Self { - self.0.impl_.unaligned = true; - self - } - - #[must_use] - pub const fn build(self) -> MemoryRegionOps { - MemoryRegionOps::(self.0, PhantomData) - } - - #[must_use] - pub const fn new() -> Self { - Self(bindings::MemoryRegionOps::ZERO, PhantomData) - } -} - -impl Default for MemoryRegionOpsBuilder { - fn default() -> Self { - Self::new() - } -} - -/// A safe wrapper around [`bindings::MemoryRegion`]. -#[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] -pub struct MemoryRegion(Opaque); - -unsafe impl Send for MemoryRegion {} -unsafe impl Sync for MemoryRegion {} - -impl MemoryRegion { - // inline to ensure that it is not included in tests, which only - // link to hwcore and qom. FIXME: inlining is actually the opposite - // of what we want, since this is the type-erased version of the - // init_io function below. Look into splitting the qemu_api crate. - #[inline(always)] - unsafe fn do_init_io( - slot: *mut bindings::MemoryRegion, - owner: *mut bindings::Object, - ops: &'static bindings::MemoryRegionOps, - name: &'static str, - size: u64, - ) { - unsafe { - let cstr = CString::new(name).unwrap(); - memory_region_init_io( - slot, - owner, - ops, - owner.cast::(), - cstr.as_ptr(), - size, - ); - } - } - - pub fn init_io>( - this: &mut MaybeUninitField<'_, T, Self>, - ops: &'static MemoryRegionOps, - name: &'static str, - size: u64, - ) { - unsafe { - Self::do_init_io( - this.as_mut_ptr().cast(), - MaybeUninitField::parent_mut(this).cast(), - &ops.0, - name, - size, - ); - } - } -} - -unsafe impl ObjectType for MemoryRegion { - type Class = bindings::MemoryRegionClass; - 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 -/// attributes are specified. -/// -/// Bus masters which don't specify any attributes will get this, -/// which has all attribute bits clear except the topmost one -/// (so that we can distinguish "all attributes deliberately clear" -/// from "didn't specify" if necessary). -pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { - unspecified: true, - ..Zeroable::ZERO -}; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index b883d7eaf1..dda71ebda7 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -9,11 +9,11 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; use common::Opaque; use qom::{prelude::*, Owned}; +use system::MemoryRegion; use crate::{ bindings, irq::{IRQState, InterruptSource}, - memory::MemoryRegion, qdev::{DeviceImpl, DeviceState}, }; diff --git a/rust/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/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..ae9b932d29 --- /dev/null +++ b/rust/system/meson.build @@ -0,0 +1,42 @@ +c_enums = [ + 'device_endian', +] +_system_bindgen_args = [] +foreach enum : c_enums + _system_bindgen_args += ['--rustified-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_system_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _system_bindgen_args, +) + +_system_rs = static_library( + 'system', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/memory.rs', + ], + {'.': _system_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], + dependencies: [common_rs, qemu_api_macros], +) + +system_rs = declare_dependency(link_with: [_system_rs], + dependencies: [qemu_api_macros, hwcore]) diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs new file mode 100644 index 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/system/src/memory.rs b/rust/system/src/memory.rs new file mode 100644 index 0000000000..29568ed767 --- /dev/null +++ b/rust/system/src/memory.rs @@ -0,0 +1,200 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` + +use std::{ + ffi::{c_uint, c_void, CStr, CString}, + marker::PhantomData, +}; + +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( + bindings::MemoryRegionOps, + // Note: quite often you'll see PhantomData mentioned when discussing + // covariance and contravariance; you don't need any of those to understand + // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* + // holds callbacks that take an argument of type &T, except the type is erased + // before the callback is stored in the bindings::MemoryRegionOps field. + // The argument of PhantomData is a function pointer in order to represent + // that relationship; while that will also provide desirable and safe variance + // for T, variance is not the point but just a consequence. + PhantomData, +); + +// SAFETY: When a *const T is passed to the callbacks, the call itself +// is done in a thread-safe manner. The invocation is okay as long as +// T itself is `Sync`. +unsafe impl Sync for MemoryRegionOps {} + +#[derive(Clone)] +pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); + +unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( + opaque: *mut c_void, + addr: hwaddr, + size: c_uint, +) -> u64 { + F::call((unsafe { &*(opaque.cast::()) }, addr, size)) +} + +unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( + opaque: *mut c_void, + addr: hwaddr, + data: u64, + size: c_uint, +) { + F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) +} + +impl MemoryRegionOpsBuilder { + #[must_use] + pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { + self.0.read = Some(memory_region_ops_read_cb::); + self + } + + #[must_use] + pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { + self.0.write = Some(memory_region_ops_write_cb::); + self + } + + #[must_use] + pub const fn big_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; + self + } + + #[must_use] + pub const fn little_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; + self + } + + #[must_use] + pub const fn native_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; + self + } + + #[must_use] + pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { + self.0.valid.min_access_size = min; + self.0.valid.max_access_size = max; + self + } + + #[must_use] + pub const fn valid_unaligned(mut self) -> Self { + self.0.valid.unaligned = true; + self + } + + #[must_use] + pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { + self.0.impl_.min_access_size = min; + self.0.impl_.max_access_size = max; + self + } + + #[must_use] + pub const fn impl_unaligned(mut self) -> Self { + self.0.impl_.unaligned = true; + self + } + + #[must_use] + pub const fn build(self) -> MemoryRegionOps { + MemoryRegionOps::(self.0, PhantomData) + } + + #[must_use] + pub const fn new() -> Self { + Self(bindings::MemoryRegionOps::ZERO, PhantomData) + } +} + +impl Default for MemoryRegionOpsBuilder { + fn default() -> Self { + Self::new() + } +} + +/// A safe wrapper around [`bindings::MemoryRegion`]. +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct MemoryRegion(Opaque); + +unsafe impl Send for MemoryRegion {} +unsafe impl Sync for MemoryRegion {} + +impl MemoryRegion { + // inline to ensure that it is not included in tests, which only + // link to hwcore and qom. FIXME: inlining is actually the opposite + // of what we want, since this is the type-erased version of the + // init_io function below. Look into splitting the qemu_api crate. + #[inline(always)] + unsafe fn do_init_io( + slot: *mut bindings::MemoryRegion, + owner: *mut bindings::Object, + ops: &'static bindings::MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + let cstr = CString::new(name).unwrap(); + memory_region_init_io( + slot, + owner, + ops, + owner.cast::(), + cstr.as_ptr(), + size, + ); + } + } + + pub fn init_io>( + this: &mut MaybeUninitField<'_, T, Self>, + ops: &'static MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + Self::do_init_io( + this.as_mut_ptr().cast(), + MaybeUninitField::parent_mut(this).cast(), + &ops.0, + name, + size, + ); + } + } +} + +unsafe impl ObjectType for MemoryRegion { + type Class = bindings::MemoryRegionClass; + 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 +/// attributes are specified. +/// +/// Bus masters which don't specify any attributes will get this, +/// which has all attribute bits clear except the topmost one +/// (so that we can distinguish "all attributes deliberately clear" +/// from "didn't specify" if necessary). +pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { + unspecified: true, + ..Zeroable::ZERO +}; 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" -- cgit 1.4.1 From 5e588c9d08b0da64fab7f370e65744cb7a4174ef Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:56 +0200 Subject: rust: split "hwcore" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-16-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 17 ++ rust/Cargo.toml | 1 + rust/bindings/src/lib.rs | 64 ++++++ rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 10 +- rust/hw/core/Cargo.toml | 26 +++ rust/hw/core/build.rs | 1 + rust/hw/core/meson.build | 80 +++++++ rust/hw/core/src/bindings.rs | 41 ++++ rust/hw/core/src/irq.rs | 115 ++++++++++ rust/hw/core/src/lib.rs | 14 ++ rust/hw/core/src/qdev.rs | 459 ++++++++++++++++++++++++++++++++++++++ rust/hw/core/src/sysbus.rs | 122 ++++++++++ rust/hw/core/tests/tests.rs | 156 +++++++++++++ rust/hw/core/wrapper.h | 32 +++ rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 26 +-- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 10 +- rust/qemu-api-macros/src/tests.rs | 20 +- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 17 +- rust/qemu-api/src/bindings.rs | 10 - rust/qemu-api/src/irq.rs | 115 ---------- rust/qemu-api/src/lib.rs | 4 - rust/qemu-api/src/prelude.rs | 4 - rust/qemu-api/src/qdev.rs | 459 -------------------------------------- rust/qemu-api/src/sysbus.rs | 122 ---------- rust/qemu-api/tests/tests.rs | 161 ------------- rust/qemu-api/wrapper.h | 6 - 33 files changed, 1168 insertions(+), 931 deletions(-) create mode 100644 rust/bindings/src/lib.rs create mode 100644 rust/hw/core/Cargo.toml create mode 120000 rust/hw/core/build.rs create mode 100644 rust/hw/core/meson.build create mode 100644 rust/hw/core/src/bindings.rs create mode 100644 rust/hw/core/src/irq.rs create mode 100644 rust/hw/core/src/lib.rs create mode 100644 rust/hw/core/src/qdev.rs create mode 100644 rust/hw/core/src/sysbus.rs create mode 100644 rust/hw/core/tests/tests.rs create mode 100644 rust/hw/core/wrapper.h delete mode 100644 rust/qemu-api/src/irq.rs delete mode 100644 rust/qemu-api/src/qdev.rs delete mode 100644 rust/qemu-api/src/sysbus.rs delete mode 100644 rust/qemu-api/tests/tests.rs (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index 432ed51354..92d575b535 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3518,6 +3518,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/Cargo.lock b/rust/Cargo.lock index e6b75f30be..77118e882b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -91,6 +91,7 @@ version = "0.1.0" dependencies = [ "bql", "common", + "hwcore", "migration", "qemu_api", "qemu_api_macros", @@ -99,6 +100,20 @@ dependencies = [ "util", ] +[[package]] +name = "hwcore" +version = "0.1.0" +dependencies = [ + "bql", + "chardev", + "common", + "migration", + "qemu_api_macros", + "qom", + "system", + "util", +] + [[package]] name = "itertools" version = "0.11.0" @@ -133,6 +148,7 @@ dependencies = [ "bql", "chardev", "common", + "hwcore", "migration", "qemu_api", "qemu_api_macros", @@ -180,6 +196,7 @@ dependencies = [ "bql", "chardev", "common", + "hwcore", "migration", "qemu_api_macros", "qom", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8e210d277a..8ec07d2065 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,6 +9,7 @@ members = [ "qemu-api", "qom", "system", + "hw/core", "hw/char/pl011", "hw/timer/hpet", "util", diff --git a/rust/bindings/src/lib.rs b/rust/bindings/src/lib.rs new file mode 100644 index 0000000000..5bf03b1370 --- /dev/null +++ b/rust/bindings/src/lib.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc +)] + +//! `bindgen`-generated declarations. + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +// SAFETY: these are implemented in C; the bindings need to assert that the +// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. +// When bindings for character devices are introduced, this can be +// moved to the Opaque<> wrapper in src/chardev.rs. +unsafe impl Send for CharBackend {} +unsafe impl Sync for CharBackend {} + +// SAFETY: this is a pure data struct +unsafe impl Send for CoalescedMemoryRange {} +unsafe impl Sync for CoalescedMemoryRange {} + +// SAFETY: these are constants and vtables; the Send and Sync requirements +// are deferred to the unsafe callbacks that they contain +unsafe impl Send for MemoryRegionOps {} +unsafe impl Sync for MemoryRegionOps {} + +unsafe impl Send for Property {} +unsafe impl Sync for Property {} + +unsafe impl Send for TypeInfo {} +unsafe impl Sync for TypeInfo {} + +unsafe impl Send for VMStateDescription {} +unsafe impl Sync for VMStateDescription {} + +unsafe impl Send for VMStateField {} +unsafe impl Sync for VMStateField {} + +unsafe impl Send for VMStateInfo {} +unsafe impl Sync for VMStateInfo {} + +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for VMStateFlags { + fn default() -> Self { + Self(0) + } +} diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 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 fae6e1b9c9..fac0432113 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 c65db5a517..a6a17d9f2d 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -7,16 +7,14 @@ use std::{ffi::CStr, mem::size_of}; use bql::BqlRefCell; use chardev::{CharBackend, Chardev, Event}; use common::{static_assert, uninit_field_mut}; +use hwcore::{ + Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource, + ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, +}; use migration::{ self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder, }; -use qemu_api::{ - irq::{IRQState, InterruptSource}, - prelude::*, - qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, - sysbus::{SysBusDevice, SysBusDeviceImpl}, -}; use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}; use util::{log::Log, log_mask_ln}; diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml new file mode 100644 index 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..7dd1ade6f0 --- /dev/null +++ b/rust/hw/core/meson.build @@ -0,0 +1,80 @@ +_hwcore_bindgen_args = [] +c_enums = [ + 'DeviceCategory', + 'GpioPolarity', + 'MachineInitPhase', + 'ResetType', +] +foreach enum : c_enums + _hwcore_bindgen_args += ['--rustified-enum', enum] +endforeach + +blocked_type = [ + 'Chardev', + 'Error', + 'ObjectClass', + 'MemoryRegion', + 'VMStateDescription', +] +foreach type: blocked_type + _hwcore_bindgen_args += ['--blocklist-type', type] +endforeach + +c_bitfields = [ + 'ClockEvent', +] +foreach enum : c_bitfields + _hwcore_bindgen_args += ['--bitfield-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_hwcore_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _hwcore_bindgen_args, +) + +_hwcore_rs = static_library( + 'hwcore', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/irq.rs', + 'src/qdev.rs', + 'src/sysbus.rs', + ], + {'.': _hwcore_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], + dependencies: [qemu_api_macros, common_rs], +) + +hwcore_rs = declare_dependency(link_with: [_hwcore_rs], + dependencies: [qom_rs, hwcore]) + +test('rust-hwcore-rs-integration', + executable( + 'rust-hwcore-rs-integration', + files('tests/tests.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--test'], + install: false, + dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_api_macros, util_rs]), + args: [ + '--test', '--test-threads', '1', + '--format', 'pretty', + ], + protocol: 'rust', + suite: ['unit', 'rust']) diff --git a/rust/hw/core/src/bindings.rs b/rust/hw/core/src/bindings.rs new file mode 100644 index 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/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs new file mode 100644 index 0000000000..fead2bbe8e --- /dev/null +++ b/rust/hw/core/src/irq.rs @@ -0,0 +1,115 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for interrupt sources + +use std::{ + ffi::{c_int, CStr}, + marker::PhantomData, + ptr, +}; + +use bql::BqlCell; +use common::Opaque; +use qom::{prelude::*, ObjectClass}; + +use crate::bindings::{self, qemu_set_irq}; + +/// An opaque wrapper around [`bindings::IRQState`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct IRQState(Opaque); + +/// Interrupt sources are used by devices to pass changes to a value (typically +/// a boolean). The interrupt sink is usually an interrupt controller or +/// GPIO controller. +/// +/// As far as devices are concerned, interrupt sources are always active-high: +/// for example, `InterruptSource`'s [`raise`](InterruptSource::raise) +/// method sends a `true` value to the sink. If the guest has to see a +/// different polarity, that change is performed by the board between the +/// device and the interrupt controller. +/// +/// 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 [`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 +/// devices are generally shared objects, interrupt sources are an example of +/// the interior mutability pattern. +/// +/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell` +/// allows access from whatever thread has it. +#[derive(Debug)] +#[repr(transparent)] +pub struct InterruptSource +where + c_int: From, +{ + cell: BqlCell<*mut bindings::IRQState>, + _marker: PhantomData, +} + +// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken +unsafe impl Sync for InterruptSource where c_int: From {} + +impl InterruptSource { + /// Send a low (`false`) value to the interrupt sink. + pub fn lower(&self) { + self.set(false); + } + + /// Send a high-low pulse to the interrupt sink. + pub fn pulse(&self) { + self.set(true); + self.set(false); + } + + /// Send a high (`true`) value to the interrupt sink. + pub fn raise(&self) { + self.set(true); + } +} + +impl InterruptSource +where + c_int: From, +{ + /// Send `level` to the interrupt sink. + pub fn set(&self, level: T) { + let ptr = self.cell.get(); + // SAFETY: the pointer is retrieved under the BQL and remains valid + // until the BQL is released, which is after qemu_set_irq() is entered. + unsafe { + qemu_set_irq(ptr, level.into()); + } + } + + pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState { + self.cell.as_ptr() + } + + pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { + assert!(!slice.is_empty()); + slice[0].as_ptr() + } +} + +impl Default for InterruptSource { + fn default() -> Self { + InterruptSource { + cell: BqlCell::new(ptr::null_mut()), + _marker: PhantomData, + } + } +} + +unsafe impl ObjectType for IRQState { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) }; +} + +qom_isa!(IRQState: Object); 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/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs new file mode 100644 index 0000000000..8e9702ce0b --- /dev/null +++ b/rust/hw/core/src/qdev.rs @@ -0,0 +1,459 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings to create devices and access device functionality from Rust. + +use std::{ + ffi::{c_int, c_void, CStr, CString}, + ptr::NonNull, +}; + +use chardev::Chardev; +use common::{callbacks::FnCall, Opaque}; +use migration::{impl_vmstate_c_struct, VMStateDescription}; +use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; +use util::{Error, Result}; + +pub use crate::bindings::{ClockEvent, DeviceClass, Property, ResetType}; +use crate::{ + bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, + irq::InterruptSource, +}; + +/// A safe wrapper around [`bindings::Clock`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Clock(Opaque); + +unsafe impl Send for Clock {} +unsafe impl Sync for Clock {} + +/// A safe wrapper around [`bindings::DeviceState`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct DeviceState(Opaque); + +unsafe impl Send for DeviceState {} +unsafe impl Sync for DeviceState {} + +/// Trait providing the contents of the `ResettablePhases` struct, +/// which is part of the QOM `Resettable` interface. +pub trait ResettablePhasesImpl { + /// If not None, this is called when the object enters reset. It + /// can reset local state of the object, but it must not do anything that + /// has a side-effect on other objects, such as raising or lowering an + /// [`InterruptSource`], or reading or writing guest memory. It takes the + /// reset's type as argument. + const ENTER: Option = None; + + /// If not None, this is called when the object for entry into reset, once + /// every object in the system which is being reset has had its + /// `ResettablePhasesImpl::ENTER` method called. At this point devices + /// can do actions that affect other objects. + /// + /// If in doubt, implement this method. + const HOLD: Option = None; + + /// If not None, this phase is called when the object leaves the reset + /// state. Actions affecting other objects are permitted. + const EXIT: Option = None; +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_enter_fn( + obj: *mut bindings::Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::ENTER.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_hold_fn( + obj: *mut bindings::Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::HOLD.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_exit_fn( + obj: *mut bindings::Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::EXIT.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type. +/// +/// This trait is used by [`qemu_api_macros::Device`] derive macro. +/// +/// Base types that already have `qdev_prop_*` globals in the QEMU API should +/// use those values as exported by the [`bindings`] module, instead of +/// redefining them. +/// +/// # Safety +/// +/// This trait is marked as `unsafe` because currently having a `const` refer to +/// an `extern static` as a reference instead of a raw pointer results in this +/// compiler error: +/// +/// ```text +/// constructing invalid value: encountered reference to `extern` static in `const` +/// ``` +/// +/// This is because the compiler generally might dereference a normal reference +/// during const evaluation, but not in this case (if it did, it'd need to +/// dereference the raw pointer so this would fail to compile). +/// +/// It is the implementer's responsibility to provide a valid +/// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe. +pub unsafe trait QDevProp { + const VALUE: *const bindings::PropertyInfo; +} + +/// Use [`bindings::qdev_prop_bool`] for `bool`. +unsafe impl QDevProp for bool { + const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_bool }; +} + +/// Use [`bindings::qdev_prop_uint64`] for `u64`. +unsafe impl QDevProp for u64 { + const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 }; +} + +/// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`]. +unsafe impl QDevProp for chardev::CharBackend { + const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr }; +} + +/// Trait to define device properties. +/// +/// # Safety +/// +/// Caller is responsible for the validity of properties array. +pub unsafe trait DevicePropertiesImpl { + /// An array providing the properties that the user can set on the + /// device. + const PROPERTIES: &'static [Property] = &[]; +} + +/// Trait providing the contents of [`DeviceClass`]. +pub trait DeviceImpl: + ObjectImpl + ResettablePhasesImpl + DevicePropertiesImpl + IsA +{ + /// _Realization_ is the second stage of device creation. It contains + /// all operations that depend on device properties and can fail (note: + /// this is not yet supported for Rust devices). + /// + /// If not `None`, the parent class's `realize` method is overridden + /// with the function pointed to by `REALIZE`. + const REALIZE: Option Result<()>> = None; + + /// A `VMStateDescription` providing the migration format for the device + /// Not a `const` because referencing statics in constants is unstable + /// until Rust 1.83.0. + const VMSTATE: Option> = None; +} + +/// # Safety +/// +/// This function is only called through the QOM machinery and +/// used by `DeviceClass::class_init`. +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_realize_fn( + dev: *mut bindings::DeviceState, + errp: *mut *mut util::bindings::Error, +) { + let state = NonNull::new(dev).unwrap().cast::(); + let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); + unsafe { + Error::ok_or_propagate(result, errp); + } +} + +unsafe impl InterfaceType for ResettableClass { + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; +} + +impl ResettableClass { + /// Fill in the virtual methods of `ResettableClass` based on the + /// definitions in the `ResettablePhasesImpl` trait. + pub fn class_init(&mut self) { + if ::ENTER.is_some() { + self.phases.enter = Some(rust_resettable_enter_fn::); + } + if ::HOLD.is_some() { + self.phases.hold = Some(rust_resettable_hold_fn::); + } + if ::EXIT.is_some() { + self.phases.exit = Some(rust_resettable_exit_fn::); + } + } +} + +impl DeviceClass { + /// Fill in the virtual methods of `DeviceClass` based on the definitions in + /// the `DeviceImpl` trait. + pub fn class_init(&mut self) { + if ::REALIZE.is_some() { + self.realize = Some(rust_realize_fn::); + } + if let Some(ref vmsd) = ::VMSTATE { + self.vmsd = vmsd.as_ref(); + } + let prop = ::PROPERTIES; + if !prop.is_empty() { + unsafe { + bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); + } + } + + ResettableClass::cast::(self).class_init::(); + self.parent_class.class_init::(); + } +} + +#[macro_export] +macro_rules! define_property { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: ::std::mem::offset_of!($state, $field) as isize, + bitnr: $bitnr, + set_default: true, + defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, + ..::common::zeroable::Zeroable::ZERO + } + }; + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: ::std::mem::offset_of!($state, $field) as isize, + set_default: true, + defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, + ..::common::zeroable::Zeroable::ZERO + } + }; + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: ::std::mem::offset_of!($state, $field) as isize, + set_default: false, + ..::common::zeroable::Zeroable::ZERO + } + }; +} + +#[macro_export] +macro_rules! declare_properties { + ($ident:ident, $($prop:expr),*$(,)*) => { + pub static $ident: [$crate::bindings::Property; { + let mut len = 0; + $({ + _ = stringify!($prop); + len += 1; + })* + len + }] = [ + $($prop),*, + ]; + }; +} + +unsafe impl ObjectType for DeviceState { + type Class = DeviceClass; + 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 +/// associated functions. +impl DeviceState { + /// Add an input clock named `name`. Invoke the callback with + /// `self` as the first parameter for the events that are requested. + /// + /// The resulting clock is added as a child of `self`, but it also + /// stays alive until after `Drop::drop` is called because C code + /// keeps an extra reference to it until `device_finalize()` calls + /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in + /// which Rust code has a reference to a child object) it would be + /// possible for this function to return a `&Clock` too. + #[inline] + pub fn init_clock_in FnCall<(&'a T, ClockEvent)>>( + this: &mut ParentInit, + name: &str, + _cb: &F, + events: ClockEvent, + ) -> Owned + where + T::ParentType: IsA, + { + fn do_init_clock_in( + dev: &DeviceState, + name: &str, + cb: Option, + events: ClockEvent, + ) -> Owned { + 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 + // add one. The callback is disabled automatically when the clock + // is unparented, which happens before the device is finalized. + unsafe { + let cstr = CString::new(name).unwrap(); + let clk = bindings::qdev_init_clock_in( + dev.0.as_mut_ptr(), + cstr.as_ptr(), + cb, + dev.0.as_void_ptr(), + events.0, + ); + + let clk: &Clock = Clock::from_raw(clk); + Owned::from(clk) + } + } + + let cb: Option = if F::is_some() { + unsafe extern "C" fn rust_clock_cb FnCall<(&'a T, ClockEvent)>>( + opaque: *mut c_void, + event: ClockEvent, + ) { + // SAFETY: the opaque is "this", which is indeed a pointer to T + F::call((unsafe { &*(opaque.cast::()) }, event)) + } + Some(rust_clock_cb::) + } else { + None + }; + + do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events) + } + + /// Add an output clock named `name`. + /// + /// The resulting clock is added as a child of `self`, but it also + /// stays alive until after `Drop::drop` is called because C code + /// keeps an extra reference to it until `device_finalize()` calls + /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in + /// which Rust code has a reference to a child object) it would be + /// possible for this function to return a `&Clock` too. + #[inline] + pub fn init_clock_out(this: &mut ParentInit, name: &str) -> Owned + where + T::ParentType: IsA, + { + unsafe { + let cstr = CString::new(name).unwrap(); + let dev: &mut DeviceState = this.upcast_mut(); + let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr()); + + let clk: &Clock = Clock::from_raw(clk); + Owned::from(clk) + } + } +} + +/// Trait for methods exposed by the [`DeviceState`] class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA`. +pub trait DeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ + fn prop_set_chr(&self, propname: &str, chr: &Owned) { + assert!(bql::is_locked()); + let c_propname = CString::new(propname).unwrap(); + let chr: &Chardev = chr; + unsafe { + bindings::qdev_prop_set_chr( + self.upcast().as_mut_ptr(), + c_propname.as_ptr(), + chr.as_mut_ptr(), + ); + } + } + + fn init_gpio_in FnCall<(&'a Self::Target, u32, u32)>>( + &self, + num_lines: u32, + _cb: F, + ) { + fn do_init_gpio_in( + dev: &DeviceState, + num_lines: u32, + gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int), + ) { + unsafe { + qdev_init_gpio_in(dev.as_mut_ptr(), Some(gpio_in_cb), num_lines as c_int); + } + } + + const { assert!(F::IS_SOME) }; + unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( + opaque: *mut c_void, + line: c_int, + level: c_int, + ) { + // SAFETY: the opaque was passed as a reference to `T` + F::call((unsafe { &*(opaque.cast::()) }, line as u32, level as u32)) + } + + let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) = + rust_irq_handler::; + + do_init_gpio_in(self.upcast(), num_lines, gpio_in_cb); + } + + fn init_gpio_out(&self, pins: &[InterruptSource]) { + unsafe { + qdev_init_gpio_out( + self.upcast().as_mut_ptr(), + InterruptSource::slice_as_ptr(pins), + pins.len() as c_int, + ); + } + } +} + +impl DeviceMethods for R where R::Target: IsA {} + +unsafe impl ObjectType for Clock { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; +} + +qom_isa!(Clock: Object); + +impl_vmstate_c_struct!(Clock, bindings::vmstate_clock); diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs new file mode 100644 index 0000000000..dda71ebda7 --- /dev/null +++ b/rust/hw/core/src/sysbus.rs @@ -0,0 +1,122 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings to access `sysbus` functionality from Rust. + +use std::{ffi::CStr, ptr::addr_of_mut}; + +pub use bindings::SysBusDeviceClass; +use common::Opaque; +use qom::{prelude::*, Owned}; +use system::MemoryRegion; + +use crate::{ + bindings, + irq::{IRQState, InterruptSource}, + qdev::{DeviceImpl, DeviceState}, +}; + +/// A safe wrapper around [`bindings::SysBusDevice`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct SysBusDevice(Opaque); + +unsafe impl Send for SysBusDevice {} +unsafe impl Sync for SysBusDevice {} + +unsafe impl ObjectType for SysBusDevice { + type Class = SysBusDeviceClass; + 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 +pub trait SysBusDeviceImpl: DeviceImpl + IsA {} + +impl SysBusDeviceClass { + /// Fill in the virtual methods of `SysBusDeviceClass` based on the + /// definitions in the `SysBusDeviceImpl` trait. + pub fn class_init(self: &mut SysBusDeviceClass) { + self.parent_class.class_init::(); + } +} + +/// Trait for methods of [`SysBusDevice`] and its subclasses. +pub trait SysBusDeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Expose a memory region to the board so that it can give it an address + /// in guest memory. Note that the ordering of calls to `init_mmio` is + /// important, since whoever creates the sysbus device will refer to the + /// region with a number that corresponds to the order of calls to + /// `init_mmio`. + fn init_mmio(&self, iomem: &MemoryRegion) { + assert!(bql::is_locked()); + unsafe { + bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); + } + } + + /// Expose an interrupt source outside the device as a qdev GPIO output. + /// Note that the ordering of calls to `init_irq` is important, since + /// 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::is_locked()); + unsafe { + bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); + } + } + + // TODO: do we want a type like GuestAddress here? + fn mmio_addr(&self, id: u32) -> Option { + 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. + let sbd = unsafe { *self.upcast().as_ptr() }; + let id: usize = id.try_into().unwrap(); + if sbd.mmio[id].memory.is_null() { + None + } else { + Some(sbd.mmio[id].addr) + } + } + + // TODO: do we want a type like GuestAddress here? + fn mmio_map(&self, id: u32, addr: u64) { + assert!(bql::is_locked()); + let id: i32 = id.try_into().unwrap(); + unsafe { + bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); + } + } + + // Owned<> is used here because sysbus_connect_irq (via + // object_property_set_link) adds a reference to the IRQState, + // which can prolong its life + fn connect_irq(&self, id: u32, irq: &Owned) { + assert!(bql::is_locked()); + let id: i32 = id.try_into().unwrap(); + let irq: &IRQState = irq; + unsafe { + bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); + } + } + + fn sysbus_realize(&self) { + // TODO: return an Error + assert!(bql::is_locked()); + unsafe { + bindings::sysbus_realize( + self.upcast().as_mut_ptr(), + addr_of_mut!(util::bindings::error_fatal), + ); + } + } +} + +impl SysBusDeviceMethods for R where R::Target: IsA {} diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs new file mode 100644 index 0000000000..21ee301fa6 --- /dev/null +++ b/rust/hw/core/tests/tests.rs @@ -0,0 +1,156 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ffi::CStr, ptr::addr_of}; + +use bql::BqlCell; +use hwcore::{DeviceImpl, DeviceState, ResettablePhasesImpl, SysBusDevice}; +use migration::{VMStateDescription, VMStateDescriptionBuilder}; +use qom::{prelude::*, ObjectImpl, ParentField}; +use util::bindings::{module_call_init, module_init_type}; + +// Test that macros can compile. +pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"name") + .unmigratable() + .build(); + +#[repr(C)] +#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +pub struct DummyState { + parent: ParentField, + #[property(rename = "migrate-clk", default = true)] + migrate_clock: bool, +} + +qom_isa!(DummyState: Object, DeviceState); + +pub struct DummyClass { + parent_class: ::Class, +} + +impl DummyClass { + pub fn class_init(self: &mut DummyClass) { + self.parent_class.class_init::(); + } +} + +unsafe impl ObjectType for DummyState { + type Class = DummyClass; + const TYPE_NAME: &'static CStr = c"dummy"; +} + +impl ObjectImpl for DummyState { + type ParentType = DeviceState; + const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::; +} + +impl ResettablePhasesImpl for DummyState {} + +impl DeviceImpl for DummyState { + const VMSTATE: Option> = Some(VMSTATE); +} + +#[repr(C)] +#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +pub struct DummyChildState { + parent: ParentField, +} + +qom_isa!(DummyChildState: Object, DeviceState, DummyState); + +pub struct DummyChildClass { + parent_class: ::Class, +} + +unsafe impl ObjectType for DummyChildState { + type Class = DummyChildClass; + const TYPE_NAME: &'static CStr = c"dummy_child"; +} + +impl ObjectImpl for DummyChildState { + type ParentType = DummyState; + const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::; +} + +impl ResettablePhasesImpl for DummyChildState {} +impl DeviceImpl for DummyChildState {} + +impl DummyChildClass { + pub fn class_init(self: &mut DummyChildClass) { + self.parent_class.class_init::(); + } +} + +fn init_qom() { + static ONCE: BqlCell = BqlCell::new(false); + + bql::start_test(); + if !ONCE.get() { + unsafe { + module_call_init(module_init_type::MODULE_INIT_QOM); + } + ONCE.set(true); + } +} + +#[test] +/// Create and immediately drop an instance. +fn test_object_new() { + init_qom(); + drop(DummyState::new()); + drop(DummyChildState::new()); +} + +#[test] +#[allow(clippy::redundant_clone)] +/// Create, clone and then drop an instance. +fn test_clone() { + init_qom(); + let p = DummyState::new(); + assert_eq!(p.clone().typename(), "dummy"); + drop(p); +} + +#[test] +/// Try invoking a method on an object. +fn test_typename() { + init_qom(); + let p = DummyState::new(); + assert_eq!(p.typename(), "dummy"); +} + +// a note on all "cast" tests: usually, especially for downcasts the desired +// class would be placed on the right, for example: +// +// let sbd_ref = p.dynamic_cast::(); +// +// Here I am doing the opposite to check that the resulting type is correct. + +#[test] +#[allow(clippy::shadow_unrelated)] +/// Test casts on shared references. +fn test_cast() { + init_qom(); + let p = DummyState::new(); + let p_ptr: *mut DummyState = p.as_mut_ptr(); + let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; + + let obj_ref: &Object = p_ref.upcast(); + assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); + + let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); + assert!(sbd_ref.is_none()); + + let dev_ref: Option<&DeviceState> = obj_ref.downcast(); + assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); + + // SAFETY: the cast is wrong, but the value is only used for comparison + unsafe { + let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); + assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); + } +} 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/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 c4ffe020f6..e6f99b6778 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 841c2ba337..3031539744 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -12,17 +12,15 @@ use std::{ use bql::{BqlCell, BqlRefCell}; use common::{bitops::IntegerExt, uninit_field_mut}; +use hwcore::{ + bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, + declare_properties, define_property, DeviceImpl, DeviceMethods, DeviceState, InterruptSource, + Property, ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, +}; use migration::{ self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, VMStateDescription, VMStateDescriptionBuilder, }; -use qemu_api::{ - bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, - irq::InterruptSource, - prelude::*, - qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - sysbus::{SysBusDevice, SysBusDeviceImpl}, -}; use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; use system::{ bindings::{address_space_memory, address_space_stl_le, hwaddr}, @@ -904,9 +902,9 @@ impl ObjectImpl for HPETState { } // TODO: Make these properties user-configurable! -qemu_api::declare_properties! { +declare_properties! { HPET_PROPERTIES, - qemu_api::define_property!( + define_property!( c"timers", HPETState, num_timers, @@ -914,7 +912,7 @@ qemu_api::declare_properties! { u8, default = HPET_MIN_TIMERS ), - qemu_api::define_property!( + define_property!( c"msi", HPETState, flags, @@ -923,7 +921,7 @@ qemu_api::declare_properties! { bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, default = false, ), - qemu_api::define_property!( + define_property!( c"hpet-intcap", HPETState, int_route_cap, @@ -931,7 +929,7 @@ qemu_api::declare_properties! { u32, default = 0 ), - qemu_api::define_property!( + define_property!( c"hpet-offset-saved", HPETState, hpet_offset_saved, @@ -1004,8 +1002,8 @@ const VMSTATE_HPET: VMStateDescription = .build(); // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the -// qemu_api::declare_properties macro. -unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { +// hwcore::declare_properties macro. +unsafe impl hwcore::DevicePropertiesImpl for HPETState { const PROPERTIES: &'static [Property] = &HPET_PROPERTIES; } diff --git a/rust/meson.build b/rust/meson.build index 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-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index e643e57ebd..830b432698 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -272,24 +272,24 @@ fn derive_device_or_error(input: DeriveInput) -> Result::VALUE }; + let qdev_prop = quote! { <#field_ty as ::hwcore::QDevProp>::VALUE }; let set_default = defval.is_some(); let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); properties_expanded.push(quote! { - ::qemu_api::bindings::Property { + ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(#prop_name), info: #qdev_prop , offset: ::core::mem::offset_of!(#name, #field_name) as isize, set_default: #set_default, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, ..::common::Zeroable::ZERO } }); } Ok(quote_spanned! {input.span() => - unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name { - const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ + unsafe impl ::hwcore::DevicePropertiesImpl for #name { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ #(#properties_expanded),* ]; } diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index 76e6c57479..9ab7eab7f3 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -100,14 +100,14 @@ fn test_derive_device() { } }, quote! { - unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { - const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ - ::qemu_api::bindings::Property { + unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(c"migrate_clock"), - info: ::VALUE, + info: ::VALUE, offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, set_default: true, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, ..::common::Zeroable::ZERO } ]; @@ -127,14 +127,14 @@ fn test_derive_device() { } }, quote! { - unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState { - const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[ - ::qemu_api::bindings::Property { + unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + ::hwcore::bindings::Property { name: ::std::ffi::CStr::as_ptr(c"migrate-clk"), - info: ::VALUE, + info: ::VALUE, offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, set_default: true, - defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 }, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, ..::common::Zeroable::ZERO } ]; diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 2884c1d460..9e7afc7e3a 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 92e2581a64..2dc638782c 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -3,22 +3,12 @@ _qemu_api_cfg = run_command(rustc_args, capture: true, check: true).stdout().strip().splitlines() c_enums = [ - 'DeviceCategory', - 'GpioPolarity', - 'MachineInitPhase', 'MemoryDeviceInfoKind', - 'ResetType', ] _qemu_api_bindgen_args = [] foreach enum : c_enums _qemu_api_bindgen_args += ['--rustified-enum', enum] endforeach -c_bitfields = [ - 'ClockEvent', -] -foreach enum : c_bitfields - _qemu_api_bindgen_args += ['--bitfield-enum', enum] -endforeach blocked_type = [ 'Chardev', @@ -55,17 +45,14 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', - 'src/irq.rs', 'src/prelude.rs', - 'src/qdev.rs', - 'src/sysbus.rs', ], {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, + dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_api_macros, qom_rs, system_rs, util_rs, hwcore], ) @@ -75,7 +62,7 @@ qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], test('rust-qemu-api-integration', executable( 'rust-qemu-api-integration', - files('tests/tests.rs', 'tests/vmstate_tests.rs'), + files('tests/vmstate_tests.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 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 @@ 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 crate::bindings::Property__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::Property {} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs deleted file mode 100644 index fead2bbe8e..0000000000 --- a/rust/qemu-api/src/irq.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for interrupt sources - -use std::{ - ffi::{c_int, CStr}, - marker::PhantomData, - ptr, -}; - -use bql::BqlCell; -use common::Opaque; -use qom::{prelude::*, ObjectClass}; - -use crate::bindings::{self, qemu_set_irq}; - -/// An opaque wrapper around [`bindings::IRQState`]. -#[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] -pub struct IRQState(Opaque); - -/// Interrupt sources are used by devices to pass changes to a value (typically -/// a boolean). The interrupt sink is usually an interrupt controller or -/// GPIO controller. -/// -/// As far as devices are concerned, interrupt sources are always active-high: -/// for example, `InterruptSource`'s [`raise`](InterruptSource::raise) -/// method sends a `true` value to the sink. If the guest has to see a -/// different polarity, that change is performed by the board between the -/// device and the interrupt controller. -/// -/// 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 [`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 -/// devices are generally shared objects, interrupt sources are an example of -/// the interior mutability pattern. -/// -/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell` -/// allows access from whatever thread has it. -#[derive(Debug)] -#[repr(transparent)] -pub struct InterruptSource -where - c_int: From, -{ - cell: BqlCell<*mut bindings::IRQState>, - _marker: PhantomData, -} - -// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken -unsafe impl Sync for InterruptSource where c_int: From {} - -impl InterruptSource { - /// Send a low (`false`) value to the interrupt sink. - pub fn lower(&self) { - self.set(false); - } - - /// Send a high-low pulse to the interrupt sink. - pub fn pulse(&self) { - self.set(true); - self.set(false); - } - - /// Send a high (`true`) value to the interrupt sink. - pub fn raise(&self) { - self.set(true); - } -} - -impl InterruptSource -where - c_int: From, -{ - /// Send `level` to the interrupt sink. - pub fn set(&self, level: T) { - let ptr = self.cell.get(); - // SAFETY: the pointer is retrieved under the BQL and remains valid - // until the BQL is released, which is after qemu_set_irq() is entered. - unsafe { - qemu_set_irq(ptr, level.into()); - } - } - - pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState { - self.cell.as_ptr() - } - - pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { - assert!(!slice.is_empty()); - slice[0].as_ptr() - } -} - -impl Default for InterruptSource { - fn default() -> Self { - InterruptSource { - cell: BqlCell::new(ptr::null_mut()), - _marker: PhantomData, - } - } -} - -unsafe impl ObjectType for IRQState { - type Class = ObjectClass; - 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 8d57440478..21b886035f 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,10 +13,6 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; -pub mod irq; -pub mod qdev; -pub mod sysbus; - // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). extern crate self as qemu_api; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 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; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs deleted file mode 100644 index 7efc796f50..0000000000 --- a/rust/qemu-api/src/qdev.rs +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings to create devices and access device functionality from Rust. - -use std::{ - ffi::{c_int, c_void, CStr, CString}, - ptr::NonNull, -}; - -pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; -use chardev::Chardev; -use common::{callbacks::FnCall, Opaque}; -use migration::{impl_vmstate_c_struct, VMStateDescription}; -use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; -use util::{Error, Result}; - -use crate::{ - bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - irq::InterruptSource, -}; - -/// A safe wrapper around [`bindings::Clock`]. -#[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] -pub struct Clock(Opaque); - -unsafe impl Send for Clock {} -unsafe impl Sync for Clock {} - -/// A safe wrapper around [`bindings::DeviceState`]. -#[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] -pub struct DeviceState(Opaque); - -unsafe impl Send for DeviceState {} -unsafe impl Sync for DeviceState {} - -/// Trait providing the contents of the `ResettablePhases` struct, -/// which is part of the QOM `Resettable` interface. -pub trait ResettablePhasesImpl { - /// If not None, this is called when the object enters reset. It - /// can reset local state of the object, but it must not do anything that - /// has a side-effect on other objects, such as raising or lowering an - /// [`InterruptSource`], or reading or writing guest memory. It takes the - /// reset's type as argument. - const ENTER: Option = None; - - /// If not None, this is called when the object for entry into reset, once - /// every object in the system which is being reset has had its - /// `ResettablePhasesImpl::ENTER` method called. At this point devices - /// can do actions that affect other objects. - /// - /// If in doubt, implement this method. - const HOLD: Option = None; - - /// If not None, this phase is called when the object leaves the reset - /// state. Actions affecting other objects are permitted. - const EXIT: Option = None; -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_resettable_enter_fn( - obj: *mut bindings::Object, - typ: ResetType, -) { - let state = NonNull::new(obj).unwrap().cast::(); - T::ENTER.unwrap()(unsafe { state.as_ref() }, typ); -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_resettable_hold_fn( - obj: *mut bindings::Object, - typ: ResetType, -) { - let state = NonNull::new(obj).unwrap().cast::(); - T::HOLD.unwrap()(unsafe { state.as_ref() }, typ); -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_resettable_exit_fn( - obj: *mut bindings::Object, - typ: ResetType, -) { - let state = NonNull::new(obj).unwrap().cast::(); - T::EXIT.unwrap()(unsafe { state.as_ref() }, typ); -} - -/// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type. -/// -/// This trait is used by [`qemu_api_macros::Device`] derive macro. -/// -/// Base types that already have `qdev_prop_*` globals in the QEMU API should -/// use those values as exported by the [`bindings`] module, instead of -/// redefining them. -/// -/// # Safety -/// -/// This trait is marked as `unsafe` because currently having a `const` refer to -/// an `extern static` as a reference instead of a raw pointer results in this -/// compiler error: -/// -/// ```text -/// constructing invalid value: encountered reference to `extern` static in `const` -/// ``` -/// -/// This is because the compiler generally might dereference a normal reference -/// during const evaluation, but not in this case (if it did, it'd need to -/// dereference the raw pointer so this would fail to compile). -/// -/// It is the implementer's responsibility to provide a valid -/// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe. -pub unsafe trait QDevProp { - const VALUE: *const bindings::PropertyInfo; -} - -/// Use [`bindings::qdev_prop_bool`] for `bool`. -unsafe impl QDevProp for bool { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_bool }; -} - -/// Use [`bindings::qdev_prop_uint64`] for `u64`. -unsafe impl QDevProp for u64 { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 }; -} - -/// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`]. -unsafe impl QDevProp for chardev::CharBackend { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr }; -} - -/// Trait to define device properties. -/// -/// # Safety -/// -/// Caller is responsible for the validity of properties array. -pub unsafe trait DevicePropertiesImpl { - /// An array providing the properties that the user can set on the - /// device. - const PROPERTIES: &'static [Property] = &[]; -} - -/// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: - ObjectImpl + ResettablePhasesImpl + DevicePropertiesImpl + IsA -{ - /// _Realization_ is the second stage of device creation. It contains - /// all operations that depend on device properties and can fail (note: - /// this is not yet supported for Rust devices). - /// - /// If not `None`, the parent class's `realize` method is overridden - /// with the function pointed to by `REALIZE`. - const REALIZE: Option Result<()>> = None; - - /// A `VMStateDescription` providing the migration format for the device - /// Not a `const` because referencing statics in constants is unstable - /// until Rust 1.83.0. - const VMSTATE: Option> = None; -} - -/// # Safety -/// -/// This function is only called through the QOM machinery and -/// used by `DeviceClass::class_init`. -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_realize_fn( - dev: *mut bindings::DeviceState, - errp: *mut *mut util::bindings::Error, -) { - let state = NonNull::new(dev).unwrap().cast::(); - let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); - unsafe { - Error::ok_or_propagate(result, errp); - } -} - -unsafe impl InterfaceType for ResettableClass { - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; -} - -impl ResettableClass { - /// Fill in the virtual methods of `ResettableClass` based on the - /// definitions in the `ResettablePhasesImpl` trait. - pub fn class_init(&mut self) { - if ::ENTER.is_some() { - self.phases.enter = Some(rust_resettable_enter_fn::); - } - if ::HOLD.is_some() { - self.phases.hold = Some(rust_resettable_hold_fn::); - } - if ::EXIT.is_some() { - self.phases.exit = Some(rust_resettable_exit_fn::); - } - } -} - -impl DeviceClass { - /// Fill in the virtual methods of `DeviceClass` based on the definitions in - /// the `DeviceImpl` trait. - pub fn class_init(&mut self) { - if ::REALIZE.is_some() { - self.realize = Some(rust_realize_fn::); - } - if let Some(ref vmsd) = ::VMSTATE { - self.vmsd = vmsd.as_ref(); - } - let prop = ::PROPERTIES; - if !prop.is_empty() { - unsafe { - bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); - } - } - - ResettableClass::cast::(self).class_init::(); - self.parent_class.class_init::(); - } -} - -#[macro_export] -macro_rules! define_property { - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - bitnr: $bitnr, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..::common::zeroable::Zeroable::ZERO - } - }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..::common::zeroable::Zeroable::ZERO - } - }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - set_default: false, - ..::common::zeroable::Zeroable::ZERO - } - }; -} - -#[macro_export] -macro_rules! declare_properties { - ($ident:ident, $($prop:expr),*$(,)*) => { - pub static $ident: [$crate::bindings::Property; { - let mut len = 0; - $({ - _ = stringify!($prop); - len += 1; - })* - len - }] = [ - $($prop),*, - ]; - }; -} - -unsafe impl ObjectType for DeviceState { - type Class = DeviceClass; - 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 -/// associated functions. -impl DeviceState { - /// Add an input clock named `name`. Invoke the callback with - /// `self` as the first parameter for the events that are requested. - /// - /// The resulting clock is added as a child of `self`, but it also - /// stays alive until after `Drop::drop` is called because C code - /// keeps an extra reference to it until `device_finalize()` calls - /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in - /// which Rust code has a reference to a child object) it would be - /// possible for this function to return a `&Clock` too. - #[inline] - pub fn init_clock_in FnCall<(&'a T, ClockEvent)>>( - this: &mut ParentInit, - name: &str, - _cb: &F, - events: ClockEvent, - ) -> Owned - where - T::ParentType: IsA, - { - fn do_init_clock_in( - dev: &DeviceState, - name: &str, - cb: Option, - events: ClockEvent, - ) -> Owned { - 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 - // add one. The callback is disabled automatically when the clock - // is unparented, which happens before the device is finalized. - unsafe { - let cstr = CString::new(name).unwrap(); - let clk = bindings::qdev_init_clock_in( - dev.0.as_mut_ptr(), - cstr.as_ptr(), - cb, - dev.0.as_void_ptr(), - events.0, - ); - - let clk: &Clock = Clock::from_raw(clk); - Owned::from(clk) - } - } - - let cb: Option = if F::is_some() { - unsafe extern "C" fn rust_clock_cb FnCall<(&'a T, ClockEvent)>>( - opaque: *mut c_void, - event: ClockEvent, - ) { - // SAFETY: the opaque is "this", which is indeed a pointer to T - F::call((unsafe { &*(opaque.cast::()) }, event)) - } - Some(rust_clock_cb::) - } else { - None - }; - - do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events) - } - - /// Add an output clock named `name`. - /// - /// The resulting clock is added as a child of `self`, but it also - /// stays alive until after `Drop::drop` is called because C code - /// keeps an extra reference to it until `device_finalize()` calls - /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in - /// which Rust code has a reference to a child object) it would be - /// possible for this function to return a `&Clock` too. - #[inline] - pub fn init_clock_out(this: &mut ParentInit, name: &str) -> Owned - where - T::ParentType: IsA, - { - unsafe { - let cstr = CString::new(name).unwrap(); - let dev: &mut DeviceState = this.upcast_mut(); - let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr()); - - let clk: &Clock = Clock::from_raw(clk); - Owned::from(clk) - } - } -} - -/// Trait for methods exposed by the [`DeviceState`] class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA`. -pub trait DeviceMethods: ObjectDeref -where - Self::Target: IsA, -{ - fn prop_set_chr(&self, propname: &str, chr: &Owned) { - assert!(bql::is_locked()); - let c_propname = CString::new(propname).unwrap(); - let chr: &Chardev = chr; - unsafe { - bindings::qdev_prop_set_chr( - self.upcast().as_mut_ptr(), - c_propname.as_ptr(), - chr.as_mut_ptr(), - ); - } - } - - fn init_gpio_in FnCall<(&'a Self::Target, u32, u32)>>( - &self, - num_lines: u32, - _cb: F, - ) { - fn do_init_gpio_in( - dev: &DeviceState, - num_lines: u32, - gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int), - ) { - unsafe { - qdev_init_gpio_in(dev.as_mut_ptr(), Some(gpio_in_cb), num_lines as c_int); - } - } - - const { assert!(F::IS_SOME) }; - unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( - opaque: *mut c_void, - line: c_int, - level: c_int, - ) { - // SAFETY: the opaque was passed as a reference to `T` - F::call((unsafe { &*(opaque.cast::()) }, line as u32, level as u32)) - } - - let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) = - rust_irq_handler::; - - do_init_gpio_in(self.upcast(), num_lines, gpio_in_cb); - } - - fn init_gpio_out(&self, pins: &[InterruptSource]) { - unsafe { - qdev_init_gpio_out( - self.upcast().as_mut_ptr(), - InterruptSource::slice_as_ptr(pins), - pins.len() as c_int, - ); - } - } -} - -impl DeviceMethods for R where R::Target: IsA {} - -unsafe impl ObjectType for Clock { - type Class = ObjectClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; -} - -qom_isa!(Clock: Object); - -impl_vmstate_c_struct!(Clock, bindings::vmstate_clock); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs deleted file mode 100644 index dda71ebda7..0000000000 --- a/rust/qemu-api/src/sysbus.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings to access `sysbus` functionality from Rust. - -use std::{ffi::CStr, ptr::addr_of_mut}; - -pub use bindings::SysBusDeviceClass; -use common::Opaque; -use qom::{prelude::*, Owned}; -use system::MemoryRegion; - -use crate::{ - bindings, - irq::{IRQState, InterruptSource}, - qdev::{DeviceImpl, DeviceState}, -}; - -/// A safe wrapper around [`bindings::SysBusDevice`]. -#[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] -pub struct SysBusDevice(Opaque); - -unsafe impl Send for SysBusDevice {} -unsafe impl Sync for SysBusDevice {} - -unsafe impl ObjectType for SysBusDevice { - type Class = SysBusDeviceClass; - 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 -pub trait SysBusDeviceImpl: DeviceImpl + IsA {} - -impl SysBusDeviceClass { - /// Fill in the virtual methods of `SysBusDeviceClass` based on the - /// definitions in the `SysBusDeviceImpl` trait. - pub fn class_init(self: &mut SysBusDeviceClass) { - self.parent_class.class_init::(); - } -} - -/// Trait for methods of [`SysBusDevice`] and its subclasses. -pub trait SysBusDeviceMethods: ObjectDeref -where - Self::Target: IsA, -{ - /// Expose a memory region to the board so that it can give it an address - /// in guest memory. Note that the ordering of calls to `init_mmio` is - /// important, since whoever creates the sysbus device will refer to the - /// region with a number that corresponds to the order of calls to - /// `init_mmio`. - fn init_mmio(&self, iomem: &MemoryRegion) { - assert!(bql::is_locked()); - unsafe { - bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); - } - } - - /// Expose an interrupt source outside the device as a qdev GPIO output. - /// Note that the ordering of calls to `init_irq` is important, since - /// 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::is_locked()); - unsafe { - bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); - } - } - - // TODO: do we want a type like GuestAddress here? - fn mmio_addr(&self, id: u32) -> Option { - 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. - let sbd = unsafe { *self.upcast().as_ptr() }; - let id: usize = id.try_into().unwrap(); - if sbd.mmio[id].memory.is_null() { - None - } else { - Some(sbd.mmio[id].addr) - } - } - - // TODO: do we want a type like GuestAddress here? - fn mmio_map(&self, id: u32, addr: u64) { - assert!(bql::is_locked()); - let id: i32 = id.try_into().unwrap(); - unsafe { - bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); - } - } - - // Owned<> is used here because sysbus_connect_irq (via - // object_property_set_link) adds a reference to the IRQState, - // which can prolong its life - fn connect_irq(&self, id: u32, irq: &Owned) { - assert!(bql::is_locked()); - let id: i32 = id.try_into().unwrap(); - let irq: &IRQState = irq; - unsafe { - bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); - } - } - - fn sysbus_realize(&self) { - // TODO: return an Error - assert!(bql::is_locked()); - unsafe { - bindings::sysbus_realize( - self.upcast().as_mut_ptr(), - addr_of_mut!(util::bindings::error_fatal), - ); - } - } -} - -impl SysBusDeviceMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs deleted file mode 100644 index f2e5eb9f4f..0000000000 --- a/rust/qemu-api/tests/tests.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ffi::CStr, ptr::addr_of}; - -use bql::BqlCell; -use migration::{VMStateDescription, VMStateDescriptionBuilder}; -use qemu_api::{ - qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, - sysbus::SysBusDevice, -}; -use qom::{prelude::*, ObjectImpl, ParentField}; -use util::bindings::{module_call_init, module_init_type}; - -mod vmstate_tests; - -// Test that macros can compile. -pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"name") - .unmigratable() - .build(); - -#[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] -pub struct DummyState { - parent: ParentField, - #[property(rename = "migrate-clk", default = true)] - migrate_clock: bool, -} - -qom_isa!(DummyState: Object, DeviceState); - -pub struct DummyClass { - parent_class: ::Class, -} - -impl DummyClass { - pub fn class_init(self: &mut DummyClass) { - self.parent_class.class_init::(); - } -} - -unsafe impl ObjectType for DummyState { - type Class = DummyClass; - const TYPE_NAME: &'static CStr = c"dummy"; -} - -impl ObjectImpl for DummyState { - type ParentType = DeviceState; - const ABSTRACT: bool = false; - const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::; -} - -impl ResettablePhasesImpl for DummyState {} - -impl DeviceImpl for DummyState { - const VMSTATE: Option> = Some(VMSTATE); -} - -#[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] -pub struct DummyChildState { - parent: ParentField, -} - -qom_isa!(DummyChildState: Object, DeviceState, DummyState); - -pub struct DummyChildClass { - parent_class: ::Class, -} - -unsafe impl ObjectType for DummyChildState { - type Class = DummyChildClass; - const TYPE_NAME: &'static CStr = c"dummy_child"; -} - -impl ObjectImpl for DummyChildState { - type ParentType = DummyState; - const ABSTRACT: bool = false; - const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::; -} - -impl ResettablePhasesImpl for DummyChildState {} -impl DeviceImpl for DummyChildState {} - -impl DummyChildClass { - pub fn class_init(self: &mut DummyChildClass) { - self.parent_class.class_init::(); - } -} - -fn init_qom() { - static ONCE: BqlCell = BqlCell::new(false); - - bql::start_test(); - if !ONCE.get() { - unsafe { - module_call_init(module_init_type::MODULE_INIT_QOM); - } - ONCE.set(true); - } -} - -#[test] -/// Create and immediately drop an instance. -fn test_object_new() { - init_qom(); - drop(DummyState::new()); - drop(DummyChildState::new()); -} - -#[test] -#[allow(clippy::redundant_clone)] -/// Create, clone and then drop an instance. -fn test_clone() { - init_qom(); - let p = DummyState::new(); - assert_eq!(p.clone().typename(), "dummy"); - drop(p); -} - -#[test] -/// Try invoking a method on an object. -fn test_typename() { - init_qom(); - let p = DummyState::new(); - assert_eq!(p.typename(), "dummy"); -} - -// a note on all "cast" tests: usually, especially for downcasts the desired -// class would be placed on the right, for example: -// -// let sbd_ref = p.dynamic_cast::(); -// -// Here I am doing the opposite to check that the resulting type is correct. - -#[test] -#[allow(clippy::shadow_unrelated)] -/// Test casts on shared references. -fn test_cast() { - init_qom(); - let p = DummyState::new(); - let p_ptr: *mut DummyState = p.as_mut_ptr(); - let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; - - let obj_ref: &Object = p_ref.upcast(); - assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); - - let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); - assert!(sbd_ref.is_none()); - - let dev_ref: Option<&DeviceState> = obj_ref.downcast(); - assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); - - // SAFETY: the cast is wrong, but the value is only used for comparison - unsafe { - let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); - assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); - } -} 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" -- cgit 1.4.1 From d58fcd05ffc682fbad02cd8d0bee840cb7997e3e Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:50:00 +0200 Subject: rust: repurpose qemu_api -> tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Link: https://lore.kernel.org/r/20250827104147.717203-20-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- rust/Cargo.lock | 30 +- rust/Cargo.toml | 2 +- rust/meson.build | 4 +- rust/qemu-api/.gitignore | 2 - rust/qemu-api/Cargo.toml | 28 -- rust/qemu-api/README.md | 19 -- rust/qemu-api/build.rs | 1 - rust/qemu-api/meson.build | 75 ----- rust/qemu-api/src/bindings.rs | 33 --- rust/qemu-api/src/lib.rs | 18 -- rust/qemu-api/src/prelude.rs | 5 - rust/qemu-api/tests/vmstate_tests.rs | 541 ----------------------------------- rust/qemu-api/wrapper.h | 53 ---- rust/tests/Cargo.toml | 27 ++ rust/tests/meson.build | 14 + rust/tests/tests/vmstate_tests.rs | 541 +++++++++++++++++++++++++++++++++++ 17 files changed, 600 insertions(+), 795 deletions(-) delete mode 100644 rust/qemu-api/.gitignore delete mode 100644 rust/qemu-api/Cargo.toml delete mode 100644 rust/qemu-api/README.md delete mode 120000 rust/qemu-api/build.rs delete mode 100644 rust/qemu-api/meson.build delete mode 100644 rust/qemu-api/src/bindings.rs delete mode 100644 rust/qemu-api/src/lib.rs delete mode 100644 rust/qemu-api/src/prelude.rs delete mode 100644 rust/qemu-api/tests/vmstate_tests.rs delete mode 100644 rust/qemu-api/wrapper.h create mode 100644 rust/tests/Cargo.toml create mode 100644 rust/tests/meson.build create mode 100644 rust/tests/tests/vmstate_tests.rs (limited to 'rust/qemu-api/src/lib.rs') diff --git a/MAINTAINERS b/MAINTAINERS index 23bda7d332..05e0597d53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3520,11 +3520,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/Cargo.lock b/rust/Cargo.lock index 2018d13fbf..ac79c6a34a 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -187,21 +187,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "qemu_api" -version = "0.1.0" -dependencies = [ - "bql", - "chardev", - "common", - "hwcore", - "migration", - "qemu_macros", - "qom", - "system", - "util", -] - [[package]] name = "qemu_macros" version = "0.1.0" @@ -252,6 +237,21 @@ dependencies = [ "util", ] +[[package]] +name = "tests" +version = "0.1.0" +dependencies = [ + "bql", + "chardev", + "common", + "hwcore", + "migration", + "qemu_macros", + "qom", + "system", + "util", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b2a5c230fa..d8183c614d 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..bd9b9cb83e 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -18,8 +18,6 @@ quote_rs_native = dependency('quote-1-rs', native: true) syn_rs_native = dependency('syn-2-rs', native: true) proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) -qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) - genrs = [] subdir('common') @@ -32,7 +30,7 @@ subdir('qom') subdir('system') subdir('chardev') subdir('hw/core') -subdir('qemu-api') +subdir('tests') subdir('hw') diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore deleted file mode 100644 index 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/Cargo.toml b/rust/qemu-api/Cargo.toml deleted file mode 100644 index 9abb88aa1f..0000000000 --- a/rust/qemu-api/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "qemu_api" -version = "0.1.0" -authors = ["Manos Pitsidianakis "] -description = "Rust bindings for QEMU" -readme = "README.md" -resolver = "2" -publish = false - -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -common = { path = "../common" } -chardev = { path = "../chardev" } -hwcore = { path = "../hw/core" } -migration = { path = "../migration" } -util = { path = "../util" } -bql = { path = "../bql" } -qemu_macros = { path = "../qemu-macros" } -qom = { path = "../qom" } -system = { path = "../system" } - -[lints] -workspace = true diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md deleted file mode 100644 index ed1b7ab263..0000000000 --- a/rust/qemu-api/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# QEMU bindings and API wrappers - -This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs. - -The C bindings can be generated with `bindgen`, using this build target: - -```console -$ make bindings.inc.rs -``` - -## Generate Rust documentation - -Common Cargo tasks can be performed from the QEMU build directory - -```console -$ make clippy -$ make rustfmt -$ make rustdoc -``` diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs deleted file mode 120000 index 71a3167885..0000000000 --- a/rust/qemu-api/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build deleted file mode 100644 index fe81f16d99..0000000000 --- a/rust/qemu-api/meson.build +++ /dev/null @@ -1,75 +0,0 @@ -_qemu_api_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), - capture: true, check: true).stdout().strip().splitlines() - -c_enums = [ - 'MemoryDeviceInfoKind', -] -_qemu_api_bindgen_args = [] -foreach enum : c_enums - _qemu_api_bindgen_args += ['--rustified-enum', enum] -endforeach - -blocked_type = [ - 'Chardev', - 'Error', - 'MemTxAttrs', - 'MemoryRegion', - 'ObjectClass', - 'VMStateDescription', - 'device_endian', -] -foreach type: blocked_type - _qemu_api_bindgen_args += ['--blocklist-type', type] -endforeach - -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_qemu_api_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + _qemu_api_bindgen_args, - ) - -_qemu_api_rs = static_library( - 'qemu_api', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/prelude.rs', - ], - {'.' : _qemu_api_bindings_inc_rs}, - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_macros, - qom_rs, system_rs, util_rs, hwcore], -) - -qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], - dependencies: [qemu_macros, qom, hwcore, chardev, migration]) - -test('rust-qemu-api-integration', - executable( - 'rust-qemu-api-integration', - files('tests/vmstate_tests.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: ['--test'], - install: false, - dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api_rs]), - args: [ - '--test', '--test-threads', '1', - '--format', 'pretty', - ], - protocol: 'rust', - suite: ['unit', 'rust']) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs deleted file mode 100644 index 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 21b886035f..0000000000 --- a/rust/qemu-api/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -#![cfg_attr(not(MESON), doc = include_str!("../README.md"))] -#![deny(clippy::missing_const_for_fn)] - -#[rustfmt::skip] -pub mod bindings; - -// preserve one-item-per-"use" syntax, it is clearer -// for prelude-like modules -#[rustfmt::skip] -pub mod prelude; - -// Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this -// crate). -extern crate self as qemu_api; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs deleted file mode 100644 index 8db56f9f81..0000000000 --- a/rust/qemu-api/src/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Commonly used traits and types for QEMU. diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs deleted file mode 100644 index fa9bbd6a12..0000000000 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright (C) 2025 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ - ffi::{c_void, CStr}, - mem::size_of, - ptr::NonNull, - slice, -}; - -use bql::BqlCell; -use common::Opaque; -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, - }, - impl_vmstate_forward, impl_vmstate_struct, - vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, - vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, -}; - -const FOO_ARRAY_MAX: usize = 3; - -// =========================== Test VMSTATE_FOOA =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOA: -// - VMSTATE_U16 -// - VMSTATE_UNUSED -// - VMSTATE_VARRAY_UINT16_UNSAFE -// - VMSTATE_VARRAY_MULTIPLY -#[repr(C)] -#[derive(Default)] -struct FooA { - arr: [u8; FOO_ARRAY_MAX], - num: u16, - arr_mul: [i8; FOO_ARRAY_MAX], - num_mul: u32, - elem: i8, -} - -static VMSTATE_FOOA: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_a") - .version_id(1) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(FooA, elem), - vmstate_unused!(size_of::()), - vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), - vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), - }) - .build(); - -impl_vmstate_struct!(FooA, VMSTATE_FOOA); - -#[test] -fn test_vmstate_uint16() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"elem\0" - ); - assert_eq!(foo_fields[0].offset, 16); - assert_eq!(foo_fields[0].num_offset, 0); - assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); - assert_eq!(foo_fields[0].version_id, 0); - assert_eq!(foo_fields[0].size, 1); - assert_eq!(foo_fields[0].num, 0); - assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); - assert!(foo_fields[0].vmsd.is_null()); - assert!(foo_fields[0].field_exists.is_none()); -} - -#[test] -fn test_vmstate_unused() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"unused\0" - ); - assert_eq!(foo_fields[1].offset, 0); - assert_eq!(foo_fields[1].num_offset, 0); - assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); - assert_eq!(foo_fields[1].version_id, 0); - assert_eq!(foo_fields[1].size, 8); - assert_eq!(foo_fields[1].num, 0); - assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); - assert!(foo_fields[1].vmsd.is_null()); - assert!(foo_fields[1].field_exists.is_none()); -} - -#[test] -fn test_vmstate_varray_uint16_unsafe() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to - // VMSTATE_VARRAY_UINT16_UNSAFE) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"arr\0" - ); - assert_eq!(foo_fields[2].offset, 0); - assert_eq!(foo_fields[2].num_offset, 4); - assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); - assert_eq!(foo_fields[2].version_id, 0); - assert_eq!(foo_fields[2].size, 1); - assert_eq!(foo_fields[2].num, 0); - assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); - assert!(foo_fields[2].vmsd.is_null()); - assert!(foo_fields[2].field_exists.is_none()); -} - -#[test] -fn test_vmstate_varray_multiply() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to - // VMSTATE_VARRAY_MULTIPLY) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), - b"arr_mul\0" - ); - assert_eq!(foo_fields[3].offset, 6); - assert_eq!(foo_fields[3].num_offset, 12); - assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); - assert_eq!(foo_fields[3].version_id, 0); - assert_eq!(foo_fields[3].size, 1); - assert_eq!(foo_fields[3].num, 16); - assert_eq!( - foo_fields[3].flags.0, - VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 - ); - assert!(foo_fields[3].vmsd.is_null()); - assert!(foo_fields[3].field_exists.is_none()); - - // The last VMStateField in VMSTATE_FOOA. - assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); -} - -// =========================== Test VMSTATE_FOOB =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOB: -// - VMSTATE_BOOL_V -// - VMSTATE_U64 -// - VMSTATE_STRUCT_VARRAY_UINT8 -// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 -// - VMSTATE_ARRAY -// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn -#[repr(C)] -#[derive(Default)] -struct FooB { - arr_a: [FooA; FOO_ARRAY_MAX], - num_a: u8, - arr_a_mul: [FooA; FOO_ARRAY_MAX], - num_a_mul: u32, - wrap: BqlCell, - val: bool, - // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. - arr_i64: [i64; FOO_ARRAY_MAX], - arr_a_wrap: [FooA; FOO_ARRAY_MAX], - num_a_wrap: BqlCell, -} - -fn validate_foob(_state: &FooB, _version_id: u8) -> bool { - true -} - -static VMSTATE_FOOB: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_b") - .version_id(2) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(FooB, val).with_version_id(2), - vmstate_of!(FooB, wrap), - vmstate_of!(FooB, arr_a[0 .. num_a]).with_version_id(1), - vmstate_of!(FooB, arr_a_mul[0 .. num_a_mul * 32]).with_version_id(2), - vmstate_of!(FooB, arr_i64), - vmstate_of!(FooB, arr_a_wrap[0 .. num_a_wrap], validate_foob), - }) - .build(); - -#[test] -fn test_vmstate_bool_v() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"val\0" - ); - assert_eq!(foo_fields[0].offset, 136); - assert_eq!(foo_fields[0].num_offset, 0); - assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); - assert_eq!(foo_fields[0].version_id, 2); - assert_eq!(foo_fields[0].size, 1); - assert_eq!(foo_fields[0].num, 0); - assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); - assert!(foo_fields[0].vmsd.is_null()); - assert!(foo_fields[0].field_exists.is_none()); -} - -#[test] -fn test_vmstate_uint64() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"wrap\0" - ); - assert_eq!(foo_fields[1].offset, 128); - assert_eq!(foo_fields[1].num_offset, 0); - assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); - assert_eq!(foo_fields[1].version_id, 0); - assert_eq!(foo_fields[1].size, 8); - assert_eq!(foo_fields[1].num, 0); - assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); - assert!(foo_fields[1].vmsd.is_null()); - assert!(foo_fields[1].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_varray_uint8() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to - // VMSTATE_STRUCT_VARRAY_UINT8) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"arr_a\0" - ); - assert_eq!(foo_fields[2].offset, 0); - assert_eq!(foo_fields[2].num_offset, 60); - assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. - assert_eq!(foo_fields[2].version_id, 1); - assert_eq!(foo_fields[2].size, 20); - assert_eq!(foo_fields[2].num, 0); - assert_eq!( - foo_fields[2].flags.0, - VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 - ); - assert_eq!(foo_fields[2].vmsd, VMSTATE_FOOA.as_ref()); - assert!(foo_fields[2].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_varray_uint32_multiply() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to - // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), - b"arr_a_mul\0" - ); - assert_eq!(foo_fields[3].offset, 64); - assert_eq!(foo_fields[3].num_offset, 124); - assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. - assert_eq!(foo_fields[3].version_id, 2); - assert_eq!(foo_fields[3].size, 20); - assert_eq!(foo_fields[3].num, 32); - assert_eq!( - foo_fields[3].flags.0, - VMStateFlags::VMS_STRUCT.0 - | VMStateFlags::VMS_VARRAY_UINT32.0 - | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 - ); - assert_eq!(foo_fields[3].vmsd, VMSTATE_FOOA.as_ref()); - assert!(foo_fields[3].field_exists.is_none()); -} - -#[test] -fn test_vmstate_macro_array() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to - // VMSTATE_ARRAY) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), - b"arr_i64\0" - ); - assert_eq!(foo_fields[4].offset, 144); - assert_eq!(foo_fields[4].num_offset, 0); - assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); - assert_eq!(foo_fields[4].version_id, 0); - assert_eq!(foo_fields[4].size, 8); - assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); - assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); - assert!(foo_fields[4].vmsd.is_null()); - assert!(foo_fields[4].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_varray_uint8_wrapper() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - let mut foo_b: FooB = Default::default(); - let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::(); - - // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to - // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in - // test_vmstate_struct_varray_uint8. - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(), - b"arr_a_wrap\0" - ); - assert_eq!(foo_fields[5].num_offset, 228); - assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) }); - - // The last VMStateField in VMSTATE_FOOB. - assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END); -} - -// =========================== Test VMSTATE_FOOC =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOC: -// - VMSTATE_POINTER -// - VMSTATE_ARRAY_OF_POINTER -struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. - -impl_vmstate_forward!(FooCWrapper); - -#[repr(C)] -struct FooC { - ptr: *const i32, - ptr_a: NonNull, - arr_ptr: [Box; FOO_ARRAY_MAX], - arr_ptr_wrap: FooCWrapper, -} - -unsafe impl Sync for FooC {} - -static VMSTATE_FOOC: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_c") - .version_id(3) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(FooC, ptr).with_version_id(2), - vmstate_of!(FooC, ptr_a), - vmstate_of!(FooC, arr_ptr), - vmstate_of!(FooC, arr_ptr_wrap), - }) - .build(); - -const PTR_SIZE: usize = size_of::<*mut ()>(); - -#[test] -fn test_vmstate_pointer() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"ptr\0" - ); - assert_eq!(foo_fields[0].offset, 0); - assert_eq!(foo_fields[0].num_offset, 0); - assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); - assert_eq!(foo_fields[0].version_id, 2); - assert_eq!(foo_fields[0].size, 4); - assert_eq!(foo_fields[0].num, 0); - assert_eq!( - foo_fields[0].flags.0, - VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 - ); - assert!(foo_fields[0].vmsd.is_null()); - assert!(foo_fields[0].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_pointer() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 2st VMStateField ("ptr_a") in VMSTATE_FOOC (corresponding to - // VMSTATE_STRUCT_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"ptr_a\0" - ); - assert_eq!(foo_fields[1].offset, PTR_SIZE); - assert_eq!(foo_fields[1].num_offset, 0); - assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref()); - assert_eq!(foo_fields[1].version_id, 0); - assert_eq!(foo_fields[1].size, size_of::()); - assert_eq!(foo_fields[1].num, 0); - assert_eq!( - foo_fields[1].flags.0, - VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0 - ); - assert!(foo_fields[1].info.is_null()); - assert!(foo_fields[1].field_exists.is_none()); -} - -#[test] -fn test_vmstate_macro_array_of_pointer() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to - // VMSTATE_ARRAY_OF_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"arr_ptr\0" - ); - assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); - assert_eq!(foo_fields[2].num_offset, 0); - assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); - assert_eq!(foo_fields[2].version_id, 0); - assert_eq!(foo_fields[2].size, PTR_SIZE); - assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); - assert_eq!( - foo_fields[2].flags.0, - VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 - ); - assert!(foo_fields[2].vmsd.is_null()); - assert!(foo_fields[2].field_exists.is_none()); -} - -#[test] -fn test_vmstate_macro_array_of_pointer_wrapped() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to - // VMSTATE_ARRAY_OF_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), - b"arr_ptr_wrap\0" - ); - assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); - assert_eq!(foo_fields[3].num_offset, 0); - assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 }); - assert_eq!(foo_fields[3].version_id, 0); - assert_eq!(foo_fields[3].size, PTR_SIZE); - assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); - assert_eq!( - foo_fields[3].flags.0, - VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 - ); - assert!(foo_fields[3].vmsd.is_null()); - assert!(foo_fields[3].field_exists.is_none()); - - // The last VMStateField in VMSTATE_FOOC. - assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); -} - -// =========================== Test VMSTATE_FOOD =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOD: -// - VMSTATE_VALIDATE - -// Add more member fields when vmstate_of support "test" parameter. -struct FooD; - -impl FooD { - fn validate_food_0(&self, _version_id: u8) -> bool { - true - } - - fn validate_food_1(_state: &FooD, _version_id: u8) -> bool { - false - } -} - -fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { - true -} - -static VMSTATE_FOOD: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_d") - .version_id(3) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0), - vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1), - vmstate_validate!(FooD, c"foo_d_2", validate_food_2), - }) - .build(); - -#[test] -fn test_vmstate_validate() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOD.as_ref().fields, 4) }; - let mut foo_d = FooD; - let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::(); - - // 1st VMStateField in VMSTATE_FOOD - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"foo_d_0\0" - ); - assert_eq!(foo_fields[0].offset, 0); - assert_eq!(foo_fields[0].num_offset, 0); - assert!(foo_fields[0].info.is_null()); - assert_eq!(foo_fields[0].version_id, 0); - assert_eq!(foo_fields[0].size, 0); - assert_eq!(foo_fields[0].num, 0); - assert_eq!( - foo_fields[0].flags.0, - VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0 - ); - assert!(foo_fields[0].vmsd.is_null()); - assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) }); - - // 2nd VMStateField in VMSTATE_FOOD - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"foo_d_1\0" - ); - assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) }); - - // 3rd VMStateField in VMSTATE_FOOD - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"foo_d_2\0" - ); - assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) }); - - // The last VMStateField in VMSTATE_FOOD. - assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END); -} 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 - * - * 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/tests/Cargo.toml b/rust/tests/Cargo.toml new file mode 100644 index 0000000000..8d106d896d --- /dev/null +++ b/rust/tests/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "tests" +version = "0.1.0" +description = "Rust integration tests 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] +common = { path = "../common" } +chardev = { path = "../chardev" } +hwcore = { path = "../hw/core" } +migration = { path = "../migration" } +util = { path = "../util" } +bql = { path = "../bql" } +qemu_macros = { path = "../qemu-macros" } +qom = { path = "../qom" } +system = { path = "../system" } + +[lints] +workspace = true diff --git a/rust/tests/meson.build b/rust/tests/meson.build new file mode 100644 index 0000000000..00688c66fb --- /dev/null +++ b/rust/tests/meson.build @@ -0,0 +1,14 @@ +test('rust-integration', + executable( + 'rust-integration', + files('tests/vmstate_tests.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--test'], + install: false, + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs]), + args: [ + '--test', '--test-threads', '1', + '--format', 'pretty', + ], + protocol: 'rust', + suite: ['unit', 'rust']) diff --git a/rust/tests/tests/vmstate_tests.rs b/rust/tests/tests/vmstate_tests.rs new file mode 100644 index 0000000000..fa9bbd6a12 --- /dev/null +++ b/rust/tests/tests/vmstate_tests.rs @@ -0,0 +1,541 @@ +// Copyright (C) 2025 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + ffi::{c_void, CStr}, + mem::size_of, + ptr::NonNull, + slice, +}; + +use bql::BqlCell; +use common::Opaque; +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, + }, + impl_vmstate_forward, impl_vmstate_struct, + vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, + vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, +}; + +const FOO_ARRAY_MAX: usize = 3; + +// =========================== Test VMSTATE_FOOA =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOA: +// - VMSTATE_U16 +// - VMSTATE_UNUSED +// - VMSTATE_VARRAY_UINT16_UNSAFE +// - VMSTATE_VARRAY_MULTIPLY +#[repr(C)] +#[derive(Default)] +struct FooA { + arr: [u8; FOO_ARRAY_MAX], + num: u16, + arr_mul: [i8; FOO_ARRAY_MAX], + num_mul: u32, + elem: i8, +} + +static VMSTATE_FOOA: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_a") + .version_id(1) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(FooA, elem), + vmstate_unused!(size_of::()), + vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), + vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), + }) + .build(); + +impl_vmstate_struct!(FooA, VMSTATE_FOOA); + +#[test] +fn test_vmstate_uint16() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; + + // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"elem\0" + ); + assert_eq!(foo_fields[0].offset, 16); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_unused() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; + + // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"unused\0" + ); + assert_eq!(foo_fields[1].offset, 0); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_uint16_unsafe() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; + + // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_UINT16_UNSAFE) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 4); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, 1); + assert_eq!(foo_fields[2].num, 0); + assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_multiply() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; + + // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_MULTIPLY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_mul\0" + ); + assert_eq!(foo_fields[3].offset, 6); + assert_eq!(foo_fields[3].num_offset, 12); + assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, 1); + assert_eq!(foo_fields[3].num, 16); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOA. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOB =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOB: +// - VMSTATE_BOOL_V +// - VMSTATE_U64 +// - VMSTATE_STRUCT_VARRAY_UINT8 +// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 +// - VMSTATE_ARRAY +// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn +#[repr(C)] +#[derive(Default)] +struct FooB { + arr_a: [FooA; FOO_ARRAY_MAX], + num_a: u8, + arr_a_mul: [FooA; FOO_ARRAY_MAX], + num_a_mul: u32, + wrap: BqlCell, + val: bool, + // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. + arr_i64: [i64; FOO_ARRAY_MAX], + arr_a_wrap: [FooA; FOO_ARRAY_MAX], + num_a_wrap: BqlCell, +} + +fn validate_foob(_state: &FooB, _version_id: u8) -> bool { + true +} + +static VMSTATE_FOOB: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_b") + .version_id(2) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(FooB, val).with_version_id(2), + vmstate_of!(FooB, wrap), + vmstate_of!(FooB, arr_a[0 .. num_a]).with_version_id(1), + vmstate_of!(FooB, arr_a_mul[0 .. num_a_mul * 32]).with_version_id(2), + vmstate_of!(FooB, arr_i64), + vmstate_of!(FooB, arr_a_wrap[0 .. num_a_wrap], validate_foob), + }) + .build(); + +#[test] +fn test_vmstate_bool_v() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; + + // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"val\0" + ); + assert_eq!(foo_fields[0].offset, 136); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_uint64() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; + + // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"wrap\0" + ); + assert_eq!(foo_fields[1].offset, 128); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; + + // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_a\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 60); + assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[2].version_id, 1); + assert_eq!(foo_fields[2].size, 20); + assert_eq!(foo_fields[2].num, 0); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 + ); + assert_eq!(foo_fields[2].vmsd, VMSTATE_FOOA.as_ref()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint32_multiply() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; + + // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to + // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_a_mul\0" + ); + assert_eq!(foo_fields[3].offset, 64); + assert_eq!(foo_fields[3].num_offset, 124); + assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[3].version_id, 2); + assert_eq!(foo_fields[3].size, 20); + assert_eq!(foo_fields[3].num, 32); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_STRUCT.0 + | VMStateFlags::VMS_VARRAY_UINT32.0 + | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert_eq!(foo_fields[3].vmsd, VMSTATE_FOOA.as_ref()); + assert!(foo_fields[3].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; + + // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to + // VMSTATE_ARRAY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), + b"arr_i64\0" + ); + assert_eq!(foo_fields[4].offset, 144); + assert_eq!(foo_fields[4].num_offset, 0); + assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); + assert_eq!(foo_fields[4].version_id, 0); + assert_eq!(foo_fields[4].size, 8); + assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); + assert!(foo_fields[4].vmsd.is_null()); + assert!(foo_fields[4].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8_wrapper() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; + let mut foo_b: FooB = Default::default(); + let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::(); + + // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in + // test_vmstate_struct_varray_uint8. + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(), + b"arr_a_wrap\0" + ); + assert_eq!(foo_fields[5].num_offset, 228); + assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) }); + + // The last VMStateField in VMSTATE_FOOB. + assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOC =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOC: +// - VMSTATE_POINTER +// - VMSTATE_ARRAY_OF_POINTER +struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. + +impl_vmstate_forward!(FooCWrapper); + +#[repr(C)] +struct FooC { + ptr: *const i32, + ptr_a: NonNull, + arr_ptr: [Box; FOO_ARRAY_MAX], + arr_ptr_wrap: FooCWrapper, +} + +unsafe impl Sync for FooC {} + +static VMSTATE_FOOC: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_c") + .version_id(3) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(FooC, ptr).with_version_id(2), + vmstate_of!(FooC, ptr_a), + vmstate_of!(FooC, arr_ptr), + vmstate_of!(FooC, arr_ptr_wrap), + }) + .build(); + +const PTR_SIZE: usize = size_of::<*mut ()>(); + +#[test] +fn test_vmstate_pointer() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; + + // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"ptr\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 4); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_pointer() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; + + // 2st VMStateField ("ptr_a") in VMSTATE_FOOC (corresponding to + // VMSTATE_STRUCT_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"ptr_a\0" + ); + assert_eq!(foo_fields[1].offset, PTR_SIZE); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref()); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, size_of::()); + assert_eq!(foo_fields[1].num, 0); + assert_eq!( + foo_fields[1].flags.0, + VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0 + ); + assert!(foo_fields[1].info.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; + + // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_ptr\0" + ); + assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); + assert_eq!(foo_fields[2].num_offset, 0); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, PTR_SIZE); + assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer_wrapped() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; + + // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_ptr_wrap\0" + ); + assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); + assert_eq!(foo_fields[3].num_offset, 0); + assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, PTR_SIZE); + assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOC. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOD =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOD: +// - VMSTATE_VALIDATE + +// Add more member fields when vmstate_of support "test" parameter. +struct FooD; + +impl FooD { + fn validate_food_0(&self, _version_id: u8) -> bool { + true + } + + fn validate_food_1(_state: &FooD, _version_id: u8) -> bool { + false + } +} + +fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { + true +} + +static VMSTATE_FOOD: VMStateDescription = VMStateDescriptionBuilder::::new() + .name(c"foo_d") + .version_id(3) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0), + vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1), + vmstate_validate!(FooD, c"foo_d_2", validate_food_2), + }) + .build(); + +#[test] +fn test_vmstate_validate() { + let foo_fields: &[VMStateField] = + unsafe { slice::from_raw_parts(VMSTATE_FOOD.as_ref().fields, 4) }; + let mut foo_d = FooD; + let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::(); + + // 1st VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"foo_d_0\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert!(foo_fields[0].info.is_null()); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 0); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) }); + + // 2nd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"foo_d_1\0" + ); + assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) }); + + // 3rd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"foo_d_2\0" + ); + assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) }); + + // The last VMStateField in VMSTATE_FOOD. + assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END); +} -- cgit 1.4.1