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 --- 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 +++ 8 files changed, 1197 insertions(+) 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 (limited to 'rust/common/src') 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() }; +} -- 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/common/src') 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 0d93f8177310515ae2d8aea8e1320e53818d70bd Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:57 +0200 Subject: rust: rename qemu_api_macros -> qemu_macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since "qemu_api" is no longer the unique crate to provide APIs. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-17-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- rust/Cargo.lock | 22 +- rust/Cargo.toml | 2 +- rust/bits/Cargo.toml | 2 +- rust/bits/meson.build | 2 +- rust/bits/src/lib.rs | 4 +- rust/chardev/Cargo.toml | 2 +- rust/chardev/meson.build | 4 +- rust/chardev/src/chardev.rs | 2 +- rust/common/src/opaque.rs | 4 +- rust/hw/char/pl011/Cargo.toml | 2 +- rust/hw/char/pl011/meson.build | 4 +- rust/hw/char/pl011/src/device.rs | 4 +- rust/hw/char/pl011/src/registers.rs | 2 +- rust/hw/core/Cargo.toml | 2 +- rust/hw/core/meson.build | 4 +- rust/hw/core/src/irq.rs | 2 +- rust/hw/core/src/qdev.rs | 6 +- rust/hw/core/src/sysbus.rs | 2 +- rust/hw/core/tests/tests.rs | 4 +- rust/hw/timer/hpet/Cargo.toml | 2 +- rust/hw/timer/hpet/meson.build | 4 +- rust/hw/timer/hpet/src/device.rs | 6 +- rust/meson.build | 2 +- rust/migration/Cargo.toml | 2 +- rust/qemu-api-macros/Cargo.toml | 24 --- rust/qemu-api-macros/meson.build | 22 -- rust/qemu-api-macros/src/bits.rs | 213 ------------------ rust/qemu-api-macros/src/lib.rs | 415 ------------------------------------ rust/qemu-api-macros/src/tests.rs | 244 --------------------- rust/qemu-api/Cargo.toml | 2 +- rust/qemu-api/meson.build | 4 +- rust/qemu-macros/Cargo.toml | 24 +++ rust/qemu-macros/meson.build | 22 ++ rust/qemu-macros/src/bits.rs | 213 ++++++++++++++++++ rust/qemu-macros/src/lib.rs | 415 ++++++++++++++++++++++++++++++++++++ rust/qemu-macros/src/tests.rs | 244 +++++++++++++++++++++ rust/qom/Cargo.toml | 2 +- rust/qom/meson.build | 4 +- rust/qom/src/qom.rs | 4 +- rust/system/Cargo.toml | 2 +- rust/system/meson.build | 4 +- rust/system/src/memory.rs | 2 +- rust/util/Cargo.toml | 2 +- rust/util/meson.build | 2 +- rust/util/src/timer.rs | 4 +- 46 files changed, 981 insertions(+), 981 deletions(-) delete mode 100644 rust/qemu-api-macros/Cargo.toml delete mode 100644 rust/qemu-api-macros/meson.build delete mode 100644 rust/qemu-api-macros/src/bits.rs delete mode 100644 rust/qemu-api-macros/src/lib.rs delete mode 100644 rust/qemu-api-macros/src/tests.rs create mode 100644 rust/qemu-macros/Cargo.toml create mode 100644 rust/qemu-macros/meson.build create mode 100644 rust/qemu-macros/src/bits.rs create mode 100644 rust/qemu-macros/src/lib.rs create mode 100644 rust/qemu-macros/src/tests.rs (limited to 'rust/common/src') diff --git a/MAINTAINERS b/MAINTAINERS index 92d575b535..23bda7d332 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3521,7 +3521,7 @@ F: rust/common/ F: rust/hw/core/ F: rust/migration/ F: rust/qemu-api -F: rust/qemu-api-macros +F: rust/qemu-macros/ F: rust/qom/ F: rust/rustfmt.toml F: rust/system/ diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 77118e882b..021ce6dd48 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ name = "bits" version = "0.1.0" dependencies = [ - "qemu_api_macros", + "qemu_macros", ] [[package]] @@ -58,7 +58,7 @@ dependencies = [ "bql", "common", "migration", - "qemu_api_macros", + "qemu_macros", "qom", "util", ] @@ -94,7 +94,7 @@ dependencies = [ "hwcore", "migration", "qemu_api", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", @@ -108,7 +108,7 @@ dependencies = [ "chardev", "common", "migration", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", @@ -134,7 +134,7 @@ name = "migration" version = "0.1.0" dependencies = [ "common", - "qemu_api_macros", + "qemu_macros", "util", ] @@ -151,7 +151,7 @@ dependencies = [ "hwcore", "migration", "qemu_api", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", @@ -198,14 +198,14 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_api_macros", + "qemu_macros", "qom", "system", "util", ] [[package]] -name = "qemu_api_macros" +name = "qemu_macros" version = "0.1.0" dependencies = [ "proc-macro2", @@ -220,7 +220,7 @@ dependencies = [ "bql", "common", "migration", - "qemu_api_macros", + "qemu_macros", "util", ] @@ -249,7 +249,7 @@ name = "system" version = "0.1.0" dependencies = [ "common", - "qemu_api_macros", + "qemu_macros", "qom", "util", ] @@ -268,7 +268,7 @@ dependencies = [ "common", "foreign", "libc", - "qemu_api_macros", + "qemu_macros", ] [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8ec07d2065..b2a5c230fa 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -5,7 +5,7 @@ members = [ "bql", "common", "migration", - "qemu-api-macros", + "qemu-macros", "qemu-api", "qom", "system", diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml index 1ff38a4117..7fce972b27 100644 --- a/rust/bits/Cargo.toml +++ b/rust/bits/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/bits/meson.build b/rust/bits/meson.build index 2a41e138c5..359ca86f15 100644 --- a/rust/bits/meson.build +++ b/rust/bits/meson.build @@ -3,7 +3,7 @@ _bits_rs = static_library( 'src/lib.rs', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - dependencies: [qemu_api_macros], + dependencies: [qemu_macros], ) bits_rs = declare_dependency(link_with: _bits_rs) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs index e9d15ad0cb..1bc882fde1 100644 --- a/rust/bits/src/lib.rs +++ b/rust/bits/src/lib.rs @@ -380,11 +380,11 @@ macro_rules! bits { }; { $type:ty: $expr:expr } => { - ::qemu_api_macros::bits_const_internal! { $type @ ($expr) } + ::qemu_macros::bits_const_internal! { $type @ ($expr) } }; { $type:ty as $int_type:ty: $expr:expr } => { - (::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type + (::qemu_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type }; } diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml index 7df9c677fc..c139177307 100644 --- a/rust/chardev/Cargo.toml +++ b/rust/chardev/Cargo.toml @@ -18,7 +18,7 @@ bql = { path = "../bql" } migration = { path = "../migration" } qom = { path = "../qom" } util = { path = "../util" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index 5d333e232b..a2fa3268d2 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -35,7 +35,7 @@ _chardev_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_api_macros], + dependencies: [common_rs, qemu_macros], ) -chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_api_macros, chardev, qemuutil]) +chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev, qemuutil]) diff --git a/rust/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs index 072d806e4a..cb6f99398e 100644 --- a/rust/chardev/src/chardev.rs +++ b/rust/chardev/src/chardev.rs @@ -26,7 +26,7 @@ use crate::bindings; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] +#[derive(qemu_macros::Wrapper)] pub struct Chardev(Opaque); pub type ChardevClass = bindings::ChardevClass; diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs index 97ed3e8452..3b3263acaa 100644 --- a/rust/common/src/opaque.rs +++ b/rust/common/src/opaque.rs @@ -192,7 +192,7 @@ impl Opaque { /// Annotates [`Self`] as a transparent wrapper for another type. /// -/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. +/// Usually defined via the [`qemu_macros::Wrapper`] derive macro. /// /// # Examples /// @@ -228,7 +228,7 @@ impl Opaque { /// /// They are not defined here to allow them to be `const`. /// -/// [`qemu_api_macros::Wrapper`]: ../../qemu_api_macros/derive.Wrapper.html +/// [`qemu_macros::Wrapper`]: ../../qemu_macros/derive.Wrapper.html pub unsafe trait Wrapper { type Wrapped; } diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 830d88586b..9e451fc0aa 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -25,7 +25,7 @@ chardev = { path = "../../../chardev" } system = { path = "../../../system" } hwcore = { path = "../../../hw/core" } qemu_api = { path = "../../../qemu-api" } -qemu_api_macros = { path = "../../../qemu-api-macros" } +qemu_macros = { path = "../../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index fac0432113..bad6a839c3 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -12,7 +12,7 @@ _libpl011_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_api_macros, + qemu_macros, qom_rs, chardev_rs, system_rs, @@ -24,6 +24,6 @@ rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( link_whole: [_libpl011_rs], # Putting proc macro crates in `dependencies` is necessary for Meson to find # them when compiling the root per-target static rust lib. - dependencies: [bilge_impl_rs, qemu_api_macros], + dependencies: [bilge_impl_rs, qemu_macros], variables: {'crate': 'pl011'}, )]) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index a6a17d9f2d..3010b6d983 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -97,7 +97,7 @@ pub struct PL011Registers { } #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: ParentField, @@ -683,7 +683,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField, diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index 2bfbd81095..a1c41347ed 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -16,7 +16,7 @@ use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] +#[derive(Debug, Eq, PartialEq, qemu_macros::TryInto)] pub enum RegisterOffset { /// Data Register /// diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml index 0b35380264..0eb9ffee26 100644 --- a/rust/hw/core/Cargo.toml +++ b/rust/hw/core/Cargo.toml @@ -20,7 +20,7 @@ chardev = { path = "../../chardev" } migration = { path = "../../migration" } system = { path = "../../system" } util = { path = "../../util" } -qemu_api_macros = { path = "../../qemu-api-macros" } +qemu_macros = { path = "../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 7dd1ade6f0..67eacf854f 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -58,7 +58,7 @@ _hwcore_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], - dependencies: [qemu_api_macros, common_rs], + dependencies: [qemu_macros, common_rs], ) hwcore_rs = declare_dependency(link_with: [_hwcore_rs], @@ -71,7 +71,7 @@ test('rust-hwcore-rs-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_api_macros, util_rs]), + dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_macros, util_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs index fead2bbe8e..d8d964cad2 100644 --- a/rust/hw/core/src/irq.rs +++ b/rust/hw/core/src/irq.rs @@ -18,7 +18,7 @@ use crate::bindings::{self, qemu_set_irq}; /// An opaque wrapper around [`bindings::IRQState`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct IRQState(Opaque); /// Interrupt sources are used by devices to pass changes to a value (typically diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 8e9702ce0b..c9faf44a71 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -23,7 +23,7 @@ use crate::{ /// A safe wrapper around [`bindings::Clock`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct Clock(Opaque); unsafe impl Send for Clock {} @@ -31,7 +31,7 @@ unsafe impl Sync for Clock {} /// A safe wrapper around [`bindings::DeviceState`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct DeviceState(Opaque); unsafe impl Send for DeviceState {} @@ -101,7 +101,7 @@ unsafe extern "C" fn rust_resettable_exit_fn( /// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type. /// -/// This trait is used by [`qemu_api_macros::Device`] derive macro. +/// This trait is used by [`qemu_macros::Device`] derive macro. /// /// Base types that already have `qdev_prop_*` globals in the QEMU API should /// use those values as exported by the [`bindings`] module, instead of diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs index dda71ebda7..92c7449b80 100644 --- a/rust/hw/core/src/sysbus.rs +++ b/rust/hw/core/src/sysbus.rs @@ -19,7 +19,7 @@ use crate::{ /// A safe wrapper around [`bindings::SysBusDevice`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct SysBusDevice(Opaque); unsafe impl Send for SysBusDevice {} diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs index 21ee301fa6..2f08b8f3bf 100644 --- a/rust/hw/core/tests/tests.rs +++ b/rust/hw/core/tests/tests.rs @@ -17,7 +17,7 @@ pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::< .build(); #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] pub struct DummyState { parent: ParentField, #[property(rename = "migrate-clk", default = true)] @@ -54,7 +54,7 @@ impl DeviceImpl for DummyState { } #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::Device)] +#[derive(qemu_macros::Object, qemu_macros::Device)] pub struct DummyChildState { parent: ParentField, } diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index e28d66f645..68e8187bb8 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -18,7 +18,7 @@ bql = { path = "../../../bql" } qom = { path = "../../../qom" } system = { path = "../../../system" } qemu_api = { path = "../../../qemu-api" } -qemu_api_macros = { path = "../../../qemu-api-macros" } +qemu_macros = { path = "../../../qemu-macros" } hwcore = { path = "../../../hw/core" } [lints] diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index e6f99b6778..3b94d5ec0a 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -9,7 +9,7 @@ _libhpet_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_api_macros, + qemu_macros, qom_rs, system_rs, hwcore_rs, @@ -20,6 +20,6 @@ rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( link_whole: [_libhpet_rs], # Putting proc macro crates in `dependencies` is necessary for Meson to find # them when compiling the root per-target static rust lib. - dependencies: [qemu_api_macros], + dependencies: [qemu_macros], variables: {'crate': 'hpet'}, )]) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 3031539744..07e0f639fc 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -97,7 +97,7 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; /// Timer N Interrupt Routing Capability (bits 32:63) const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; -#[derive(qemu_api_macros::TryInto)] +#[derive(qemu_macros::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Timer registers, masked by 0x18 @@ -110,7 +110,7 @@ enum TimerRegister { ROUTE = 16, } -#[derive(qemu_api_macros::TryInto)] +#[derive(qemu_macros::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Global registers @@ -520,7 +520,7 @@ impl HPETTimer { /// HPET Event Timer Block Abstraction #[repr(C)] -#[derive(qemu_api_macros::Object)] +#[derive(qemu_macros::Object)] pub struct HPETState { parent_obj: ParentField, iomem: MemoryRegion, diff --git a/rust/meson.build b/rust/meson.build index 041b0a473e..9f6a0b161d 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -23,7 +23,7 @@ qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) genrs = [] subdir('common') -subdir('qemu-api-macros') +subdir('qemu-macros') subdir('bits') subdir('util') subdir('migration') diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index 98e6df2109..66af81e0a3 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -15,7 +15,7 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } util = { path = "../util" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-api-macros/Cargo.toml deleted file mode 100644 index 0cd40c8e16..0000000000 --- a/rust/qemu-api-macros/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "qemu_api_macros" -version = "0.1.0" -authors = ["Manos Pitsidianakis "] -description = "Rust bindings for QEMU - Utility macros" -resolver = "2" -publish = false - -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1" -quote = "1" -syn = { version = "2", features = ["extra-traits"] } - -[lints] -workspace = true diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build deleted file mode 100644 index 2152bcb99b..0000000000 --- a/rust/qemu-api-macros/meson.build +++ /dev/null @@ -1,22 +0,0 @@ -_qemu_api_macros_rs = rust.proc_macro( - 'qemu_api_macros', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: [ - '--cfg', 'use_fallback', - '--cfg', 'feature="syn-error"', - '--cfg', 'feature="proc-macro"', - ], - dependencies: [ - proc_macro2_rs_native, - quote_rs_native, - syn_rs_native, - ], -) - -qemu_api_macros = declare_dependency( - link_with: _qemu_api_macros_rs, -) - -rust.test('rust-qemu-api-macros-tests', _qemu_api_macros_rs, - suite: ['unit', 'rust']) diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs deleted file mode 100644 index a80a3b9fee..0000000000 --- a/rust/qemu-api-macros/src/bits.rs +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later - -// shadowing is useful together with "if let" -#![allow(clippy::shadow_unrelated)] - -use proc_macro2::{ - Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, -}; -use syn::Error; - -pub struct BitsConstInternal { - typ: TokenTree, -} - -fn paren(ts: TokenStream) -> TokenTree { - TT::Group(Group::new(Delimiter::Parenthesis, ts)) -} - -fn ident(s: &'static str) -> TokenTree { - TT::Ident(Ident::new(s, Span::call_site())) -} - -fn punct(ch: char) -> TokenTree { - TT::Punct(Punct::new(ch, Spacing::Alone)) -} - -/// Implements a recursive-descent parser that translates Boolean expressions on -/// bitmasks to invocations of `const` functions defined by the `bits!` macro. -impl BitsConstInternal { - // primary ::= '(' or ')' - // | ident - // | '!' ident - fn parse_primary( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - let next = match tok { - TT::Group(ref g) => { - if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { - return Err(Error::new(g.span(), "expected parenthesis")); - } - let mut stream = g.stream().into_iter(); - let Some(first_tok) = stream.next() else { - return Err(Error::new(g.span(), "expected operand, found ')'")); - }; - let mut output = TokenStream::new(); - // start from the lowest precedence - let next = self.parse_or(first_tok, &mut stream, &mut output)?; - if let Some(tok) = next { - return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); - } - out.extend(Some(paren(output))); - it.next() - } - TT::Ident(_) => { - let mut output = TokenStream::new(); - output.extend([ - self.typ.clone(), - TT::Punct(Punct::new(':', Spacing::Joint)), - TT::Punct(Punct::new(':', Spacing::Joint)), - tok, - ]); - out.extend(Some(paren(output))); - it.next() - } - TT::Punct(ref p) => { - if p.as_char() != '!' { - return Err(Error::new(p.span(), "expected operand")); - } - let Some(rhs_tok) = it.next() else { - return Err(Error::new(p.span(), "expected operand at end of input")); - }; - let next = self.parse_primary(rhs_tok, it, out)?; - out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); - next - } - _ => { - return Err(Error::new(tok.span(), "unexpected literal")); - } - }; - Ok(next) - } - - fn parse_binop< - F: Fn( - &Self, - TokenTree, - &mut dyn Iterator, - &mut TokenStream, - ) -> Result, Error>, - >( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ch: char, - f: F, - method: &'static str, - ) -> Result, Error> { - let mut next = f(self, tok, it, out)?; - while next.is_some() { - let op = next.as_ref().unwrap(); - let TT::Punct(ref p) = op else { break }; - if p.as_char() != ch { - break; - } - - let Some(rhs_tok) = it.next() else { - return Err(Error::new(p.span(), "expected operand at end of input")); - }; - let mut rhs = TokenStream::new(); - next = f(self, rhs_tok, it, &mut rhs)?; - out.extend([punct('.'), ident(method), paren(rhs)]); - } - Ok(next) - } - - // sub ::= primary ('-' primary)* - pub fn parse_sub( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") - } - - // and ::= sub ('&' sub)* - fn parse_and( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") - } - - // xor ::= and ('&' and)* - fn parse_xor( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") - } - - // or ::= xor ('|' xor)* - pub fn parse_or( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") - } - - pub fn parse( - it: &mut dyn Iterator, - ) -> Result { - let mut pos = Span::call_site(); - let mut typ = proc_macro2::TokenStream::new(); - - // Gobble everything up to an `@` sign, which is followed by a - // parenthesized expression; that is, all token trees except the - // last two form the type. - let next = loop { - let tok = it.next(); - if let Some(ref t) = tok { - pos = t.span(); - } - match tok { - None => break None, - Some(TT::Punct(ref p)) if p.as_char() == '@' => { - let tok = it.next(); - if let Some(ref t) = tok { - pos = t.span(); - } - break tok; - } - Some(x) => typ.extend(Some(x)), - } - }; - - let Some(tok) = next else { - return Err(Error::new( - pos, - "expected expression, do not call this macro directly", - )); - }; - let TT::Group(ref _group) = tok else { - return Err(Error::new( - tok.span(), - "expected parenthesis, do not call this macro directly", - )); - }; - let mut out = TokenStream::new(); - let state = Self { - typ: TT::Group(Group::new(Delimiter::None, typ)), - }; - - let next = state.parse_primary(tok, it, &mut out)?; - - // A parenthesized expression is a single production of the grammar, - // so the input must have reached the last token. - if let Some(tok) = next { - return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); - } - Ok(out) - } -} diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs deleted file mode 100644 index 830b432698..0000000000 --- a/rust/qemu-api-macros/src/lib.rs +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use proc_macro::TokenStream; -use quote::{quote, quote_spanned, ToTokens}; -use syn::{ - parse::Parse, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, - token::Comma, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, - Variant, -}; -mod bits; -use bits::BitsConstInternal; - -#[cfg(test)] -mod tests; - -fn get_fields<'a>( - input: &'a DeriveInput, - msg: &str, -) -> Result<&'a Punctuated, Error> { - let Data::Struct(ref s) = &input.data else { - return Err(Error::new( - input.ident.span(), - format!("Struct required for {msg}"), - )); - }; - let Fields::Named(ref fs) = &s.fields else { - return Err(Error::new( - input.ident.span(), - format!("Named fields required for {msg}"), - )); - }; - Ok(&fs.named) -} - -fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, Error> { - let Data::Struct(ref s) = &input.data else { - return Err(Error::new( - input.ident.span(), - format!("Struct required for {msg}"), - )); - }; - let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else { - return Err(Error::new( - s.fields.span(), - format!("Tuple struct required for {msg}"), - )); - }; - if unnamed.len() != 1 { - return Err(Error::new( - s.fields.span(), - format!("A single field is required for {msg}"), - )); - } - Ok(&unnamed[0]) -} - -fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { - let expected = parse_quote! { #[repr(C)] }; - - if input.attrs.iter().any(|attr| attr == &expected) { - Ok(()) - } else { - Err(Error::new( - input.ident.span(), - format!("#[repr(C)] required for {msg}"), - )) - } -} - -fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { - let expected = parse_quote! { #[repr(transparent)] }; - - if input.attrs.iter().any(|attr| attr == &expected) { - Ok(()) - } else { - Err(Error::new( - input.ident.span(), - format!("#[repr(transparent)] required for {msg}"), - )) - } -} - -fn derive_object_or_error(input: DeriveInput) -> Result { - is_c_repr(&input, "#[derive(Object)]")?; - - let name = &input.ident; - let parent = &get_fields(&input, "#[derive(Object)]")? - .get(0) - .ok_or_else(|| { - Error::new( - input.ident.span(), - "#[derive(Object)] requires a parent field", - ) - })? - .ident; - - Ok(quote! { - ::common::assert_field_type!(#name, #parent, - ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>); - - ::util::module_init! { - MODULE_INIT_QOM => unsafe { - ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO); - } - } - }) -} - -#[proc_macro_derive(Object)] -pub fn derive_object(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_object_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -fn derive_opaque_or_error(input: DeriveInput) -> Result { - is_transparent_repr(&input, "#[derive(Wrapper)]")?; - - let name = &input.ident; - let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?; - let typ = &field.ty; - - Ok(quote! { - 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 { - let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); - unsafe { ptr.as_ref() } - } - - pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { - self.0.as_mut_ptr() - } - - pub const fn as_ptr(&self) -> *const ::Wrapped { - self.0.as_ptr() - } - - pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void { - self.0.as_void_ptr() - } - - pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { - slot.cast() - } - } - }) -} - -#[derive(Debug)] -enum DevicePropertyName { - CStr(syn::LitCStr), - Str(syn::LitStr), -} - -#[derive(Debug)] -struct DeviceProperty { - rename: Option, - defval: Option, -} - -impl Parse for DeviceProperty { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _: syn::Token![#] = input.parse()?; - let bracketed; - _ = syn::bracketed!(bracketed in input); - let attribute = bracketed.parse::()?; - debug_assert_eq!(&attribute.to_string(), "property"); - let mut retval = Self { - rename: None, - defval: None, - }; - let content; - _ = syn::parenthesized!(content in bracketed); - while !content.is_empty() { - let value: syn::Ident = content.parse()?; - if value == "rename" { - let _: syn::Token![=] = content.parse()?; - if retval.rename.is_some() { - return Err(syn::Error::new( - value.span(), - "`rename` can only be used at most once", - )); - } - if content.peek(syn::LitStr) { - retval.rename = Some(DevicePropertyName::Str(content.parse::()?)); - } else { - retval.rename = - Some(DevicePropertyName::CStr(content.parse::()?)); - } - } else if value == "default" { - let _: syn::Token![=] = content.parse()?; - if retval.defval.is_some() { - return Err(syn::Error::new( - value.span(), - "`default` can only be used at most once", - )); - } - retval.defval = Some(content.parse()?); - } else { - return Err(syn::Error::new( - value.span(), - format!("unrecognized field `{value}`"), - )); - } - - if !content.is_empty() { - let _: syn::Token![,] = content.parse()?; - } - } - Ok(retval) - } -} - -#[proc_macro_derive(Device, attributes(property))] -pub fn derive_device(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_device_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -fn derive_device_or_error(input: DeriveInput) -> Result { - is_c_repr(&input, "#[derive(Device)]")?; - let properties: Vec<(syn::Field, DeviceProperty)> = get_fields(&input, "#[derive(Device)]")? - .iter() - .flat_map(|f| { - f.attrs - .iter() - .filter(|a| a.path().is_ident("property")) - .map(|a| Ok((f.clone(), syn::parse2(a.to_token_stream())?))) - }) - .collect::, Error>>()?; - let name = &input.ident; - let mut properties_expanded = vec![]; - - for (field, prop) in properties { - let DeviceProperty { rename, defval } = prop; - let field_name = field.ident.unwrap(); - macro_rules! str_to_c_str { - ($value:expr, $span:expr) => {{ - let (value, span) = ($value, $span); - let cstr = std::ffi::CString::new(value.as_str()).map_err(|err| { - Error::new( - span, - format!( - "Property name `{value}` cannot be represented as a C string: {err}" - ), - ) - })?; - let cstr_lit = syn::LitCStr::new(&cstr, span); - Ok(quote! { #cstr_lit }) - }}; - } - - let prop_name = rename.map_or_else( - || str_to_c_str!(field_name.to_string(), field_name.span()), - |rename| -> Result { - match rename { - DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }), - DevicePropertyName::Str(str_lit) => { - str_to_c_str!(str_lit.value(), str_lit.span()) - } - } - }, - )?; - let field_ty = field.ty.clone(); - let qdev_prop = quote! { <#field_ty as ::hwcore::QDevProp>::VALUE }; - let set_default = defval.is_some(); - let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); - properties_expanded.push(quote! { - ::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: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, - ..::common::Zeroable::ZERO - } - }); - } - - Ok(quote_spanned! {input.span() => - unsafe impl ::hwcore::DevicePropertiesImpl for #name { - const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ - #(#properties_expanded),* - ]; - } - }) -} - -#[proc_macro_derive(Wrapper)] -pub fn derive_opaque(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_opaque_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -#[allow(non_snake_case)] -fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { - let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); - if let Some(repr) = repr { - let nested = repr.parse_args_with(Punctuated::::parse_terminated)?; - for meta in nested { - match meta { - Meta::Path(path) if path.is_ident("u8") => return Ok(path), - Meta::Path(path) if path.is_ident("u16") => return Ok(path), - Meta::Path(path) if path.is_ident("u32") => return Ok(path), - Meta::Path(path) if path.is_ident("u64") => return Ok(path), - _ => {} - } - } - } - - Err(Error::new( - input.ident.span(), - format!("#[repr(u8/u16/u32/u64) required for {msg}"), - )) -} - -fn get_variants(input: &DeriveInput) -> Result<&Punctuated, Error> { - let Data::Enum(ref e) = &input.data else { - return Err(Error::new( - input.ident.span(), - "Cannot derive TryInto for union or struct.", - )); - }; - if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { - return Err(Error::new( - v.fields.span(), - "Cannot derive TryInto for enum with non-unit variants.", - )); - } - Ok(&e.variants) -} - -#[rustfmt::skip::macros(quote)] -fn derive_tryinto_body( - name: &Ident, - variants: &Punctuated, - repr: &Path, -) -> Result { - let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); - - Ok(quote! { - #(const #discriminants: #repr = #name::#discriminants as #repr;)* - match value { - #(#discriminants => core::result::Result::Ok(#name::#discriminants),)* - _ => core::result::Result::Err(value), - } - }) -} - -#[rustfmt::skip::macros(quote)] -fn derive_tryinto_or_error(input: DeriveInput) -> Result { - let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; - let name = &input.ident; - let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?; - let errmsg = format!("invalid value for {name}"); - - Ok(quote! { - impl #name { - #[allow(dead_code)] - pub const fn into_bits(self) -> #repr { - self as #repr - } - - #[allow(dead_code)] - pub const fn from_bits(value: #repr) -> Self { - match ({ - #body - }) { - Ok(x) => x, - Err(_) => panic!(#errmsg), - } - } - } - impl core::convert::TryFrom<#repr> for #name { - type Error = #repr; - - #[allow(ambiguous_associated_items)] - fn try_from(value: #repr) -> Result { - #body - } - } - }) -} - -#[proc_macro_derive(TryInto)] -pub fn derive_tryinto(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_tryinto_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -#[proc_macro] -pub fn bits_const_internal(ts: TokenStream) -> TokenStream { - let ts = proc_macro2::TokenStream::from(ts); - let mut it = ts.into_iter(); - - BitsConstInternal::parse(&mut it) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs deleted file mode 100644 index 9ab7eab7f3..0000000000 --- a/rust/qemu-api-macros/src/tests.rs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2025, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use quote::quote; - -use super::*; - -macro_rules! derive_compile_fail { - ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{ - let input: proc_macro2::TokenStream = $input; - let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*]; - let derive_fn: fn(input: syn::DeriveInput) -> Result = - $derive_fn; - - let input: syn::DeriveInput = syn::parse2(input).unwrap(); - let result = derive_fn(input); - let err = result.unwrap_err().into_compile_error(); - assert_eq!( - err.to_string(), - quote! { #(#error_msg)* }.to_string() - ); - }}; -} - -macro_rules! derive_compile { - ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ - let input: proc_macro2::TokenStream = $input; - let expected: proc_macro2::TokenStream = $($expected)*; - let derive_fn: fn(input: syn::DeriveInput) -> Result = - $derive_fn; - - let input: syn::DeriveInput = syn::parse2(input).unwrap(); - let result = derive_fn(input).unwrap(); - assert_eq!(result.to_string(), expected.to_string()); - }}; -} - -#[test] -fn test_derive_device() { - // Check that repr(C) is used - derive_compile_fail!( - derive_device_or_error, - quote! { - #[derive(Device)] - struct Foo { - _unused: [u8; 0], - } - }, - "#[repr(C)] required for #[derive(Device)]" - ); - // Check that invalid/misspelled attributes raise an error - derive_compile_fail!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - struct DummyState { - #[property(defalt = true)] - migrate_clock: bool, - } - }, - "unrecognized field `defalt`" - ); - // Check that repeated attributes are not allowed: - derive_compile_fail!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - struct DummyState { - #[property(rename = "migrate-clk", rename = "migrate-clk", default = true)] - migrate_clock: bool, - } - }, - "`rename` can only be used at most once" - ); - derive_compile_fail!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - struct DummyState { - #[property(default = true, default = true)] - migrate_clock: bool, - } - }, - "`default` can only be used at most once" - ); - // Check that the field name is preserved when `rename` isn't used: - derive_compile!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - pub struct DummyState { - parent: ParentField, - #[property(default = true)] - migrate_clock: bool, - } - }, - quote! { - 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, - offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, - set_default: true, - defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, - ..::common::Zeroable::ZERO - } - ]; - } - } - ); - // Check that `rename` value is used for the property name when used: - derive_compile!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - pub struct DummyState { - parent: ParentField, - #[property(rename = "migrate-clk", default = true)] - migrate_clock: bool, - } - }, - quote! { - 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, - offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, - set_default: true, - defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, - ..::common::Zeroable::ZERO - } - ]; - } - } - ); -} - -#[test] -fn test_derive_object() { - derive_compile_fail!( - derive_object_or_error, - quote! { - #[derive(Object)] - struct Foo { - _unused: [u8; 0], - } - }, - "#[repr(C)] required for #[derive(Object)]" - ); - derive_compile!( - derive_object_or_error, - quote! { - #[derive(Object)] - #[repr(C)] - struct Foo { - _unused: [u8; 0], - } - }, - quote! { - ::common::assert_field_type!( - Foo, - _unused, - ::qom::ParentField<::ParentType> - ); - ::util::module_init! { - MODULE_INIT_QOM => unsafe { - ::qom::type_register_static(&::TYPE_INFO); - } - } - } - ); -} - -#[test] -fn test_derive_tryinto() { - derive_compile_fail!( - derive_tryinto_or_error, - quote! { - #[derive(TryInto)] - struct Foo { - _unused: [u8; 0], - } - }, - "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" - ); - derive_compile!( - derive_tryinto_or_error, - quote! { - #[derive(TryInto)] - #[repr(u8)] - enum Foo { - First = 0, - Second, - } - }, - quote! { - impl Foo { - #[allow(dead_code)] - pub const fn into_bits(self) -> u8 { - self as u8 - } - - #[allow(dead_code)] - pub const fn from_bits(value: u8) -> Self { - match ({ - const First: u8 = Foo::First as u8; - const Second: u8 = Foo::Second as u8; - match value { - First => core::result::Result::Ok(Foo::First), - Second => core::result::Result::Ok(Foo::Second), - _ => core::result::Result::Err(value), - } - }) { - Ok(x) => x, - Err(_) => panic!("invalid value for Foo"), - } - } - } - - impl core::convert::TryFrom for Foo { - type Error = u8; - - #[allow(ambiguous_associated_items)] - fn try_from(value: u8) -> Result { - const First: u8 = Foo::First as u8; - const Second: u8 = Foo::Second as u8; - match value { - First => core::result::Result::Ok(Foo::First), - Second => core::result::Result::Ok(Foo::Second), - _ => core::result::Result::Err(value), - } - } - } - } - ); -} diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 9e7afc7e3a..9abb88aa1f 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -20,9 +20,9 @@ hwcore = { path = "../hw/core" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } +qemu_macros = { path = "../qemu-macros" } qom = { path = "../qom" } system = { path = "../system" } -qemu_api_macros = { path = "../qemu-api-macros" } [lints] workspace = true diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 2dc638782c..fe81f16d99 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -52,12 +52,12 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_api_macros, + dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_macros, qom_rs, system_rs, util_rs, hwcore], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], - dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) + dependencies: [qemu_macros, qom, hwcore, chardev, migration]) test('rust-qemu-api-integration', executable( diff --git a/rust/qemu-macros/Cargo.toml b/rust/qemu-macros/Cargo.toml new file mode 100644 index 0000000000..3b6f1d337f --- /dev/null +++ b/rust/qemu-macros/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "qemu_macros" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +description = "Rust bindings for QEMU - Utility macros" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { version = "2", features = ["extra-traits"] } + +[lints] +workspace = true diff --git a/rust/qemu-macros/meson.build b/rust/qemu-macros/meson.build new file mode 100644 index 0000000000..d0b2992e20 --- /dev/null +++ b/rust/qemu-macros/meson.build @@ -0,0 +1,22 @@ +_qemu_macros_rs = rust.proc_macro( + 'qemu_macros', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: [ + '--cfg', 'use_fallback', + '--cfg', 'feature="syn-error"', + '--cfg', 'feature="proc-macro"', + ], + dependencies: [ + proc_macro2_rs_native, + quote_rs_native, + syn_rs_native, + ], +) + +qemu_macros = declare_dependency( + link_with: _qemu_macros_rs, +) + +rust.test('rust-qemu-macros-tests', _qemu_macros_rs, + suite: ['unit', 'rust']) diff --git a/rust/qemu-macros/src/bits.rs b/rust/qemu-macros/src/bits.rs new file mode 100644 index 0000000000..a80a3b9fee --- /dev/null +++ b/rust/qemu-macros/src/bits.rs @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later + +// shadowing is useful together with "if let" +#![allow(clippy::shadow_unrelated)] + +use proc_macro2::{ + Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, +}; +use syn::Error; + +pub struct BitsConstInternal { + typ: TokenTree, +} + +fn paren(ts: TokenStream) -> TokenTree { + TT::Group(Group::new(Delimiter::Parenthesis, ts)) +} + +fn ident(s: &'static str) -> TokenTree { + TT::Ident(Ident::new(s, Span::call_site())) +} + +fn punct(ch: char) -> TokenTree { + TT::Punct(Punct::new(ch, Spacing::Alone)) +} + +/// Implements a recursive-descent parser that translates Boolean expressions on +/// bitmasks to invocations of `const` functions defined by the `bits!` macro. +impl BitsConstInternal { + // primary ::= '(' or ')' + // | ident + // | '!' ident + fn parse_primary( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, Error> { + let next = match tok { + TT::Group(ref g) => { + if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { + return Err(Error::new(g.span(), "expected parenthesis")); + } + let mut stream = g.stream().into_iter(); + let Some(first_tok) = stream.next() else { + return Err(Error::new(g.span(), "expected operand, found ')'")); + }; + let mut output = TokenStream::new(); + // start from the lowest precedence + let next = self.parse_or(first_tok, &mut stream, &mut output)?; + if let Some(tok) = next { + return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); + } + out.extend(Some(paren(output))); + it.next() + } + TT::Ident(_) => { + let mut output = TokenStream::new(); + output.extend([ + self.typ.clone(), + TT::Punct(Punct::new(':', Spacing::Joint)), + TT::Punct(Punct::new(':', Spacing::Joint)), + tok, + ]); + out.extend(Some(paren(output))); + it.next() + } + TT::Punct(ref p) => { + if p.as_char() != '!' { + return Err(Error::new(p.span(), "expected operand")); + } + let Some(rhs_tok) = it.next() else { + return Err(Error::new(p.span(), "expected operand at end of input")); + }; + let next = self.parse_primary(rhs_tok, it, out)?; + out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); + next + } + _ => { + return Err(Error::new(tok.span(), "unexpected literal")); + } + }; + Ok(next) + } + + fn parse_binop< + F: Fn( + &Self, + TokenTree, + &mut dyn Iterator, + &mut TokenStream, + ) -> Result, Error>, + >( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ch: char, + f: F, + method: &'static str, + ) -> Result, Error> { + let mut next = f(self, tok, it, out)?; + while next.is_some() { + let op = next.as_ref().unwrap(); + let TT::Punct(ref p) = op else { break }; + if p.as_char() != ch { + break; + } + + let Some(rhs_tok) = it.next() else { + return Err(Error::new(p.span(), "expected operand at end of input")); + }; + let mut rhs = TokenStream::new(); + next = f(self, rhs_tok, it, &mut rhs)?; + out.extend([punct('.'), ident(method), paren(rhs)]); + } + Ok(next) + } + + // sub ::= primary ('-' primary)* + pub fn parse_sub( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, Error> { + self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") + } + + // and ::= sub ('&' sub)* + fn parse_and( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, Error> { + self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") + } + + // xor ::= and ('&' and)* + fn parse_xor( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, Error> { + self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") + } + + // or ::= xor ('|' xor)* + pub fn parse_or( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, Error> { + self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") + } + + pub fn parse( + it: &mut dyn Iterator, + ) -> Result { + let mut pos = Span::call_site(); + let mut typ = proc_macro2::TokenStream::new(); + + // Gobble everything up to an `@` sign, which is followed by a + // parenthesized expression; that is, all token trees except the + // last two form the type. + let next = loop { + let tok = it.next(); + if let Some(ref t) = tok { + pos = t.span(); + } + match tok { + None => break None, + Some(TT::Punct(ref p)) if p.as_char() == '@' => { + let tok = it.next(); + if let Some(ref t) = tok { + pos = t.span(); + } + break tok; + } + Some(x) => typ.extend(Some(x)), + } + }; + + let Some(tok) = next else { + return Err(Error::new( + pos, + "expected expression, do not call this macro directly", + )); + }; + let TT::Group(ref _group) = tok else { + return Err(Error::new( + tok.span(), + "expected parenthesis, do not call this macro directly", + )); + }; + let mut out = TokenStream::new(); + let state = Self { + typ: TT::Group(Group::new(Delimiter::None, typ)), + }; + + let next = state.parse_primary(tok, it, &mut out)?; + + // A parenthesized expression is a single production of the grammar, + // so the input must have reached the last token. + if let Some(tok) = next { + return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); + } + Ok(out) + } +} diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs new file mode 100644 index 0000000000..830b432698 --- /dev/null +++ b/rust/qemu-macros/src/lib.rs @@ -0,0 +1,415 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro::TokenStream; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{ + parse::Parse, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, + token::Comma, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, + Variant, +}; +mod bits; +use bits::BitsConstInternal; + +#[cfg(test)] +mod tests; + +fn get_fields<'a>( + input: &'a DeriveInput, + msg: &str, +) -> Result<&'a Punctuated, Error> { + let Data::Struct(ref s) = &input.data else { + return Err(Error::new( + input.ident.span(), + format!("Struct required for {msg}"), + )); + }; + let Fields::Named(ref fs) = &s.fields else { + return Err(Error::new( + input.ident.span(), + format!("Named fields required for {msg}"), + )); + }; + Ok(&fs.named) +} + +fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, Error> { + let Data::Struct(ref s) = &input.data else { + return Err(Error::new( + input.ident.span(), + format!("Struct required for {msg}"), + )); + }; + let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else { + return Err(Error::new( + s.fields.span(), + format!("Tuple struct required for {msg}"), + )); + }; + if unnamed.len() != 1 { + return Err(Error::new( + s.fields.span(), + format!("A single field is required for {msg}"), + )); + } + Ok(&unnamed[0]) +} + +fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { + let expected = parse_quote! { #[repr(C)] }; + + if input.attrs.iter().any(|attr| attr == &expected) { + Ok(()) + } else { + Err(Error::new( + input.ident.span(), + format!("#[repr(C)] required for {msg}"), + )) + } +} + +fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { + let expected = parse_quote! { #[repr(transparent)] }; + + if input.attrs.iter().any(|attr| attr == &expected) { + Ok(()) + } else { + Err(Error::new( + input.ident.span(), + format!("#[repr(transparent)] required for {msg}"), + )) + } +} + +fn derive_object_or_error(input: DeriveInput) -> Result { + is_c_repr(&input, "#[derive(Object)]")?; + + let name = &input.ident; + let parent = &get_fields(&input, "#[derive(Object)]")? + .get(0) + .ok_or_else(|| { + Error::new( + input.ident.span(), + "#[derive(Object)] requires a parent field", + ) + })? + .ident; + + Ok(quote! { + ::common::assert_field_type!(#name, #parent, + ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>); + + ::util::module_init! { + MODULE_INIT_QOM => unsafe { + ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO); + } + } + }) +} + +#[proc_macro_derive(Object)] +pub fn derive_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + derive_object_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +fn derive_opaque_or_error(input: DeriveInput) -> Result { + is_transparent_repr(&input, "#[derive(Wrapper)]")?; + + let name = &input.ident; + let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?; + let typ = &field.ty; + + Ok(quote! { + 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 { + let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); + unsafe { ptr.as_ref() } + } + + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + self.0.as_mut_ptr() + } + + pub const fn as_ptr(&self) -> *const ::Wrapped { + self.0.as_ptr() + } + + pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void { + self.0.as_void_ptr() + } + + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { + slot.cast() + } + } + }) +} + +#[derive(Debug)] +enum DevicePropertyName { + CStr(syn::LitCStr), + Str(syn::LitStr), +} + +#[derive(Debug)] +struct DeviceProperty { + rename: Option, + defval: Option, +} + +impl Parse for DeviceProperty { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let _: syn::Token![#] = input.parse()?; + let bracketed; + _ = syn::bracketed!(bracketed in input); + let attribute = bracketed.parse::()?; + debug_assert_eq!(&attribute.to_string(), "property"); + let mut retval = Self { + rename: None, + defval: None, + }; + let content; + _ = syn::parenthesized!(content in bracketed); + while !content.is_empty() { + let value: syn::Ident = content.parse()?; + if value == "rename" { + let _: syn::Token![=] = content.parse()?; + if retval.rename.is_some() { + return Err(syn::Error::new( + value.span(), + "`rename` can only be used at most once", + )); + } + if content.peek(syn::LitStr) { + retval.rename = Some(DevicePropertyName::Str(content.parse::()?)); + } else { + retval.rename = + Some(DevicePropertyName::CStr(content.parse::()?)); + } + } else if value == "default" { + let _: syn::Token![=] = content.parse()?; + if retval.defval.is_some() { + return Err(syn::Error::new( + value.span(), + "`default` can only be used at most once", + )); + } + retval.defval = Some(content.parse()?); + } else { + return Err(syn::Error::new( + value.span(), + format!("unrecognized field `{value}`"), + )); + } + + if !content.is_empty() { + let _: syn::Token![,] = content.parse()?; + } + } + Ok(retval) + } +} + +#[proc_macro_derive(Device, attributes(property))] +pub fn derive_device(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + derive_device_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +fn derive_device_or_error(input: DeriveInput) -> Result { + is_c_repr(&input, "#[derive(Device)]")?; + let properties: Vec<(syn::Field, DeviceProperty)> = get_fields(&input, "#[derive(Device)]")? + .iter() + .flat_map(|f| { + f.attrs + .iter() + .filter(|a| a.path().is_ident("property")) + .map(|a| Ok((f.clone(), syn::parse2(a.to_token_stream())?))) + }) + .collect::, Error>>()?; + let name = &input.ident; + let mut properties_expanded = vec![]; + + for (field, prop) in properties { + let DeviceProperty { rename, defval } = prop; + let field_name = field.ident.unwrap(); + macro_rules! str_to_c_str { + ($value:expr, $span:expr) => {{ + let (value, span) = ($value, $span); + let cstr = std::ffi::CString::new(value.as_str()).map_err(|err| { + Error::new( + span, + format!( + "Property name `{value}` cannot be represented as a C string: {err}" + ), + ) + })?; + let cstr_lit = syn::LitCStr::new(&cstr, span); + Ok(quote! { #cstr_lit }) + }}; + } + + let prop_name = rename.map_or_else( + || str_to_c_str!(field_name.to_string(), field_name.span()), + |rename| -> Result { + match rename { + DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }), + DevicePropertyName::Str(str_lit) => { + str_to_c_str!(str_lit.value(), str_lit.span()) + } + } + }, + )?; + let field_ty = field.ty.clone(); + let qdev_prop = quote! { <#field_ty as ::hwcore::QDevProp>::VALUE }; + let set_default = defval.is_some(); + let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); + properties_expanded.push(quote! { + ::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: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, + ..::common::Zeroable::ZERO + } + }); + } + + Ok(quote_spanned! {input.span() => + unsafe impl ::hwcore::DevicePropertiesImpl for #name { + const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ + #(#properties_expanded),* + ]; + } + }) +} + +#[proc_macro_derive(Wrapper)] +pub fn derive_opaque(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + derive_opaque_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +#[allow(non_snake_case)] +fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { + let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); + if let Some(repr) = repr { + let nested = repr.parse_args_with(Punctuated::::parse_terminated)?; + for meta in nested { + match meta { + Meta::Path(path) if path.is_ident("u8") => return Ok(path), + Meta::Path(path) if path.is_ident("u16") => return Ok(path), + Meta::Path(path) if path.is_ident("u32") => return Ok(path), + Meta::Path(path) if path.is_ident("u64") => return Ok(path), + _ => {} + } + } + } + + Err(Error::new( + input.ident.span(), + format!("#[repr(u8/u16/u32/u64) required for {msg}"), + )) +} + +fn get_variants(input: &DeriveInput) -> Result<&Punctuated, Error> { + let Data::Enum(ref e) = &input.data else { + return Err(Error::new( + input.ident.span(), + "Cannot derive TryInto for union or struct.", + )); + }; + if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { + return Err(Error::new( + v.fields.span(), + "Cannot derive TryInto for enum with non-unit variants.", + )); + } + Ok(&e.variants) +} + +#[rustfmt::skip::macros(quote)] +fn derive_tryinto_body( + name: &Ident, + variants: &Punctuated, + repr: &Path, +) -> Result { + let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); + + Ok(quote! { + #(const #discriminants: #repr = #name::#discriminants as #repr;)* + match value { + #(#discriminants => core::result::Result::Ok(#name::#discriminants),)* + _ => core::result::Result::Err(value), + } + }) +} + +#[rustfmt::skip::macros(quote)] +fn derive_tryinto_or_error(input: DeriveInput) -> Result { + let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; + let name = &input.ident; + let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?; + let errmsg = format!("invalid value for {name}"); + + Ok(quote! { + impl #name { + #[allow(dead_code)] + pub const fn into_bits(self) -> #repr { + self as #repr + } + + #[allow(dead_code)] + pub const fn from_bits(value: #repr) -> Self { + match ({ + #body + }) { + Ok(x) => x, + Err(_) => panic!(#errmsg), + } + } + } + impl core::convert::TryFrom<#repr> for #name { + type Error = #repr; + + #[allow(ambiguous_associated_items)] + fn try_from(value: #repr) -> Result { + #body + } + } + }) +} + +#[proc_macro_derive(TryInto)] +pub fn derive_tryinto(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + derive_tryinto_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +#[proc_macro] +pub fn bits_const_internal(ts: TokenStream) -> TokenStream { + let ts = proc_macro2::TokenStream::from(ts); + let mut it = ts.into_iter(); + + BitsConstInternal::parse(&mut it) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs new file mode 100644 index 0000000000..9ab7eab7f3 --- /dev/null +++ b/rust/qemu-macros/src/tests.rs @@ -0,0 +1,244 @@ +// Copyright 2025, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use quote::quote; + +use super::*; + +macro_rules! derive_compile_fail { + ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{ + let input: proc_macro2::TokenStream = $input; + let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*]; + let derive_fn: fn(input: syn::DeriveInput) -> Result = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input); + let err = result.unwrap_err().into_compile_error(); + assert_eq!( + err.to_string(), + quote! { #(#error_msg)* }.to_string() + ); + }}; +} + +macro_rules! derive_compile { + ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ + let input: proc_macro2::TokenStream = $input; + let expected: proc_macro2::TokenStream = $($expected)*; + let derive_fn: fn(input: syn::DeriveInput) -> Result = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input).unwrap(); + assert_eq!(result.to_string(), expected.to_string()); + }}; +} + +#[test] +fn test_derive_device() { + // Check that repr(C) is used + derive_compile_fail!( + derive_device_or_error, + quote! { + #[derive(Device)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(C)] required for #[derive(Device)]" + ); + // Check that invalid/misspelled attributes raise an error + derive_compile_fail!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + struct DummyState { + #[property(defalt = true)] + migrate_clock: bool, + } + }, + "unrecognized field `defalt`" + ); + // Check that repeated attributes are not allowed: + derive_compile_fail!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + struct DummyState { + #[property(rename = "migrate-clk", rename = "migrate-clk", default = true)] + migrate_clock: bool, + } + }, + "`rename` can only be used at most once" + ); + derive_compile_fail!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + struct DummyState { + #[property(default = true, default = true)] + migrate_clock: bool, + } + }, + "`default` can only be used at most once" + ); + // Check that the field name is preserved when `rename` isn't used: + derive_compile!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + pub struct DummyState { + parent: ParentField, + #[property(default = true)] + migrate_clock: bool, + } + }, + quote! { + 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, + offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + set_default: true, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, + ..::common::Zeroable::ZERO + } + ]; + } + } + ); + // Check that `rename` value is used for the property name when used: + derive_compile!( + derive_device_or_error, + quote! { + #[repr(C)] + #[derive(Device)] + pub struct DummyState { + parent: ParentField, + #[property(rename = "migrate-clk", default = true)] + migrate_clock: bool, + } + }, + quote! { + 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, + offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, + set_default: true, + defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, + ..::common::Zeroable::ZERO + } + ]; + } + } + ); +} + +#[test] +fn test_derive_object() { + derive_compile_fail!( + derive_object_or_error, + quote! { + #[derive(Object)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(C)] required for #[derive(Object)]" + ); + derive_compile!( + derive_object_or_error, + quote! { + #[derive(Object)] + #[repr(C)] + struct Foo { + _unused: [u8; 0], + } + }, + quote! { + ::common::assert_field_type!( + Foo, + _unused, + ::qom::ParentField<::ParentType> + ); + ::util::module_init! { + MODULE_INIT_QOM => unsafe { + ::qom::type_register_static(&::TYPE_INFO); + } + } + } + ); +} + +#[test] +fn test_derive_tryinto() { + derive_compile_fail!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" + ); + derive_compile!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + #[repr(u8)] + enum Foo { + First = 0, + Second, + } + }, + quote! { + impl Foo { + #[allow(dead_code)] + pub const fn into_bits(self) -> u8 { + self as u8 + } + + #[allow(dead_code)] + pub const fn from_bits(value: u8) -> Self { + match ({ + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + }) { + Ok(x) => x, + Err(_) => panic!("invalid value for Foo"), + } + } + } + + impl core::convert::TryFrom for Foo { + type Error = u8; + + #[allow(ambiguous_associated_items)] + fn try_from(value: u8) -> Result { + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + } + } + } + ); +} diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml index 46bbf7c7fe..060ad2ec34 100644 --- a/rust/qom/Cargo.toml +++ b/rust/qom/Cargo.toml @@ -16,7 +16,7 @@ rust-version.workspace = true common = { path = "../common" } bql = { path = "../bql" } migration = { path = "../migration" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } util = { path = "../util" } [lints] diff --git a/rust/qom/meson.build b/rust/qom/meson.build index 84a65cb737..40c51b71b2 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -28,10 +28,10 @@ _qom_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs], - dependencies: [common_rs, qemu_api_macros], + dependencies: [common_rs, qemu_macros], ) -qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_api_macros, qom]) +qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom]) # Doctests are essentially integration tests, so they need the same dependencies. # Note that running them requires the object files for C code, so place them diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs index 3ea1ad9c5b..2cd1d85011 100644 --- a/rust/qom/src/qom.rs +++ b/rust/qom/src/qom.rs @@ -112,7 +112,7 @@ pub use crate::bindings::{type_register_static, ObjectClass}; /// A safe wrapper around [`bindings::Object`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct Object(Opaque); unsafe impl Send for Object {} @@ -173,7 +173,7 @@ macro_rules! qom_isa { /// /// ```ignore /// #[repr(C)] -/// #[derive(qemu_api_macros::Object)] +/// #[derive(qemu_macros::Object)] /// pub struct MyDevice { /// parent: ParentField, /// ... diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml index 6803895e08..d8338c8348 100644 --- a/rust/system/Cargo.toml +++ b/rust/system/Cargo.toml @@ -16,7 +16,7 @@ rust-version.workspace = true common = { path = "../common" } qom = { path = "../qom" } util = { path = "../util" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/system/meson.build b/rust/system/meson.build index ae9b932d29..9f88166f3d 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -35,8 +35,8 @@ _system_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_api_macros], + dependencies: [common_rs, qemu_macros], ) system_rs = declare_dependency(link_with: [_system_rs], - dependencies: [qemu_api_macros, hwcore]) + dependencies: [qemu_macros, hwcore]) diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs index 29568ed767..7312f809f5 100644 --- a/rust/system/src/memory.rs +++ b/rust/system/src/memory.rs @@ -129,7 +129,7 @@ impl Default for MemoryRegionOpsBuilder { /// A safe wrapper around [`bindings::MemoryRegion`]. #[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] +#[derive(qemu_macros::Wrapper)] pub struct MemoryRegion(Opaque); unsafe impl Send for MemoryRegion {} diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 637df61060..18e6619ca0 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -17,7 +17,7 @@ anyhow = { workspace = true } foreign = { workspace = true } libc = { workspace = true } common = { path = "../common" } -qemu_api_macros = { path = "../qemu-api-macros" } +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/util/meson.build b/rust/util/meson.build index 56e929349b..197872c9b2 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,7 +39,7 @@ _util_rs = static_library( ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_api_macros, qom, qemuutil], + dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_macros, qom, qemuutil], ) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs index 383e1a6e77..622b6ee309 100644 --- a/rust/util/src/timer.rs +++ b/rust/util/src/timer.rs @@ -15,14 +15,14 @@ use crate::bindings::{ /// A safe wrapper around [`bindings::QEMUTimer`]. #[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] +#[derive(Debug, qemu_macros::Wrapper)] pub struct Timer(Opaque); unsafe impl Send for Timer {} unsafe impl Sync for Timer {} #[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] +#[derive(qemu_macros::Wrapper)] pub struct TimerListGroup(Opaque); unsafe impl Send for TimerListGroup {} -- cgit 1.4.1 From e4444d71e85b5f5ea8311eb59fea3e52f5fc5a14 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:50:02 +0200 Subject: rust: re-export qemu macros from common/qom/hwcore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just a bit nicer. Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-22-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 2 +- rust/Cargo.lock | 8 +------- rust/chardev/Cargo.toml | 1 - rust/chardev/meson.build | 2 +- rust/chardev/src/chardev.rs | 2 +- rust/common/Cargo.toml | 1 + rust/common/meson.build | 2 +- rust/common/src/lib.rs | 2 ++ rust/common/src/opaque.rs | 4 +--- rust/hw/char/pl011/Cargo.toml | 1 - rust/hw/char/pl011/meson.build | 1 - rust/hw/char/pl011/src/device.rs | 4 ++-- rust/hw/char/pl011/src/registers.rs | 2 +- rust/hw/core/Cargo.toml | 2 +- rust/hw/core/meson.build | 2 +- rust/hw/core/src/irq.rs | 2 +- rust/hw/core/src/lib.rs | 1 + rust/hw/core/src/qdev.rs | 4 ++-- rust/hw/core/src/sysbus.rs | 2 +- rust/hw/core/tests/tests.rs | 4 ++-- rust/hw/timer/hpet/Cargo.toml | 1 - rust/hw/timer/hpet/meson.build | 1 - rust/hw/timer/hpet/src/device.rs | 6 +++--- rust/meson.build | 3 ++- rust/migration/Cargo.toml | 1 - rust/qom/src/lib.rs | 2 ++ rust/qom/src/qom.rs | 4 ++-- rust/system/Cargo.toml | 1 - rust/system/meson.build | 2 +- rust/system/src/memory.rs | 2 +- rust/tests/Cargo.toml | 1 - rust/util/Cargo.toml | 1 - rust/util/meson.build | 2 +- rust/util/src/timer.rs | 4 ++-- 34 files changed, 35 insertions(+), 45 deletions(-) (limited to 'rust/common/src') diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 20d15347de..29eb48af35 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -278,7 +278,7 @@ a raw pointer, for use in calls to C functions. It can be used for example as follows:: #[repr(transparent)] - #[derive(Debug, qemu_api_macros::Wrapper)] + #[derive(Debug, common::Wrapper)] pub struct Object(Opaque); where the special ``derive`` macro provides useful methods such as diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ac79c6a34a..eea928621a 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -58,7 +58,6 @@ dependencies = [ "bql", "common", "migration", - "qemu_macros", "qom", "util", ] @@ -68,6 +67,7 @@ name = "common" version = "0.1.0" dependencies = [ "libc", + "qemu_macros", ] [[package]] @@ -93,7 +93,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_macros", "qom", "system", "util", @@ -133,7 +132,6 @@ name = "migration" version = "0.1.0" dependencies = [ "common", - "qemu_macros", "util", ] @@ -149,7 +147,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_macros", "qom", "system", "util", @@ -232,7 +229,6 @@ name = "system" version = "0.1.0" dependencies = [ "common", - "qemu_macros", "qom", "util", ] @@ -246,7 +242,6 @@ dependencies = [ "common", "hwcore", "migration", - "qemu_macros", "qom", "system", "util", @@ -266,7 +261,6 @@ dependencies = [ "common", "foreign", "libc", - "qemu_macros", ] [[package]] diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml index c139177307..3e77972546 100644 --- a/rust/chardev/Cargo.toml +++ b/rust/chardev/Cargo.toml @@ -18,7 +18,6 @@ bql = { path = "../bql" } migration = { path = "../migration" } qom = { path = "../qom" } util = { path = "../util" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index a2fa3268d2..370895c111 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -38,4 +38,4 @@ _chardev_rs = static_library( dependencies: [common_rs, qemu_macros], ) -chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [qemu_macros, chardev, qemuutil]) +chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil]) diff --git a/rust/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs index cb6f99398e..2014479674 100644 --- a/rust/chardev/src/chardev.rs +++ b/rust/chardev/src/chardev.rs @@ -26,7 +26,7 @@ use crate::bindings; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] -#[derive(qemu_macros::Wrapper)] +#[derive(common::Wrapper)] pub struct Chardev(Opaque); pub type ChardevClass = bindings::ChardevClass; diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index 5e106427e8..0e1b4fc505 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [dependencies] libc.workspace = true +qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/common/meson.build b/rust/common/meson.build index 230a967760..b805e0faf5 100644 --- a/rust/common/meson.build +++ b/rust/common/meson.build @@ -19,7 +19,7 @@ _common_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _common_cfg, - dependencies: [libc_rs], + dependencies: [libc_rs, qemu_macros], ) common_rs = declare_dependency(link_with: [_common_rs]) diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index 25216503aa..8311bf945d 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +pub use qemu_macros::{TryInto, Wrapper}; + pub mod assertions; pub mod bitops; diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs index 3b3263acaa..c941fb4546 100644 --- a/rust/common/src/opaque.rs +++ b/rust/common/src/opaque.rs @@ -192,7 +192,7 @@ impl Opaque { /// Annotates [`Self`] as a transparent wrapper for another type. /// -/// Usually defined via the [`qemu_macros::Wrapper`] derive macro. +/// Usually defined via the [`crate::Wrapper`] derive macro. /// /// # Examples /// @@ -227,8 +227,6 @@ impl Opaque { /// ``` /// /// They are not defined here to allow them to be `const`. -/// -/// [`qemu_macros::Wrapper`]: ../../qemu_macros/derive.Wrapper.html pub unsafe trait Wrapper { type Wrapped; } diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 285d25c217..b2418abc4b 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -24,7 +24,6 @@ qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } system = { path = "../../../system" } hwcore = { path = "../../../hw/core" } -qemu_macros = { path = "../../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index a14993f692..628a523870 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -35,7 +35,6 @@ _libpl011_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_macros, qom_rs, chardev_rs, system_rs, diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 85626a969d..1b4587d5f6 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -97,7 +97,7 @@ pub struct PL011Registers { } #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: ParentField, @@ -683,7 +683,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField, diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index a1c41347ed..0c3a4d7d21 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -16,7 +16,7 @@ use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, Eq, PartialEq, qemu_macros::TryInto)] +#[derive(Debug, Eq, PartialEq, common::TryInto)] pub enum RegisterOffset { /// Data Register /// diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml index 0eb9ffee26..9a9aa51708 100644 --- a/rust/hw/core/Cargo.toml +++ b/rust/hw/core/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +qemu_macros = { path = "../../qemu-macros" } common = { path = "../../common" } bql = { path = "../../bql" } qom = { path = "../../qom" } @@ -20,7 +21,6 @@ chardev = { path = "../../chardev" } migration = { path = "../../migration" } system = { path = "../../system" } util = { path = "../../util" } -qemu_macros = { path = "../../qemu-macros" } [lints] workspace = true diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 67eacf854f..81d8c77f9a 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -71,7 +71,7 @@ test('rust-hwcore-rs-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_macros, util_rs]), + dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs index d8d964cad2..e0d7784d97 100644 --- a/rust/hw/core/src/irq.rs +++ b/rust/hw/core/src/irq.rs @@ -18,7 +18,7 @@ use crate::bindings::{self, qemu_set_irq}; /// An opaque wrapper around [`bindings::IRQState`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct IRQState(Opaque); /// Interrupt sources are used by devices to pass changes to a value (typically diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs index c5588d9bc2..b40801eb84 100644 --- a/rust/hw/core/src/lib.rs +++ b/rust/hw/core/src/lib.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later +pub use qemu_macros::Device; pub use qom; pub mod bindings; diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index c9faf44a71..71b9ef141c 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -23,7 +23,7 @@ use crate::{ /// A safe wrapper around [`bindings::Clock`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct Clock(Opaque); unsafe impl Send for Clock {} @@ -31,7 +31,7 @@ unsafe impl Sync for Clock {} /// A safe wrapper around [`bindings::DeviceState`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct DeviceState(Opaque); unsafe impl Send for DeviceState {} diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs index 92c7449b80..282315fce9 100644 --- a/rust/hw/core/src/sysbus.rs +++ b/rust/hw/core/src/sysbus.rs @@ -19,7 +19,7 @@ use crate::{ /// A safe wrapper around [`bindings::SysBusDevice`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct SysBusDevice(Opaque); unsafe impl Send for SysBusDevice {} diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs index 2f08b8f3bf..247d812866 100644 --- a/rust/hw/core/tests/tests.rs +++ b/rust/hw/core/tests/tests.rs @@ -17,7 +17,7 @@ pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::< .build(); #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] pub struct DummyState { parent: ParentField, #[property(rename = "migrate-clk", default = true)] @@ -54,7 +54,7 @@ impl DeviceImpl for DummyState { } #[repr(C)] -#[derive(qemu_macros::Object, qemu_macros::Device)] +#[derive(qom::Object, hwcore::Device)] pub struct DummyChildState { parent: ParentField, } diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 08bf97af55..f781b28d8b 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -17,7 +17,6 @@ migration = { path = "../../../migration" } bql = { path = "../../../bql" } qom = { path = "../../../qom" } system = { path = "../../../system" } -qemu_macros = { path = "../../../qemu-macros" } hwcore = { path = "../../../hw/core" } [lints] diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 8ab26630d9..b6bb9477f0 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -8,7 +8,6 @@ _libhpet_rs = static_library( util_rs, migration_rs, bql_rs, - qemu_macros, qom_rs, system_rs, hwcore_rs, diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 07e0f639fc..3cfbe9c32b 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -97,7 +97,7 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; /// Timer N Interrupt Routing Capability (bits 32:63) const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; -#[derive(qemu_macros::TryInto)] +#[derive(common::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Timer registers, masked by 0x18 @@ -110,7 +110,7 @@ enum TimerRegister { ROUTE = 16, } -#[derive(qemu_macros::TryInto)] +#[derive(common::TryInto)] #[repr(u64)] #[allow(non_camel_case_types)] /// Global registers @@ -520,7 +520,7 @@ impl HPETTimer { /// HPET Event Timer Block Abstraction #[repr(C)] -#[derive(qemu_macros::Object)] +#[derive(qom::Object)] pub struct HPETState { parent_obj: ParentField, iomem: MemoryRegion, diff --git a/rust/meson.build b/rust/meson.build index bd9b9cb83e..c7bd6aba45 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -20,8 +20,9 @@ proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) genrs = [] -subdir('common') subdir('qemu-macros') + +subdir('common') subdir('bits') subdir('util') subdir('migration') diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index 66af81e0a3..708bfaaa68 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -15,7 +15,6 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } util = { path = "../util" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs index 204c6fea2f..24c44fc2af 100644 --- a/rust/qom/src/lib.rs +++ b/rust/qom/src/lib.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +pub use qemu_macros::Object; + pub mod bindings; // preserve one-item-per-"use" syntax, it is clearer diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs index 2cd1d85011..5808051cd7 100644 --- a/rust/qom/src/qom.rs +++ b/rust/qom/src/qom.rs @@ -112,7 +112,7 @@ pub use crate::bindings::{type_register_static, ObjectClass}; /// A safe wrapper around [`bindings::Object`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct Object(Opaque); unsafe impl Send for Object {} @@ -173,7 +173,7 @@ macro_rules! qom_isa { /// /// ```ignore /// #[repr(C)] -/// #[derive(qemu_macros::Object)] +/// #[derive(qom::Object)] /// pub struct MyDevice { /// parent: ParentField, /// ... diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml index d8338c8348..7fd369b9e3 100644 --- a/rust/system/Cargo.toml +++ b/rust/system/Cargo.toml @@ -16,7 +16,6 @@ rust-version.workspace = true common = { path = "../common" } qom = { path = "../qom" } util = { path = "../util" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/system/meson.build b/rust/system/meson.build index 9f88166f3d..3ec140de01 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -39,4 +39,4 @@ _system_rs = static_library( ) system_rs = declare_dependency(link_with: [_system_rs], - dependencies: [qemu_macros, hwcore]) + dependencies: [hwcore]) diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs index 7312f809f5..02aa3af7b1 100644 --- a/rust/system/src/memory.rs +++ b/rust/system/src/memory.rs @@ -129,7 +129,7 @@ impl Default for MemoryRegionOpsBuilder { /// A safe wrapper around [`bindings::MemoryRegion`]. #[repr(transparent)] -#[derive(qemu_macros::Wrapper)] +#[derive(common::Wrapper)] pub struct MemoryRegion(Opaque); unsafe impl Send for MemoryRegion {} diff --git a/rust/tests/Cargo.toml b/rust/tests/Cargo.toml index 8d106d896d..d47dc3314d 100644 --- a/rust/tests/Cargo.toml +++ b/rust/tests/Cargo.toml @@ -19,7 +19,6 @@ hwcore = { path = "../hw/core" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } -qemu_macros = { path = "../qemu-macros" } qom = { path = "../qom" } system = { path = "../system" } diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 18e6619ca0..1f6767ed9d 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -17,7 +17,6 @@ anyhow = { workspace = true } foreign = { workspace = true } libc = { workspace = true } common = { path = "../common" } -qemu_macros = { path = "../qemu-macros" } [lints] workspace = true diff --git a/rust/util/meson.build b/rust/util/meson.build index 197872c9b2..87a893673d 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,7 +39,7 @@ _util_rs = static_library( ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_macros, qom, qemuutil], + dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qom, qemuutil], ) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs index 622b6ee309..c6b3e4088e 100644 --- a/rust/util/src/timer.rs +++ b/rust/util/src/timer.rs @@ -15,14 +15,14 @@ use crate::bindings::{ /// A safe wrapper around [`bindings::QEMUTimer`]. #[repr(transparent)] -#[derive(Debug, qemu_macros::Wrapper)] +#[derive(Debug, common::Wrapper)] pub struct Timer(Opaque); unsafe impl Send for Timer {} unsafe impl Sync for Timer {} #[repr(transparent)] -#[derive(qemu_macros::Wrapper)] +#[derive(common::Wrapper)] pub struct TimerListGroup(Opaque); unsafe impl Send for TimerListGroup {} -- cgit 1.4.1