From 71e84e5ae87b8e6adce3af7dd2a490c7b2f39772 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:36 +0200 Subject: rust: add missing const markers for MSRV==1.83.0 Rust 1.83 allows more functions to be marked const. Fix clippy with bumped minimum supported Rust version. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-5-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/timer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/qemu-api/src/timer.rs') diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index 0a2d111d49..0daec62f92 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -39,7 +39,7 @@ impl Timer { /// /// The timer must be initialized before it is armed with /// [`modify`](Self::modify). - pub unsafe fn new() -> Self { + pub const unsafe fn new() -> Self { // SAFETY: requirements relayed to callers of Timer::new Self(unsafe { Opaque::zeroed() }) } -- cgit 1.4.1 From aecca0676ddd9e032de4eeda371b81598d3257bb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:37 +0200 Subject: rust: use inline const expressions They were stabilized in Rust 1.79.0. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-6-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 9 +++------ rust/qemu-api/src/callbacks.rs | 27 +-------------------------- rust/qemu-api/src/chardev.rs | 2 +- rust/qemu-api/src/qdev.rs | 2 +- rust/qemu-api/src/timer.rs | 2 +- rust/qemu-api/src/vmstate.rs | 2 +- 6 files changed, 8 insertions(+), 36 deletions(-) (limited to 'rust/qemu-api/src/timer.rs') diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index e0ee4a9837..98e3a33a3c 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -79,9 +79,6 @@ QEMU supports rustc version 1.83.0 and newer. The following features from relatively new versions of Rust are not used for historical reasons; patches are welcome: -* inline const expression (stable in 1.79.0), currently worked around with - associated constants in the ``FnCall`` trait. - * associated constants are still explicitly marked ``'static`` (`changed in 1.81.0`__) @@ -97,9 +94,9 @@ patches are welcome: before QEMU can use them. For now, there is special code in ``util/error.c`` to support non-NUL-terminated file names. -* associated const equality would be nice to have for some users of - ``callbacks::FnCall``, but is still experimental. ``ASSERT_IS_SOME`` - replaces it. +Associated const equality would be nice to have for some users of +``callbacks::FnCall``, but is still experimental. Const assertions +are used instead. __ https://github.com/rust-lang/rust/pull/125258 diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs index 9642a16eb8..dbe2305f50 100644 --- a/rust/qemu-api/src/callbacks.rs +++ b/rust/qemu-api/src/callbacks.rs @@ -113,31 +113,6 @@ use std::{mem, ptr::NonNull}; /// 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 { - /// Referring to this internal constant asserts that the `Self` type is - /// zero-sized. Can be replaced by an inline const expression in - /// Rust 1.79.0+. - const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::() == 0) }; - - /// Referring to this constant asserts that the `Self` type is an actual - /// function type, which can be used to catch incorrect use of `()` - /// at compile time. - /// - /// # Examples - /// - /// ```compile_fail - /// # use qemu_api::callbacks::FnCall; - /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { - /// let _: () = F::ASSERT_IS_SOME; - /// F::call((s,)) - /// } - /// - /// let s: String = call_it((), "hello world"); // does not compile - /// ``` - /// - /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in - /// Rust 1.79.0 or newer. - const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) }; - /// `true` if `Self` is an actual function type and not `()`. /// /// # Examples @@ -195,7 +170,7 @@ macro_rules! impl_call { #[inline(always)] fn call(a: ($($args,)*)) -> R { - let _: () = Self::ASSERT_ZERO_SIZED; + 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, diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 6e0590d758..cb27be5256 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -138,7 +138,7 @@ impl CharBackend { F::call((owner, event)) } - let _: () = CanReceiveFn::ASSERT_IS_SOME; + const { assert!(CanReceiveFn::IS_SOME) }; let receive_cb: Option = if ReceiveFn::is_some() { Some(rust_receive_cb::) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 36f02fb57d..52d54a4494 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -373,7 +373,7 @@ where } } - let _: () = F::ASSERT_IS_SOME; + const { assert!(F::IS_SOME) }; unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( opaque: *mut c_void, line: c_int, diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index 0daec62f92..1e639eaf22 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -56,7 +56,7 @@ impl Timer { ) where F: for<'a> FnCall<(&'a T,)>, { - let _: () = F::ASSERT_IS_SOME; + const { assert!(F::IS_SOME) }; /// timer expiration callback unsafe extern "C" fn rust_timer_handler FnCall<(&'a T,)>>( diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 812f390d78..8515e38213 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -457,7 +457,7 @@ macro_rules! vmstate_exist_fn { const fn test_cb_builder__ $crate::callbacks::FnCall<(&'a T, u8), bool>>( _phantom: ::core::marker::PhantomData, ) -> $crate::vmstate::VMSFieldExistCb { - let _: () = F::ASSERT_IS_SOME; + const { assert!(F::IS_SOME) }; $crate::vmstate::rust_vms_test_field_exists:: } -- cgit 1.4.1 From 593c408a6a8cd8b0af9bf60c7c3625da7910a737 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:48 +0200 Subject: rust: split Rust-only "common" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-6-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 10 + rust/Cargo.toml | 1 + rust/common/Cargo.toml | 19 ++ rust/common/meson.build | 34 ++++ rust/common/src/assertions.rs | 148 +++++++++++++++ rust/common/src/bitops.rs | 118 ++++++++++++ rust/common/src/callbacks.rs | 216 +++++++++++++++++++++ rust/common/src/errno.rs | 354 +++++++++++++++++++++++++++++++++++ rust/common/src/lib.rs | 20 ++ rust/common/src/opaque.rs | 238 +++++++++++++++++++++++ rust/common/src/uninit.rs | 85 +++++++++ rust/common/src/zeroable.rs | 18 ++ rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 3 +- rust/hw/char/pl011/src/device.rs | 3 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 3 +- rust/hw/timer/hpet/src/device.rs | 2 +- rust/hw/timer/hpet/src/fw_cfg.rs | 3 +- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 16 +- rust/qemu-api-macros/src/tests.rs | 6 +- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 14 +- rust/qemu-api/src/assertions.rs | 148 --------------- rust/qemu-api/src/bindings.rs | 21 +++ rust/qemu-api/src/bitops.rs | 119 ------------ rust/qemu-api/src/callbacks.rs | 216 --------------------- rust/qemu-api/src/cell.rs | 235 +---------------------- rust/qemu-api/src/chardev.rs | 5 +- rust/qemu-api/src/errno.rs | 354 ----------------------------------- rust/qemu-api/src/error.rs | 3 +- rust/qemu-api/src/irq.rs | 3 +- rust/qemu-api/src/lib.rs | 6 - rust/qemu-api/src/log.rs | 4 +- rust/qemu-api/src/memory.rs | 5 +- rust/qemu-api/src/prelude.rs | 6 +- rust/qemu-api/src/qdev.rs | 10 +- rust/qemu-api/src/qom.rs | 3 +- rust/qemu-api/src/sysbus.rs | 3 +- rust/qemu-api/src/timer.rs | 8 +- rust/qemu-api/src/uninit.rs | 85 --------- rust/qemu-api/src/vmstate.rs | 23 +-- rust/qemu-api/src/zeroable.rs | 37 ---- rust/qemu-api/tests/vmstate_tests.rs | 3 +- 46 files changed, 1351 insertions(+), 1262 deletions(-) create mode 100644 rust/common/Cargo.toml create mode 100644 rust/common/meson.build create mode 100644 rust/common/src/assertions.rs create mode 100644 rust/common/src/bitops.rs create mode 100644 rust/common/src/callbacks.rs create mode 100644 rust/common/src/errno.rs create mode 100644 rust/common/src/lib.rs create mode 100644 rust/common/src/opaque.rs create mode 100644 rust/common/src/uninit.rs create mode 100644 rust/common/src/zeroable.rs delete mode 100644 rust/qemu-api/src/assertions.rs delete mode 100644 rust/qemu-api/src/bitops.rs delete mode 100644 rust/qemu-api/src/callbacks.rs delete mode 100644 rust/qemu-api/src/errno.rs delete mode 100644 rust/qemu-api/src/uninit.rs delete mode 100644 rust/qemu-api/src/zeroable.rs (limited to 'rust/qemu-api/src/timer.rs') diff --git a/MAINTAINERS b/MAINTAINERS index fb045388b9..cee5a34206 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3515,6 +3515,7 @@ F: include/hw/registerfields.h Rust M: Manos Pitsidianakis S: Maintained +F: rust/common/ F: rust/qemu-api F: rust/qemu-api-macros F: rust/rustfmt.toml diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4baf6ba663..71e8c7ed62 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -44,6 +44,13 @@ dependencies = [ "qemu_api_macros", ] +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "libc", +] + [[package]] name = "either" version = "1.12.0" @@ -63,6 +70,7 @@ dependencies = [ name = "hpet" version = "0.1.0" dependencies = [ + "common", "qemu_api", "qemu_api_macros", ] @@ -89,6 +97,7 @@ dependencies = [ "bilge", "bilge-impl", "bits", + "common", "qemu_api", "qemu_api_macros", ] @@ -130,6 +139,7 @@ name = "qemu_api" version = "0.1.0" dependencies = [ "anyhow", + "common", "foreign", "libc", "qemu_api_macros", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index cd4bf8ef8e..c0426d4243 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "bits", + "common", "qemu-api-macros", "qemu-api", "hw/char/pl011", diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml new file mode 100644 index 0000000000..5e106427e8 --- /dev/null +++ b/rust/common/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "common" +version = "0.1.0" +description = "Rust common code for QEMU" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +libc.workspace = true + +[lints] +workspace = true diff --git a/rust/common/meson.build b/rust/common/meson.build new file mode 100644 index 0000000000..230a967760 --- /dev/null +++ b/rust/common/meson.build @@ -0,0 +1,34 @@ +_common_cfg = run_command(rustc_args, + '--config-headers', config_host_h, '--features', files('Cargo.toml'), + capture: true, check: true).stdout().strip().splitlines() + +_common_rs = static_library( + 'common', + structured_sources( + [ + 'src/lib.rs', + 'src/assertions.rs', + 'src/bitops.rs', + 'src/callbacks.rs', + 'src/errno.rs', + 'src/opaque.rs', + 'src/uninit.rs', + 'src/zeroable.rs', + ], + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: _common_cfg, + dependencies: [libc_rs], +) + +common_rs = declare_dependency(link_with: [_common_rs]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-common-doctests', + _common_rs, + protocol: 'rust', + dependencies: common_rs, + suite: ['doc', 'rust']) diff --git a/rust/common/src/assertions.rs b/rust/common/src/assertions.rs new file mode 100644 index 0000000000..91f83a5d3d --- /dev/null +++ b/rust/common/src/assertions.rs @@ -0,0 +1,148 @@ +// Copyright 2024, Red Hat Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +#![doc(hidden)] +//! This module provides macros to check the equality of types and +//! the type of `struct` fields. This can be useful to ensure that +//! types match the expectations of C code. +//! +//! Documentation is hidden because it only exposes macros, which +//! are exported directly from `common`. + +// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 +// (stackoverflow answers are released under MIT license). + +#[doc(hidden)] +pub trait EqType { + type Itself; +} + +impl EqType for T { + type Itself = T; +} + +/// Assert that two types are the same. +/// +/// # Examples +/// +/// ``` +/// # use common::assert_same_type; +/// # use std::ops::Deref; +/// assert_same_type!(u32, u32); +/// assert_same_type!( as Deref>::Target, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use common::assert_same_type; +/// assert_same_type!(&Box, &u32); +/// ``` +#[macro_export] +macro_rules! assert_same_type { + ($t1:ty, $t2:ty) => { + const _: () = { + #[allow(unused)] + fn assert_same_type(v: $t1) { + fn types_must_be_equal(_: T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $t2>(v); + } + }; + }; +} + +/// Assert that a field of a struct has the given type. +/// +/// # Examples +/// +/// ``` +/// # use common::assert_field_type; +/// pub struct A { +/// field1: u32, +/// } +/// +/// assert_field_type!(A, field1, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use common::assert_field_type; +/// # pub struct A { field1: u32 } +/// assert_field_type!(A, field1, i32); +/// ``` +#[macro_export] +macro_rules! assert_field_type { + (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { + const _: () = { + #[allow(unused)] + const fn assert_field_type($param_name: &$t) { + const fn types_must_be_equal(_: &T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $ti>(&$($field)*); + } + }; + }; + + ($t:ty, $i:tt, $ti:ty) => { + $crate::assert_field_type!(@internal v, $ti, $t, v.$i); + }; +} + +/// Assert that an expression matches a pattern. This can also be +/// useful to compare enums that do not implement `Eq`. +/// +/// # Examples +/// +/// ``` +/// # use common::assert_match; +/// // JoinHandle does not implement `Eq`, therefore the result +/// // does not either. +/// let result: Result, u32> = Err(42); +/// assert_match!(result, Err(42)); +/// ``` +#[macro_export] +macro_rules! assert_match { + ($a:expr, $b:pat) => { + assert!( + match $a { + $b => true, + _ => false, + }, + "{} = {:?} does not match {}", + stringify!($a), + $a, + stringify!($b) + ); + }; +} + +/// Assert at compile time that an expression is true. This is similar +/// to `const { assert!(...); }` but it works outside functions, as well as +/// on versions of Rust before 1.79. +/// +/// # Examples +/// +/// ``` +/// # use common::static_assert; +/// static_assert!("abc".len() == 3); +/// ``` +/// +/// ```compile_fail +/// # use common::static_assert; +/// static_assert!("abc".len() == 2); // does not compile +/// ``` +#[macro_export] +macro_rules! static_assert { + ($x:expr) => { + const _: () = assert!($x); + }; +} diff --git a/rust/common/src/bitops.rs b/rust/common/src/bitops.rs new file mode 100644 index 0000000000..06c78c3b8a --- /dev/null +++ b/rust/common/src/bitops.rs @@ -0,0 +1,118 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This module provides bit operation extensions to integer types. + +use std::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, +}; + +/// Trait for extensions to integer types +pub trait IntegerExt: + Add + AddAssign + + BitAnd + BitAndAssign + + BitOr + BitOrAssign + + BitXor + BitXorAssign + + Copy + + Div + DivAssign + + Eq + + Mul + MulAssign + + Not + Ord + PartialOrd + + Rem + RemAssign + + Shl + ShlAssign + + Shl + ShlAssign + // add more as needed + Shr + ShrAssign + + Shr + ShrAssign // add more as needed +{ + const BITS: u32; + const MAX: Self; + const MIN: Self; + const ONE: Self; + const ZERO: Self; + + #[inline] + #[must_use] + fn bit(start: u32) -> Self + { + debug_assert!(start < Self::BITS); + + Self::ONE << start + } + + #[inline] + #[must_use] + fn mask(start: u32, length: u32) -> Self + { + /* FIXME: Implement a more elegant check with error handling support? */ + debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start); + + (Self::MAX >> (Self::BITS - length)) << start + } + + #[inline] + #[must_use] + fn deposit(self, start: u32, length: u32, + fieldval: U) -> Self + where Self: From + { + debug_assert!(length <= U::BITS); + + let mask = Self::mask(start, length); + (self & !mask) | ((Self::from(fieldval) << start) & mask) + } + + #[inline] + #[must_use] + fn extract(self, start: u32, length: u32) -> Self + { + let mask = Self::mask(start, length); + (self & mask) >> start + } +} + +macro_rules! impl_num_ext { + ($type:ty) => { + impl IntegerExt for $type { + const BITS: u32 = <$type>::BITS; + const MAX: Self = <$type>::MAX; + const MIN: Self = <$type>::MIN; + const ONE: Self = 1; + const ZERO: Self = 0; + } + }; +} + +impl_num_ext!(u8); +impl_num_ext!(u16); +impl_num_ext!(u32); +impl_num_ext!(u64); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deposit() { + assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15); + assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15); + } + + #[test] + fn test_extract() { + assert_eq!(15u32.extract(2, 4), 3); + } + + #[test] + fn test_bit() { + assert_eq!(u8::bit(7), 128); + assert_eq!(u32::bit(16), 0x10000); + } + + #[test] + fn test_mask() { + assert_eq!(u8::mask(7, 1), 128); + assert_eq!(u32::mask(8, 8), 0xff00); + } +} diff --git a/rust/common/src/callbacks.rs b/rust/common/src/callbacks.rs new file mode 100644 index 0000000000..b8898fe96f --- /dev/null +++ b/rust/common/src/callbacks.rs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT + +//! Utility functions to deal with callbacks from C to Rust. + +use std::{mem, ptr::NonNull}; + +/// Trait for functions (types implementing [`Fn`]) that can be used as +/// callbacks. These include both zero-capture closures and function pointers. +/// +/// In Rust, calling a function through the `Fn` trait normally requires a +/// `self` parameter, even though for zero-sized functions (including function +/// pointers) the type itself contains all necessary information to call the +/// function. This trait provides a `call` function that doesn't require `self`, +/// allowing zero-sized functions to be called using only their type. +/// +/// This enables zero-sized functions to be passed entirely through generic +/// parameters and resolved at compile-time. A typical use is a function +/// receiving an unused parameter of generic type `F` and calling it via +/// `F::call` or passing it to another function via `func::`. +/// +/// QEMU uses this trick to create wrappers to C callbacks. The wrappers +/// are needed to convert an opaque `*mut c_void` into a Rust reference, +/// but they only have a single opaque that they can use. The `FnCall` +/// trait makes it possible to use that opaque for `self` or any other +/// reference: +/// +/// ```ignore +/// // The compiler creates a new `rust_bh_cb` wrapper for each function +/// // passed to `qemu_bh_schedule_oneshot` below. +/// unsafe extern "C" fn rust_bh_cb FnCall<(&'a T,)>>( +/// opaque: *mut c_void, +/// ) { +/// // SAFETY: the opaque was passed as a reference to `T`. +/// F::call((unsafe { &*(opaque.cast::()) }, )) +/// } +/// +/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`. +/// // Using a reference allows usage in const context. +/// fn qemu_bh_schedule_oneshot FnCall<(&'a T,)>>(_f: &F, opaque: &T) { +/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; +/// unsafe { +/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void) +/// } +/// } +/// ``` +/// +/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore +/// compiled to a separate function ("monomorphization"). If you wanted +/// to pass `self` as the opaque value, the generic parameters would be +/// `rust_bh_cb::`. +/// +/// `Args` is a tuple type whose types are the arguments of the function, +/// while `R` is the returned type. +/// +/// # Examples +/// +/// ``` +/// # use common::callbacks::FnCall; +/// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// F::call((s,)) +/// } +/// +/// let s: String = call_it(&str::to_owned, "hello world"); +/// assert_eq!(s, "hello world"); +/// ``` +/// +/// Note that the compiler will produce a different version of `call_it` for +/// each function that is passed to it. Therefore the argument is not really +/// used, except to decide what is `F` and what `F::call` does. +/// +/// Attempting to pass a non-zero-sized closure causes a compile-time failure: +/// +/// ```compile_fail +/// # use common::callbacks::FnCall; +/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { +/// # F::call((s,)) +/// # } +/// let x: &'static str = "goodbye world"; +/// call_it(&move |_| String::from(x), "hello workd"); +/// ``` +/// +/// `()` can be used to indicate "no function": +/// +/// ``` +/// # use common::callbacks::FnCall; +/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { +/// if F::IS_SOME { +/// Some(F::call((s,))) +/// } else { +/// None +/// } +/// } +/// +/// assert!(optional(&(), "hello world").is_none()); +/// ``` +/// +/// Invoking `F::call` will then be a run-time error. +/// +/// ```should_panic +/// # use common::callbacks::FnCall; +/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// # F::call((s,)) +/// # } +/// let s: String = call_it(&(), "hello world"); // panics +/// ``` +/// +/// # Safety +/// +/// Because `Self` is a zero-sized type, all instances of the type are +/// equivalent. However, in addition to this, `Self` must have no invariants +/// that could be violated by creating a reference to it. +/// +/// This is always true for zero-capture closures and function pointers, as long +/// as the code is able to name the function in the first place. +pub unsafe trait FnCall: 'static + Sync + Sized { + /// `true` if `Self` is an actual function type and not `()`. + /// + /// # Examples + /// + /// You can use `IS_SOME` to catch this at compile time: + /// + /// ```compile_fail + /// # use common::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// const { assert!(F::IS_SOME) } + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + const IS_SOME: bool; + + /// `false` if `Self` is an actual function type, `true` if it is `()`. + fn is_none() -> bool { + !Self::IS_SOME + } + + /// `true` if `Self` is an actual function type, `false` if it is `()`. + fn is_some() -> bool { + Self::IS_SOME + } + + /// Call the function with the arguments in args. + fn call(a: Args) -> R; +} + +/// `()` acts as a "null" callback. Using `()` and `function` is nicer +/// than `None` and `Some(function)`, because the compiler is unable to +/// infer the type of just `None`. Therefore, the trait itself acts as the +/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. +unsafe impl FnCall for () { + const IS_SOME: bool = false; + + /// Call the function with the arguments in args. + fn call(_a: Args) -> R { + panic!("callback not specified") + } +} + +macro_rules! impl_call { + ($($args:ident,)* ) => ( + // SAFETY: because each function is treated as a separate type, + // accessing `FnCall` is only possible in code that would be + // allowed to call the function. + unsafe impl FnCall<($($args,)*), R> for F + where + F: 'static + Sync + Sized + Fn($($args, )*) -> R, + { + const IS_SOME: bool = true; + + #[inline(always)] + fn call(a: ($($args,)*)) -> R { + const { assert!(mem::size_of::() == 0) }; + + // SAFETY: the safety of this method is the condition for implementing + // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, + // see https://github.com/rust-lang/libs-team/issues/292. + let f: &'static F = unsafe { &*NonNull::::dangling().as_ptr() }; + let ($($args,)*) = a; + f($($args,)*) + } + } + ) +} + +impl_call!(_1, _2, _3, _4, _5,); +impl_call!(_1, _2, _3, _4,); +impl_call!(_1, _2, _3,); +impl_call!(_1, _2,); +impl_call!(_1,); +impl_call!(); + +#[cfg(test)] +mod tests { + use super::*; + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { + F::call(("hello world",)) + } + + #[test] + fn test_call() { + assert_eq!(do_test_call(&str::to_owned), "hello world") + } + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { + assert!(F::is_some()); + } + + #[test] + fn test_is_some() { + do_test_is_some(&str::to_owned); + } +} diff --git a/rust/common/src/errno.rs b/rust/common/src/errno.rs new file mode 100644 index 0000000000..64b2933b07 --- /dev/null +++ b/rust/common/src/errno.rs @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Utility functions to convert `errno` to and from +//! [`io::Error`]/[`io::Result`] +//! +//! QEMU C functions often have a "positive success/negative `errno`" calling +//! convention. This module provides functions to portably convert an integer +//! into an [`io::Result`] and back. + +use std::{ + convert::{self, TryFrom}, + io::{self, ErrorKind}, +}; + +/// An `errno` value that can be converted into an [`io::Error`] +pub struct Errno(pub u16); + +// On Unix, from_raw_os_error takes an errno value and OS errors +// are printed using strerror. On Windows however it takes a +// GetLastError() value; therefore we need to convert errno values +// into io::Error by hand. This is the same mapping that the +// standard library uses to retrieve the kind of OS errors +// (`std::sys::pal::unix::decode_error_kind`). +impl From for ErrorKind { + fn from(value: Errno) -> ErrorKind { + use ErrorKind::*; + let Errno(errno) = value; + match i32::from(errno) { + libc::EPERM | libc::EACCES => PermissionDenied, + libc::ENOENT => NotFound, + libc::EINTR => Interrupted, + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + libc::ENOMEM => OutOfMemory, + libc::EEXIST => AlreadyExists, + libc::EINVAL => InvalidInput, + libc::EPIPE => BrokenPipe, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::ENOTCONN => NotConnected, + libc::ENOTSUP => Unsupported, + libc::ETIMEDOUT => TimedOut, + _ => Other, + } + } +} + +// This is used on Windows for all io::Errors, but also on Unix if the +// io::Error does not have a raw OS error. This is the reversed +// mapping of the above; EIO is returned for unknown ErrorKinds. +impl From for Errno { + fn from(value: io::ErrorKind) -> Errno { + use ErrorKind::*; + let errno = match value { + // can be both EPERM or EACCES :( pick one + PermissionDenied => libc::EPERM, + NotFound => libc::ENOENT, + Interrupted => libc::EINTR, + WouldBlock => libc::EAGAIN, + OutOfMemory => libc::ENOMEM, + AlreadyExists => libc::EEXIST, + InvalidInput => libc::EINVAL, + BrokenPipe => libc::EPIPE, + AddrInUse => libc::EADDRINUSE, + AddrNotAvailable => libc::EADDRNOTAVAIL, + ConnectionAborted => libc::ECONNABORTED, + ConnectionRefused => libc::ECONNREFUSED, + ConnectionReset => libc::ECONNRESET, + NotConnected => libc::ENOTCONN, + Unsupported => libc::ENOTSUP, + TimedOut => libc::ETIMEDOUT, + _ => libc::EIO, + }; + Errno(errno as u16) + } +} + +impl From for io::Error { + #[cfg(unix)] + fn from(value: Errno) -> io::Error { + let Errno(errno) = value; + io::Error::from_raw_os_error(errno.into()) + } + + #[cfg(windows)] + fn from(value: Errno) -> io::Error { + let error_kind: ErrorKind = value.into(); + error_kind.into() + } +} + +impl From for Errno { + fn from(value: io::Error) -> Errno { + if cfg!(unix) { + if let Some(errno) = value.raw_os_error() { + return Errno(u16::try_from(errno).unwrap()); + } + } + value.kind().into() + } +} + +impl From for Errno { + fn from(_value: convert::Infallible) -> Errno { + panic!("unreachable") + } +} + +/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] +/// for the "right" set of types. +mod traits { + use super::Errno; + + /// A signed type that can be converted into an + /// [`io::Result`](std::io::Result) + pub trait GetErrno { + /// Unsigned variant of `Self`, used as the type for the `Ok` case. + type Out; + + /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative + fn into_errno_result(self) -> Result; + } + + /// A type that can be taken out of an [`io::Result`](std::io::Result) and + /// converted into "positive success/negative `errno`" convention. + pub trait MergeErrno { + /// Signed variant of `Self`, used as the return type of + /// [`into_neg_errno`](super::into_neg_errno). + type Out: From + std::ops::Neg; + + /// Return `self`, asserting that it is in range + fn map_ok(self) -> Self::Out; + } + + macro_rules! get_errno { + ($t:ty, $out:ty) => { + impl GetErrno for $t { + type Out = $out; + fn into_errno_result(self) -> Result { + match self { + 0.. => Ok(self as $out), + -65535..=-1 => Err(Errno(-self as u16)), + _ => panic!("{self} is not a negative errno"), + } + } + } + }; + } + + get_errno!(i32, u32); + get_errno!(i64, u64); + get_errno!(isize, usize); + + macro_rules! merge_errno { + ($t:ty, $out:ty) => { + impl MergeErrno for $t { + type Out = $out; + fn map_ok(self) -> Self::Out { + self.try_into().unwrap() + } + } + }; + } + + merge_errno!(u8, i32); + merge_errno!(u16, i32); + merge_errno!(u32, i32); + merge_errno!(u64, i64); + + impl MergeErrno for () { + type Out = i32; + fn map_ok(self) -> i32 { + 0 + } + } +} + +use traits::{GetErrno, MergeErrno}; + +/// Convert an integer value into a [`io::Result`]. +/// +/// Positive values are turned into an `Ok` result; negative values +/// are interpreted as negated `errno` and turned into an `Err`. +/// +/// ``` +/// # use common::errno::into_io_result; +/// # use std::io::ErrorKind; +/// let ok = into_io_result(1i32).unwrap(); +/// assert_eq!(ok, 1u32); +/// +/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM +/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); +/// ``` +/// +/// # Panics +/// +/// Since the result is an unsigned integer, negative values must +/// be close to 0; values that are too far away are considered +/// likely overflows and will panic: +/// +/// ```should_panic +/// # use common::errno::into_io_result; +/// # #[allow(dead_code)] +/// let err = into_io_result(-0x1234_5678i32); // panic +/// ``` +pub fn into_io_result(value: T) -> io::Result { + value.into_errno_result().map_err(Into::into) +} + +/// Convert a [`Result`] into an integer value, using negative `errno` +/// values to report errors. +/// +/// ``` +/// # use common::errno::into_neg_errno; +/// # use std::io::{self, ErrorKind}; +/// let ok: io::Result<()> = Ok(()); +/// assert_eq!(into_neg_errno(ok), 0); +/// +/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); +/// assert_eq!(into_neg_errno(err), -22); // -EINVAL +/// ``` +/// +/// Since this module also provides the ability to convert [`io::Error`] +/// to an `errno` value, [`io::Result`] is the most commonly used type +/// for the argument of this function: +/// +/// # Panics +/// +/// Since the result is a signed integer, integer `Ok` values must remain +/// positive: +/// +/// ```should_panic +/// # use common::errno::into_neg_errno; +/// # use std::io; +/// let err: io::Result = Ok(0x8899_AABB); +/// into_neg_errno(err) // panic +/// # ; +/// ``` +pub fn into_neg_errno>(value: Result) -> T::Out { + match value { + Ok(x) => x.map_ok(), + Err(err) => -T::Out::from(err.into().0), + } +} + +#[cfg(test)] +mod tests { + use std::io::ErrorKind; + + use super::*; + use crate::assert_match; + + #[test] + pub fn test_from_u8() { + let ok: io::Result<_> = Ok(42u8); + assert_eq!(into_neg_errno(ok), 42); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_from_u16() { + let ok: io::Result<_> = Ok(1234u16); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i32() { + assert_match!(into_io_result(1234i32), Ok(1234)); + + let err = into_io_result(-1i32).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(1)); + assert_match!(err.kind(), ErrorKind::PermissionDenied); + } + + #[test] + pub fn test_from_u32() { + let ok: io::Result<_> = Ok(1234u32); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i64() { + assert_match!(into_io_result(1234i64), Ok(1234)); + + let err = into_io_result(-22i64).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(22)); + assert_match!(err.kind(), ErrorKind::InvalidInput); + } + + #[test] + pub fn test_from_u64() { + let ok: io::Result<_> = Ok(1234u64); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::InvalidInput.into()); + assert_eq!(into_neg_errno(err), -22); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(6)); + assert_eq!(into_neg_errno(os_err), -6); + } + } + + #[test] + pub fn test_isize() { + assert_match!(into_io_result(1234isize), Ok(1234)); + + let err = into_io_result(-4isize).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(4)); + assert_match!(err.kind(), ErrorKind::Interrupted); + } + + #[test] + pub fn test_from_unit() { + let ok: io::Result<_> = Ok(()); + assert_eq!(into_neg_errno(ok), 0); + + let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); + assert_eq!(into_neg_errno(err), -12); + + if cfg!(unix) { + let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); + assert_eq!(into_neg_errno(os_err), -2); + } + } +} diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs new file mode 100644 index 0000000000..25216503aa --- /dev/null +++ b/rust/common/src/lib.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod assertions; + +pub mod bitops; + +pub mod callbacks; +pub use callbacks::FnCall; + +pub mod errno; +pub use errno::Errno; + +pub mod opaque; +pub use opaque::{Opaque, Wrapper}; + +pub mod uninit; +pub use uninit::MaybeUninitField; + +pub mod zeroable; +pub use zeroable::Zeroable; diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs new file mode 100644 index 0000000000..d25a5f3ae1 --- /dev/null +++ b/rust/common/src/opaque.rs @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: MIT + +//! ## Opaque wrappers +//! +//! The cell types from the previous section are useful at the boundaries +//! of code that requires interior mutability. When writing glue code that +//! interacts directly with C structs, however, it is useful to operate +//! at a lower level. +//! +//! C functions often violate Rust's fundamental assumptions about memory +//! safety by modifying memory even if it is shared. Furthermore, C structs +//! often start their life uninitialized and may be populated lazily. +//! +//! For this reason, this module provides the [`Opaque`] type to opt out +//! of Rust's usual guarantees about the wrapped type. Access to the wrapped +//! value is always through raw pointers, obtained via methods like +//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These +//! pointers can then be passed to C functions or dereferenced; both actions +//! require `unsafe` blocks, making it clear where safety guarantees must be +//! manually verified. For example +//! +//! ```ignore +//! unsafe { +//! let state = Opaque::::uninit(); +//! qemu_struct_init(state.as_mut_ptr()); +//! } +//! ``` +//! +//! [`Opaque`] will usually be wrapped one level further, so that +//! bridge methods can be added to the wrapper: +//! +//! ```ignore +//! pub struct MyStruct(Opaque); +//! +//! impl MyStruct { +//! fn new() -> Pin> { +//! let result = Box::pin(unsafe { Opaque::uninit() }); +//! unsafe { qemu_struct_init(result.as_mut_ptr()) }; +//! result +//! } +//! } +//! ``` +//! +//! This pattern of wrapping bindgen-generated types in [`Opaque`] provides +//! several advantages: +//! +//! * The choice of traits to be implemented is not limited by the +//! bindgen-generated code. For example, [`Drop`] can be added without +//! disabling [`Copy`] on the underlying bindgen type +//! +//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper +//! type rather than being automatically derived from the C struct's layout +//! +//! * Methods can be implemented in a separate crate from the bindgen-generated +//! bindings +//! +//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display) +//! implementations can be customized to be more readable than the raw C +//! struct representation +//! +//! The [`Opaque`] type does not include BQL validation; it is possible to +//! assert in the code that the right lock is taken, to use it together +//! with a custom lock guard type, or to let C code take the lock, as +//! appropriate. It is also possible to use it with non-thread-safe +//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`] +//! it is neither `Sync` nor `Send`. +//! +//! While [`Opaque`] is necessary for C interop, it should be used sparingly +//! and only at FFI boundaries. For QEMU-specific types that need interior +//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. +//! +//! [`BqlCell`]: ../../qemu_api/cell/struct.BqlCell.html +//! [`BqlRefCell`]: ../../qemu_api/cell/struct.BqlRefCell.html +use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull}; + +/// Stores an opaque value that is shared with C code. +/// +/// Often, C structs can changed when calling a C function even if they are +/// behind a shared Rust reference, or they can be initialized lazily and have +/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's +/// strict aliasing rules, which normally prevent mutation through shared +/// references. +/// +/// Wrapping the struct with `Opaque` ensures that the Rust compiler does not +/// assume the usual constraints that Rust structs require, and allows using +/// shared references on the Rust side. +/// +/// `Opaque` is `#[repr(transparent)]`, so that it matches the memory layout +/// of `T`. +#[repr(transparent)] +pub struct Opaque { + value: UnsafeCell>, + // PhantomPinned also allows multiple references to the `Opaque`, i.e. + // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; + // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. + _pin: PhantomPinned, +} + +impl Opaque { + /// Creates a new shared reference from a C pointer + /// + /// # Safety + /// + /// The pointer must be valid, though it need not point to a valid value. + pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self { + let ptr = NonNull::new(ptr).unwrap().cast::(); + // SAFETY: Self is a transparent wrapper over T + unsafe { ptr.as_ref() } + } + + /// Creates a new opaque object with uninitialized contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be initialized and pinned before + /// calling them. + pub const unsafe fn uninit() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::uninit()), + _pin: PhantomPinned, + } + } + + /// Creates a new opaque object with zeroed contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned (and possibly initialized) + /// before calling them. + pub const unsafe fn zeroed() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::zeroed()), + _pin: PhantomPinned, + } + } + + /// Returns a raw mutable pointer to the opaque data. + pub const fn as_mut_ptr(&self) -> *mut T { + UnsafeCell::get(&self.value).cast() + } + + /// Returns a raw pointer to the opaque data. + pub const fn as_ptr(&self) -> *const T { + self.as_mut_ptr().cast_const() + } + + /// Returns a raw pointer to the opaque data that can be passed to a + /// C function as `void *`. + pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { + UnsafeCell::get(&self.value).cast() + } + + /// Converts a raw pointer to the wrapped type. + pub const fn raw_get(slot: *mut Self) -> *mut T { + // Compare with Linux's raw_get method, which goes through an UnsafeCell + // because it takes a *const Self instead. + slot.cast() + } +} + +impl fmt::Debug for Opaque { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut name: String = "Opaque<".to_string(); + name += std::any::type_name::(); + name += ">"; + f.debug_tuple(&name).field(&self.as_ptr()).finish() + } +} + +impl Opaque { + /// Creates a new opaque object with default contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned before calling them. + pub unsafe fn new() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::new(T::default())), + _pin: PhantomPinned, + } + } +} + +/// Annotates [`Self`] as a transparent wrapper for another type. +/// +/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. +/// +/// # Examples +/// +/// ``` +/// # use std::mem::ManuallyDrop; +/// # use common::opaque::Wrapper; +/// #[repr(transparent)] +/// pub struct Example { +/// inner: ManuallyDrop, +/// } +/// +/// unsafe impl Wrapper for Example { +/// type Wrapped = String; +/// } +/// ``` +/// +/// # Safety +/// +/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type, +/// whether directly or indirectly. +/// +/// # Methods +/// +/// By convention, types that implement Wrapper also implement the following +/// methods: +/// +/// ```ignore +/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self; +/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped; +/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped; +/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped; +/// ``` +/// +/// They are not defined here to allow them to be `const`. +/// +/// [`qemu_api_macros::Wrapper`]: ../../qemu_api_macros/derive.Wrapper.html +pub unsafe trait Wrapper { + type Wrapped; +} + +unsafe impl Wrapper for Opaque { + type Wrapped = T; +} diff --git a/rust/common/src/uninit.rs b/rust/common/src/uninit.rs new file mode 100644 index 0000000000..e7f9fcd2e3 --- /dev/null +++ b/rust/common/src/uninit.rs @@ -0,0 +1,85 @@ +//! Access fields of a [`MaybeUninit`] + +use std::{ + mem::MaybeUninit, + ops::{Deref, DerefMut}, +}; + +pub struct MaybeUninitField<'a, T, U> { + parent: &'a mut MaybeUninit, + child: *mut U, +} + +impl<'a, T, U> MaybeUninitField<'a, T, U> { + #[doc(hidden)] + pub const fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { + MaybeUninitField { parent, child } + } + + /// Return a constant pointer to the containing object of the field. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub const fn parent(f: &Self) -> *const T { + f.parent.as_ptr() + } + + /// Return a mutable pointer to the containing object. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub const fn parent_mut(f: &mut Self) -> *mut T { + f.parent.as_mut_ptr() + } +} + +impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { + type Target = MaybeUninit; + + fn deref(&self) -> &MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &*(self.child.cast()) } + } +} + +impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { + fn deref_mut(&mut self) -> &mut MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &mut *(self.child.cast()) } + } +} + +/// ``` +/// #[derive(Debug)] +/// struct S { +/// x: u32, +/// y: u32, +/// } +/// +/// # use std::mem::MaybeUninit; +/// # use common::{assert_match, uninit_field_mut}; +/// +/// let mut s: MaybeUninit = MaybeUninit::zeroed(); +/// uninit_field_mut!(s, x).write(5); +/// let s = unsafe { s.assume_init() }; +/// assert_match!(s, S { x: 5, y: 0 }); +/// ``` +#[macro_export] +macro_rules! uninit_field_mut { + ($container:expr, $($field:tt)+) => {{ + let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container; + let container_ptr__ = container__.as_mut_ptr(); + + // SAFETY: the container is not used directly, only through a MaybeUninit<>, + // so the safety is delegated to the caller and to final invocation of + // assume_init() + let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) }; + $crate::uninit::MaybeUninitField::new(container__, target__) + }}; +} diff --git a/rust/common/src/zeroable.rs b/rust/common/src/zeroable.rs new file mode 100644 index 0000000000..fd056deb1f --- /dev/null +++ b/rust/common/src/zeroable.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Defines a trait for structs that can be safely initialized with zero bytes. + +/// Encapsulates the requirement that +/// `MaybeUninit::::zeroed().assume_init()` does not cause undefined +/// behavior. +/// +/// # Safety +/// +/// Do not add this trait to a type unless all-zeroes is a valid value for the +/// type. In particular, raw pointers can be zero, but references and +/// `NonNull` cannot. +pub unsafe trait Zeroable: Default { + /// Return a value of Self whose memory representation consists of all + /// zeroes, with the possible exclusion of padding bytes. + const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; +} diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 88ef110507..6d15f107df 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -16,6 +16,7 @@ rust-version.workspace = true bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } bits = { path = "../../../bits" } +common = { path = "../../../common" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 2a1be329ab..c4a9f531f7 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -7,7 +7,8 @@ _libpl011_rs = static_library( bilge_rs, bilge_impl_rs, bits_rs, - qemu_api, + common_rs, + qemu_api_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 8411db8d00..b4aa6c45f8 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -4,6 +4,7 @@ use std::{ffi::CStr, mem::size_of}; +use common::{static_assert, uninit_field_mut}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, impl_vmstate_struct, @@ -14,9 +15,7 @@ use qemu_api::{ prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField, ParentInit}, - static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, - uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index ac5df23c1d..ba7354f07e 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +common = { path = "../../../common" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index c2d7c0532c..c91d330439 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -4,7 +4,8 @@ _libhpet_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', dependencies: [ - qemu_api, + common_rs, + qemu_api_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index dd5326a40d..72375d3155 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -10,6 +10,7 @@ use std::{ slice::from_ref, }; +use common::{bitops::IntegerExt, uninit_field_mut}; use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, @@ -27,7 +28,6 @@ use qemu_api::{ qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, - uninit_field_mut, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index 619d662ee1..0605225fbb 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -4,7 +4,8 @@ use std::ptr::addr_of_mut; -use qemu_api::{cell::bql_locked, zeroable::Zeroable}; +use common::Zeroable; +use qemu_api::cell::bql_locked; /// Each `HPETState` represents a Event Timer Block. The v1 spec supports /// up to 8 blocks. QEMU only uses 1 block (in PC machine). diff --git a/rust/meson.build b/rust/meson.build index 331f11b7e7..402f8d6600 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -22,6 +22,7 @@ qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) genrs = [] +subdir('common') subdir('qemu-api-macros') subdir('bits') subdir('qemu-api') diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index a65a7ce2fe..49003a4780 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -97,7 +97,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); ::qemu_api::module_init! { @@ -125,20 +125,20 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result::Wrapped; + unsafe impl ::common::opaque::Wrapper for #name { + type Wrapped = <#typ as ::common::opaque::Wrapper>::Wrapped; } impl #name { - pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); unsafe { ptr.as_ref() } } - pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { self.0.as_mut_ptr() } - pub const fn as_ptr(&self) -> *const ::Wrapped { + pub const fn as_ptr(&self) -> *const ::Wrapped { self.0.as_ptr() } @@ -146,7 +146,7 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result *mut ::Wrapped { + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { slot.cast() } } @@ -282,7 +282,7 @@ fn derive_device_or_error(input: DeriveInput) -> Result::ParentType> diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index c5ed78035b..2e0e204491 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -14,6 +14,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +common = { path = "../common" } qemu_api_macros = { path = "../qemu-api-macros" } anyhow = { workspace = true } foreign = { workspace = true } diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 062009f161..64af3caef5 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -52,13 +52,9 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', - 'src/assertions.rs', 'src/bindings.rs', - 'src/bitops.rs', - 'src/callbacks.rs', 'src/cell.rs', 'src/chardev.rs', - 'src/errno.rs', 'src/error.rs', 'src/irq.rs', 'src/log.rs', @@ -69,23 +65,21 @@ _qemu_api_rs = static_library( 'src/qom.rs', 'src/sysbus.rs', 'src/timer.rs', - 'src/uninit.rs', 'src/vmstate.rs', - 'src/zeroable.rs', ], {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, + dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, qom, hwcore, chardev, migration], ) rust.test('rust-qemu-api-tests', _qemu_api_rs, suite: ['unit', 'rust']) -qemu_api = declare_dependency(link_with: [_qemu_api_rs], +qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) # Doctests are essentially integration tests, so they need the same dependencies. @@ -94,7 +88,7 @@ qemu_api = declare_dependency(link_with: [_qemu_api_rs], rust.doctest('rust-qemu-api-doctests', _qemu_api_rs, protocol: 'rust', - dependencies: qemu_api, + dependencies: [qemu_api_rs], suite: ['doc', 'rust']) test('rust-qemu-api-integration', @@ -104,7 +98,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [qemu_api]), + dependencies: [common_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs deleted file mode 100644 index e4fe23b674..0000000000 --- a/rust/qemu-api/src/assertions.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2024, Red Hat Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -#![doc(hidden)] -//! This module provides macros to check the equality of types and -//! the type of `struct` fields. This can be useful to ensure that -//! types match the expectations of C code. -//! -//! Documentation is hidden because it only exposes macros, which -//! are exported directly from `qemu_api`. - -// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 -// (stackoverflow answers are released under MIT license). - -#[doc(hidden)] -pub trait EqType { - type Itself; -} - -impl EqType for T { - type Itself = T; -} - -/// Assert that two types are the same. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::assert_same_type; -/// # use std::ops::Deref; -/// assert_same_type!(u32, u32); -/// assert_same_type!( as Deref>::Target, u32); -/// ``` -/// -/// Different types will cause a compile failure -/// -/// ```compile_fail -/// # use qemu_api::assert_same_type; -/// assert_same_type!(&Box, &u32); -/// ``` -#[macro_export] -macro_rules! assert_same_type { - ($t1:ty, $t2:ty) => { - const _: () = { - #[allow(unused)] - fn assert_same_type(v: $t1) { - fn types_must_be_equal(_: T) - where - T: $crate::assertions::EqType, - { - } - types_must_be_equal::<_, $t2>(v); - } - }; - }; -} - -/// Assert that a field of a struct has the given type. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::assert_field_type; -/// pub struct A { -/// field1: u32, -/// } -/// -/// assert_field_type!(A, field1, u32); -/// ``` -/// -/// Different types will cause a compile failure -/// -/// ```compile_fail -/// # use qemu_api::assert_field_type; -/// # pub struct A { field1: u32 } -/// assert_field_type!(A, field1, i32); -/// ``` -#[macro_export] -macro_rules! assert_field_type { - (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { - const _: () = { - #[allow(unused)] - const fn assert_field_type($param_name: &$t) { - const fn types_must_be_equal(_: &T) - where - T: $crate::assertions::EqType, - { - } - types_must_be_equal::<_, $ti>(&$($field)*); - } - }; - }; - - ($t:ty, $i:tt, $ti:ty) => { - $crate::assert_field_type!(@internal v, $ti, $t, v.$i); - }; -} - -/// Assert that an expression matches a pattern. This can also be -/// useful to compare enums that do not implement `Eq`. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::assert_match; -/// // JoinHandle does not implement `Eq`, therefore the result -/// // does not either. -/// let result: Result, u32> = Err(42); -/// assert_match!(result, Err(42)); -/// ``` -#[macro_export] -macro_rules! assert_match { - ($a:expr, $b:pat) => { - assert!( - match $a { - $b => true, - _ => false, - }, - "{} = {:?} does not match {}", - stringify!($a), - $a, - stringify!($b) - ); - }; -} - -/// Assert at compile time that an expression is true. This is similar -/// to `const { assert!(...); }` but it works outside functions, as well as -/// on versions of Rust before 1.79. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::static_assert; -/// static_assert!("abc".len() == 3); -/// ``` -/// -/// ```compile_fail -/// # use qemu_api::static_assert; -/// static_assert!("abc".len() == 2); // does not compile -/// ``` -#[macro_export] -macro_rules! static_assert { - ($x:expr) => { - const _: () = assert!($x); - }; -} diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index b8104dea8b..3acdd903b5 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -20,6 +20,8 @@ //! `bindgen`-generated declarations. +use common::Zeroable; + #[cfg(MESON)] include!("bindings.inc.rs"); @@ -56,3 +58,22 @@ unsafe impl Sync for VMStateField {} unsafe impl Send for VMStateInfo {} unsafe impl Sync for VMStateInfo {} + +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for crate::bindings::VMStateFlags { + fn default() -> Self { + Self(0) + } +} + +unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} +unsafe impl Zeroable for crate::bindings::Property {} +unsafe impl Zeroable for crate::bindings::VMStateFlags {} +unsafe impl Zeroable for crate::bindings::VMStateField {} +unsafe impl Zeroable for crate::bindings::VMStateDescription {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} +unsafe impl Zeroable for crate::bindings::MemTxAttrs {} +unsafe impl Zeroable for crate::bindings::CharBackend {} diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs deleted file mode 100644 index b1e3a530ab..0000000000 --- a/rust/qemu-api/src/bitops.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -//! This module provides bit operation extensions to integer types. -//! It is usually included via the `qemu_api` prelude. - -use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, - Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, -}; - -/// Trait for extensions to integer types -pub trait IntegerExt: - Add + AddAssign + - BitAnd + BitAndAssign + - BitOr + BitOrAssign + - BitXor + BitXorAssign + - Copy + - Div + DivAssign + - Eq + - Mul + MulAssign + - Not + Ord + PartialOrd + - Rem + RemAssign + - Shl + ShlAssign + - Shl + ShlAssign + // add more as needed - Shr + ShrAssign + - Shr + ShrAssign // add more as needed -{ - const BITS: u32; - const MAX: Self; - const MIN: Self; - const ONE: Self; - const ZERO: Self; - - #[inline] - #[must_use] - fn bit(start: u32) -> Self - { - debug_assert!(start < Self::BITS); - - Self::ONE << start - } - - #[inline] - #[must_use] - fn mask(start: u32, length: u32) -> Self - { - /* FIXME: Implement a more elegant check with error handling support? */ - debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start); - - (Self::MAX >> (Self::BITS - length)) << start - } - - #[inline] - #[must_use] - fn deposit(self, start: u32, length: u32, - fieldval: U) -> Self - where Self: From - { - debug_assert!(length <= U::BITS); - - let mask = Self::mask(start, length); - (self & !mask) | ((Self::from(fieldval) << start) & mask) - } - - #[inline] - #[must_use] - fn extract(self, start: u32, length: u32) -> Self - { - let mask = Self::mask(start, length); - (self & mask) >> start - } -} - -macro_rules! impl_num_ext { - ($type:ty) => { - impl IntegerExt for $type { - const BITS: u32 = <$type>::BITS; - const MAX: Self = <$type>::MAX; - const MIN: Self = <$type>::MIN; - const ONE: Self = 1; - const ZERO: Self = 0; - } - }; -} - -impl_num_ext!(u8); -impl_num_ext!(u16); -impl_num_ext!(u32); -impl_num_ext!(u64); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_deposit() { - assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15); - assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15); - } - - #[test] - fn test_extract() { - assert_eq!(15u32.extract(2, 4), 3); - } - - #[test] - fn test_bit() { - assert_eq!(u8::bit(7), 128); - assert_eq!(u32::bit(16), 0x10000); - } - - #[test] - fn test_mask() { - assert_eq!(u8::mask(7, 1), 128); - assert_eq!(u32::mask(8, 8), 0xff00); - } -} diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs deleted file mode 100644 index dbe2305f50..0000000000 --- a/rust/qemu-api/src/callbacks.rs +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: MIT - -//! Utility functions to deal with callbacks from C to Rust. - -use std::{mem, ptr::NonNull}; - -/// Trait for functions (types implementing [`Fn`]) that can be used as -/// callbacks. These include both zero-capture closures and function pointers. -/// -/// In Rust, calling a function through the `Fn` trait normally requires a -/// `self` parameter, even though for zero-sized functions (including function -/// pointers) the type itself contains all necessary information to call the -/// function. This trait provides a `call` function that doesn't require `self`, -/// allowing zero-sized functions to be called using only their type. -/// -/// This enables zero-sized functions to be passed entirely through generic -/// parameters and resolved at compile-time. A typical use is a function -/// receiving an unused parameter of generic type `F` and calling it via -/// `F::call` or passing it to another function via `func::`. -/// -/// QEMU uses this trick to create wrappers to C callbacks. The wrappers -/// are needed to convert an opaque `*mut c_void` into a Rust reference, -/// but they only have a single opaque that they can use. The `FnCall` -/// trait makes it possible to use that opaque for `self` or any other -/// reference: -/// -/// ```ignore -/// // The compiler creates a new `rust_bh_cb` wrapper for each function -/// // passed to `qemu_bh_schedule_oneshot` below. -/// unsafe extern "C" fn rust_bh_cb FnCall<(&'a T,)>>( -/// opaque: *mut c_void, -/// ) { -/// // SAFETY: the opaque was passed as a reference to `T`. -/// F::call((unsafe { &*(opaque.cast::()) }, )) -/// } -/// -/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`. -/// // Using a reference allows usage in const context. -/// fn qemu_bh_schedule_oneshot FnCall<(&'a T,)>>(_f: &F, opaque: &T) { -/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; -/// unsafe { -/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void) -/// } -/// } -/// ``` -/// -/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore -/// compiled to a separate function ("monomorphization"). If you wanted -/// to pass `self` as the opaque value, the generic parameters would be -/// `rust_bh_cb::`. -/// -/// `Args` is a tuple type whose types are the arguments of the function, -/// while `R` is the returned type. -/// -/// # Examples -/// -/// ``` -/// # use qemu_api::callbacks::FnCall; -/// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { -/// F::call((s,)) -/// } -/// -/// let s: String = call_it(&str::to_owned, "hello world"); -/// assert_eq!(s, "hello world"); -/// ``` -/// -/// Note that the compiler will produce a different version of `call_it` for -/// each function that is passed to it. Therefore the argument is not really -/// used, except to decide what is `F` and what `F::call` does. -/// -/// Attempting to pass a non-zero-sized closure causes a compile-time failure: -/// -/// ```compile_fail -/// # use qemu_api::callbacks::FnCall; -/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { -/// # F::call((s,)) -/// # } -/// let x: &'static str = "goodbye world"; -/// call_it(&move |_| String::from(x), "hello workd"); -/// ``` -/// -/// `()` can be used to indicate "no function": -/// -/// ``` -/// # use qemu_api::callbacks::FnCall; -/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { -/// if F::IS_SOME { -/// Some(F::call((s,))) -/// } else { -/// None -/// } -/// } -/// -/// assert!(optional(&(), "hello world").is_none()); -/// ``` -/// -/// Invoking `F::call` will then be a run-time error. -/// -/// ```should_panic -/// # use qemu_api::callbacks::FnCall; -/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { -/// # F::call((s,)) -/// # } -/// let s: String = call_it(&(), "hello world"); // panics -/// ``` -/// -/// # Safety -/// -/// Because `Self` is a zero-sized type, all instances of the type are -/// equivalent. However, in addition to this, `Self` must have no invariants -/// that could be violated by creating a reference to it. -/// -/// This is always true for zero-capture closures and function pointers, as long -/// as the code is able to name the function in the first place. -pub unsafe trait FnCall: 'static + Sync + Sized { - /// `true` if `Self` is an actual function type and not `()`. - /// - /// # Examples - /// - /// You can use `IS_SOME` to catch this at compile time: - /// - /// ```compile_fail - /// # use qemu_api::callbacks::FnCall; - /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { - /// const { assert!(F::IS_SOME) } - /// F::call((s,)) - /// } - /// - /// let s: String = call_it((), "hello world"); // does not compile - /// ``` - const IS_SOME: bool; - - /// `false` if `Self` is an actual function type, `true` if it is `()`. - fn is_none() -> bool { - !Self::IS_SOME - } - - /// `true` if `Self` is an actual function type, `false` if it is `()`. - fn is_some() -> bool { - Self::IS_SOME - } - - /// Call the function with the arguments in args. - fn call(a: Args) -> R; -} - -/// `()` acts as a "null" callback. Using `()` and `function` is nicer -/// than `None` and `Some(function)`, because the compiler is unable to -/// infer the type of just `None`. Therefore, the trait itself acts as the -/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. -unsafe impl FnCall for () { - const IS_SOME: bool = false; - - /// Call the function with the arguments in args. - fn call(_a: Args) -> R { - panic!("callback not specified") - } -} - -macro_rules! impl_call { - ($($args:ident,)* ) => ( - // SAFETY: because each function is treated as a separate type, - // accessing `FnCall` is only possible in code that would be - // allowed to call the function. - unsafe impl FnCall<($($args,)*), R> for F - where - F: 'static + Sync + Sized + Fn($($args, )*) -> R, - { - const IS_SOME: bool = true; - - #[inline(always)] - fn call(a: ($($args,)*)) -> R { - const { assert!(mem::size_of::() == 0) }; - - // SAFETY: the safety of this method is the condition for implementing - // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, - // see https://github.com/rust-lang/libs-team/issues/292. - let f: &'static F = unsafe { &*NonNull::::dangling().as_ptr() }; - let ($($args,)*) = a; - f($($args,)*) - } - } - ) -} - -impl_call!(_1, _2, _3, _4, _5,); -impl_call!(_1, _2, _3, _4,); -impl_call!(_1, _2, _3,); -impl_call!(_1, _2,); -impl_call!(_1,); -impl_call!(); - -#[cfg(test)] -mod tests { - use super::*; - - // The `_f` parameter is unused but it helps the compiler infer `F`. - fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { - F::call(("hello world",)) - } - - #[test] - fn test_call() { - assert_eq!(do_test_call(&str::to_owned), "hello world") - } - - // The `_f` parameter is unused but it helps the compiler infer `F`. - fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { - assert!(F::is_some()); - } - - #[test] - fn test_is_some() { - do_test_is_some(&str::to_owned); - } -} diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 9943d7286b..d13848df20 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -141,82 +141,13 @@ //! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), //! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The //! thread will panic if these rules are violated or if the BQL is not held. -//! -//! ## Opaque wrappers -//! -//! The cell types from the previous section are useful at the boundaries -//! of code that requires interior mutability. When writing glue code that -//! interacts directly with C structs, however, it is useful to operate -//! at a lower level. -//! -//! C functions often violate Rust's fundamental assumptions about memory -//! safety by modifying memory even if it is shared. Furthermore, C structs -//! often start their life uninitialized and may be populated lazily. -//! -//! For this reason, this module provides the [`Opaque`] type to opt out -//! of Rust's usual guarantees about the wrapped type. Access to the wrapped -//! value is always through raw pointers, obtained via methods like -//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These -//! pointers can then be passed to C functions or dereferenced; both actions -//! require `unsafe` blocks, making it clear where safety guarantees must be -//! manually verified. For example -//! -//! ```ignore -//! unsafe { -//! let state = Opaque::::uninit(); -//! qemu_struct_init(state.as_mut_ptr()); -//! } -//! ``` -//! -//! [`Opaque`] will usually be wrapped one level further, so that -//! bridge methods can be added to the wrapper: -//! -//! ```ignore -//! pub struct MyStruct(Opaque); -//! -//! impl MyStruct { -//! fn new() -> Pin> { -//! let result = Box::pin(unsafe { Opaque::uninit() }); -//! unsafe { qemu_struct_init(result.as_mut_ptr()) }; -//! result -//! } -//! } -//! ``` -//! -//! This pattern of wrapping bindgen-generated types in [`Opaque`] provides -//! several advantages: -//! -//! * The choice of traits to be implemented is not limited by the -//! bindgen-generated code. For example, [`Drop`] can be added without -//! disabling [`Copy`] on the underlying bindgen type -//! -//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper -//! type rather than being automatically derived from the C struct's layout -//! -//! * Methods can be implemented in a separate crate from the bindgen-generated -//! bindings -//! -//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display) -//! implementations can be customized to be more readable than the raw C -//! struct representation -//! -//! The [`Opaque`] type does not include BQL validation; it is possible to -//! assert in the code that the right lock is taken, to use it together -//! with a custom lock guard type, or to let C code take the lock, as -//! appropriate. It is also possible to use it with non-thread-safe -//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`] -//! it is neither `Sync` nor `Send`. -//! -//! While [`Opaque`] is necessary for C interop, it should be used sparingly -//! and only at FFI boundaries. For QEMU-specific types that need interior -//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. use std::{ cell::{Cell, UnsafeCell}, cmp::Ordering, fmt, - marker::{PhantomData, PhantomPinned}, - mem::{self, MaybeUninit}, + marker::PhantomData, + mem, ops::{Deref, DerefMut}, ptr::NonNull, }; @@ -939,165 +870,3 @@ impl fmt::Display for BqlRefMut<'_, T> { (**self).fmt(f) } } - -/// Stores an opaque value that is shared with C code. -/// -/// Often, C structs can changed when calling a C function even if they are -/// behind a shared Rust reference, or they can be initialized lazily and have -/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's -/// strict aliasing rules, which normally prevent mutation through shared -/// references. -/// -/// Wrapping the struct with `Opaque` ensures that the Rust compiler does not -/// assume the usual constraints that Rust structs require, and allows using -/// shared references on the Rust side. -/// -/// `Opaque` is `#[repr(transparent)]`, so that it matches the memory layout -/// of `T`. -#[repr(transparent)] -pub struct Opaque { - value: UnsafeCell>, - // PhantomPinned also allows multiple references to the `Opaque`, i.e. - // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; - // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. - _pin: PhantomPinned, -} - -impl Opaque { - /// Creates a new shared reference from a C pointer - /// - /// # Safety - /// - /// The pointer must be valid, though it need not point to a valid value. - pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self { - let ptr = NonNull::new(ptr).unwrap().cast::(); - // SAFETY: Self is a transparent wrapper over T - unsafe { ptr.as_ref() } - } - - /// Creates a new opaque object with uninitialized contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be initialized and pinned before - /// calling them. - pub const unsafe fn uninit() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::uninit()), - _pin: PhantomPinned, - } - } - - /// Creates a new opaque object with zeroed contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be pinned (and possibly initialized) - /// before calling them. - pub const unsafe fn zeroed() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::zeroed()), - _pin: PhantomPinned, - } - } - - /// Returns a raw mutable pointer to the opaque data. - pub const fn as_mut_ptr(&self) -> *mut T { - UnsafeCell::get(&self.value).cast() - } - - /// Returns a raw pointer to the opaque data. - pub const fn as_ptr(&self) -> *const T { - self.as_mut_ptr().cast_const() - } - - /// Returns a raw pointer to the opaque data that can be passed to a - /// C function as `void *`. - pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { - UnsafeCell::get(&self.value).cast() - } - - /// Converts a raw pointer to the wrapped type. - pub const fn raw_get(slot: *mut Self) -> *mut T { - // Compare with Linux's raw_get method, which goes through an UnsafeCell - // because it takes a *const Self instead. - slot.cast() - } -} - -impl fmt::Debug for Opaque { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut name: String = "Opaque<".to_string(); - name += std::any::type_name::(); - name += ">"; - f.debug_tuple(&name).field(&self.as_ptr()).finish() - } -} - -impl Opaque { - /// Creates a new opaque object with default contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be pinned before calling them. - pub unsafe fn new() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::new(T::default())), - _pin: PhantomPinned, - } - } -} - -/// Annotates [`Self`] as a transparent wrapper for another type. -/// -/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. -/// -/// # Examples -/// -/// ``` -/// # use std::mem::ManuallyDrop; -/// # use qemu_api::cell::Wrapper; -/// #[repr(transparent)] -/// pub struct Example { -/// inner: ManuallyDrop, -/// } -/// -/// unsafe impl Wrapper for Example { -/// type Wrapped = String; -/// } -/// ``` -/// -/// # Safety -/// -/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type, -/// whether directly or indirectly. -/// -/// # Methods -/// -/// By convention, types that implement Wrapper also implement the following -/// methods: -/// -/// ```ignore -/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self; -/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped; -/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped; -/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped; -/// ``` -/// -/// They are not defined here to allow them to be `const`. -pub unsafe trait Wrapper { - type Wrapped; -} - -unsafe impl Wrapper for Opaque { - type Wrapped = T; -} diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index cb27be5256..5a351dcecb 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -18,10 +18,11 @@ use std::{ slice, }; +use common::{callbacks::FnCall, errno, Opaque}; + use crate::{ bindings, - callbacks::FnCall, - cell::{BqlRefMut, Opaque}, + cell::{BqlRefCell, BqlRefMut}, prelude::*, }; diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs deleted file mode 100644 index 507850fe33..0000000000 --- a/rust/qemu-api/src/errno.rs +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Utility functions to convert `errno` to and from -//! [`io::Error`]/[`io::Result`] -//! -//! QEMU C functions often have a "positive success/negative `errno`" calling -//! convention. This module provides functions to portably convert an integer -//! into an [`io::Result`] and back. - -use std::{ - convert::{self, TryFrom}, - io::{self, ErrorKind}, -}; - -/// An `errno` value that can be converted into an [`io::Error`] -pub struct Errno(pub u16); - -// On Unix, from_raw_os_error takes an errno value and OS errors -// are printed using strerror. On Windows however it takes a -// GetLastError() value; therefore we need to convert errno values -// into io::Error by hand. This is the same mapping that the -// standard library uses to retrieve the kind of OS errors -// (`std::sys::pal::unix::decode_error_kind`). -impl From for ErrorKind { - fn from(value: Errno) -> ErrorKind { - use ErrorKind::*; - let Errno(errno) = value; - match i32::from(errno) { - libc::EPERM | libc::EACCES => PermissionDenied, - libc::ENOENT => NotFound, - libc::EINTR => Interrupted, - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - libc::ENOMEM => OutOfMemory, - libc::EEXIST => AlreadyExists, - libc::EINVAL => InvalidInput, - libc::EPIPE => BrokenPipe, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::ENOTCONN => NotConnected, - libc::ENOTSUP => Unsupported, - libc::ETIMEDOUT => TimedOut, - _ => Other, - } - } -} - -// This is used on Windows for all io::Errors, but also on Unix if the -// io::Error does not have a raw OS error. This is the reversed -// mapping of the above; EIO is returned for unknown ErrorKinds. -impl From for Errno { - fn from(value: io::ErrorKind) -> Errno { - use ErrorKind::*; - let errno = match value { - // can be both EPERM or EACCES :( pick one - PermissionDenied => libc::EPERM, - NotFound => libc::ENOENT, - Interrupted => libc::EINTR, - WouldBlock => libc::EAGAIN, - OutOfMemory => libc::ENOMEM, - AlreadyExists => libc::EEXIST, - InvalidInput => libc::EINVAL, - BrokenPipe => libc::EPIPE, - AddrInUse => libc::EADDRINUSE, - AddrNotAvailable => libc::EADDRNOTAVAIL, - ConnectionAborted => libc::ECONNABORTED, - ConnectionRefused => libc::ECONNREFUSED, - ConnectionReset => libc::ECONNRESET, - NotConnected => libc::ENOTCONN, - Unsupported => libc::ENOTSUP, - TimedOut => libc::ETIMEDOUT, - _ => libc::EIO, - }; - Errno(errno as u16) - } -} - -impl From for io::Error { - #[cfg(unix)] - fn from(value: Errno) -> io::Error { - let Errno(errno) = value; - io::Error::from_raw_os_error(errno.into()) - } - - #[cfg(windows)] - fn from(value: Errno) -> io::Error { - let error_kind: ErrorKind = value.into(); - error_kind.into() - } -} - -impl From for Errno { - fn from(value: io::Error) -> Errno { - if cfg!(unix) { - if let Some(errno) = value.raw_os_error() { - return Errno(u16::try_from(errno).unwrap()); - } - } - value.kind().into() - } -} - -impl From for Errno { - fn from(_value: convert::Infallible) -> Errno { - panic!("unreachable") - } -} - -/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] -/// for the "right" set of types. -mod traits { - use super::Errno; - - /// A signed type that can be converted into an - /// [`io::Result`](std::io::Result) - pub trait GetErrno { - /// Unsigned variant of `Self`, used as the type for the `Ok` case. - type Out; - - /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative - fn into_errno_result(self) -> Result; - } - - /// A type that can be taken out of an [`io::Result`](std::io::Result) and - /// converted into "positive success/negative `errno`" convention. - pub trait MergeErrno { - /// Signed variant of `Self`, used as the return type of - /// [`into_neg_errno`](super::into_neg_errno). - type Out: From + std::ops::Neg; - - /// Return `self`, asserting that it is in range - fn map_ok(self) -> Self::Out; - } - - macro_rules! get_errno { - ($t:ty, $out:ty) => { - impl GetErrno for $t { - type Out = $out; - fn into_errno_result(self) -> Result { - match self { - 0.. => Ok(self as $out), - -65535..=-1 => Err(Errno(-self as u16)), - _ => panic!("{self} is not a negative errno"), - } - } - } - }; - } - - get_errno!(i32, u32); - get_errno!(i64, u64); - get_errno!(isize, usize); - - macro_rules! merge_errno { - ($t:ty, $out:ty) => { - impl MergeErrno for $t { - type Out = $out; - fn map_ok(self) -> Self::Out { - self.try_into().unwrap() - } - } - }; - } - - merge_errno!(u8, i32); - merge_errno!(u16, i32); - merge_errno!(u32, i32); - merge_errno!(u64, i64); - - impl MergeErrno for () { - type Out = i32; - fn map_ok(self) -> i32 { - 0 - } - } -} - -use traits::{GetErrno, MergeErrno}; - -/// Convert an integer value into a [`io::Result`]. -/// -/// Positive values are turned into an `Ok` result; negative values -/// are interpreted as negated `errno` and turned into an `Err`. -/// -/// ``` -/// # use qemu_api::errno::into_io_result; -/// # use std::io::ErrorKind; -/// let ok = into_io_result(1i32).unwrap(); -/// assert_eq!(ok, 1u32); -/// -/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM -/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); -/// ``` -/// -/// # Panics -/// -/// Since the result is an unsigned integer, negative values must -/// be close to 0; values that are too far away are considered -/// likely overflows and will panic: -/// -/// ```should_panic -/// # use qemu_api::errno::into_io_result; -/// # #[allow(dead_code)] -/// let err = into_io_result(-0x1234_5678i32); // panic -/// ``` -pub fn into_io_result(value: T) -> io::Result { - value.into_errno_result().map_err(Into::into) -} - -/// Convert a [`Result`] into an integer value, using negative `errno` -/// values to report errors. -/// -/// ``` -/// # use qemu_api::errno::into_neg_errno; -/// # use std::io::{self, ErrorKind}; -/// let ok: io::Result<()> = Ok(()); -/// assert_eq!(into_neg_errno(ok), 0); -/// -/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); -/// assert_eq!(into_neg_errno(err), -22); // -EINVAL -/// ``` -/// -/// Since this module also provides the ability to convert [`io::Error`] -/// to an `errno` value, [`io::Result`] is the most commonly used type -/// for the argument of this function: -/// -/// # Panics -/// -/// Since the result is a signed integer, integer `Ok` values must remain -/// positive: -/// -/// ```should_panic -/// # use qemu_api::errno::into_neg_errno; -/// # use std::io; -/// let err: io::Result = Ok(0x8899_AABB); -/// into_neg_errno(err) // panic -/// # ; -/// ``` -pub fn into_neg_errno>(value: Result) -> T::Out { - match value { - Ok(x) => x.map_ok(), - Err(err) => -T::Out::from(err.into().0), - } -} - -#[cfg(test)] -mod tests { - use std::io::ErrorKind; - - use super::*; - use crate::assert_match; - - #[test] - pub fn test_from_u8() { - let ok: io::Result<_> = Ok(42u8); - assert_eq!(into_neg_errno(ok), 42); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_from_u16() { - let ok: io::Result<_> = Ok(1234u16); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_i32() { - assert_match!(into_io_result(1234i32), Ok(1234)); - - let err = into_io_result(-1i32).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(1)); - assert_match!(err.kind(), ErrorKind::PermissionDenied); - } - - #[test] - pub fn test_from_u32() { - let ok: io::Result<_> = Ok(1234u32); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_i64() { - assert_match!(into_io_result(1234i64), Ok(1234)); - - let err = into_io_result(-22i64).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(22)); - assert_match!(err.kind(), ErrorKind::InvalidInput); - } - - #[test] - pub fn test_from_u64() { - let ok: io::Result<_> = Ok(1234u64); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::InvalidInput.into()); - assert_eq!(into_neg_errno(err), -22); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(6)); - assert_eq!(into_neg_errno(os_err), -6); - } - } - - #[test] - pub fn test_isize() { - assert_match!(into_io_result(1234isize), Ok(1234)); - - let err = into_io_result(-4isize).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(4)); - assert_match!(err.kind(), ErrorKind::Interrupted); - } - - #[test] - pub fn test_from_unit() { - let ok: io::Result<_> = Ok(()); - assert_eq!(into_neg_errno(ok), 0); - - let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); - assert_eq!(into_neg_errno(err), -12); - - if cfg!(unix) { - let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); - assert_eq!(into_neg_errno(os_err), -2); - } - } -} diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs index e114fc4178..8bac3cbec8 100644 --- a/rust/qemu-api/src/error.rs +++ b/rust/qemu-api/src/error.rs @@ -316,10 +316,11 @@ mod tests { use std::ffi::CStr; use anyhow::anyhow; + use common::assert_match; use foreign::OwnedPointer; use super::*; - use crate::{assert_match, bindings}; + use crate::bindings; #[track_caller] fn error_for_test(msg: &CStr) -> OwnedPointer { diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 1526e6f63a..ea6b32848c 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -10,9 +10,10 @@ use std::{ ptr, }; +use common::Opaque; + use crate::{ bindings::{self, qemu_set_irq}, - cell::Opaque, prelude::*, qom::ObjectClass, }; diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index daa2493bb6..6232e39e71 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -13,12 +13,8 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; -pub mod assertions; -pub mod bitops; -pub mod callbacks; pub mod cell; pub mod chardev; -pub mod errno; pub mod error; pub mod irq; pub mod log; @@ -28,9 +24,7 @@ pub mod qdev; pub mod qom; pub mod sysbus; pub mod timer; -pub mod uninit; pub mod vmstate; -pub mod zeroable; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs index a441b8c1f2..d07f6272dc 100644 --- a/rust/qemu-api/src/log.rs +++ b/rust/qemu-api/src/log.rs @@ -8,7 +8,9 @@ use std::{ ptr::NonNull, }; -use crate::{bindings, errno}; +use common::errno; + +use crate::bindings; #[repr(u32)] /// Represents specific error categories within QEMU's logging system. diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index e40fad6cf1..f790cb5fd2 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -10,14 +10,11 @@ use std::{ }; pub use bindings::{hwaddr, MemTxAttrs}; +use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; use crate::{ bindings::{self, device_endian, memory_region_init_io}, - callbacks::FnCall, - cell::Opaque, prelude::*, - uninit::MaybeUninitField, - zeroable::Zeroable, }; pub struct MemoryRegionOps( diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 8f9e23ee2c..dcfe71497f 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -4,13 +4,9 @@ //! Commonly used traits and types for QEMU. -pub use crate::bitops::IntegerExt; - pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; -pub use crate::errno; - pub use crate::log_mask_ln; pub use crate::qdev::DeviceMethods; @@ -19,8 +15,8 @@ pub use crate::qom::InterfaceType; pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; -pub use crate::qom::ObjectDeref; pub use crate::qom::ObjectClassMethods; +pub use crate::qom::ObjectDeref; pub use crate::qom::ObjectMethods; pub use crate::qom::ObjectType; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index e2b4121cac..8be125fae4 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -10,11 +10,11 @@ use std::{ }; pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; +use common::{callbacks::FnCall, Opaque}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - callbacks::FnCall, - cell::{bql_locked, Opaque}, + cell::bql_locked, chardev::Chardev, error::{Error, Result}, impl_vmstate_c_struct, @@ -246,7 +246,7 @@ macro_rules! define_property { bitnr: $bitnr, set_default: true, defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { @@ -257,7 +257,7 @@ macro_rules! define_property { offset: ::std::mem::offset_of!($state, $field) as isize, set_default: true, defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { @@ -267,7 +267,7 @@ macro_rules! define_property { info: $prop, offset: ::std::mem::offset_of!($state, $field) as isize, set_default: false, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; } diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 12c6fc6752..49b4f03ccf 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -102,13 +102,14 @@ use std::{ }; pub use bindings::ObjectClass; +use common::Opaque; use crate::{ bindings::{ self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, - cell::{bql_locked, Opaque}, + cell::bql_locked, impl_vmstate_pointer, }; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index e92502a8fe..4a5b4cbbf6 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -7,10 +7,11 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; +use common::Opaque; use crate::{ bindings, - cell::{bql_locked, Opaque}, + cell::bql_locked, irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index 1e639eaf22..383e1a6e77 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -7,10 +7,10 @@ use std::{ pin::Pin, }; -use crate::{ - bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType}, - callbacks::FnCall, - cell::Opaque, +use common::{callbacks::FnCall, Opaque}; + +use crate::bindings::{ + self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType, }; /// A safe wrapper around [`bindings::QEMUTimer`]. diff --git a/rust/qemu-api/src/uninit.rs b/rust/qemu-api/src/uninit.rs deleted file mode 100644 index b0a829729d..0000000000 --- a/rust/qemu-api/src/uninit.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Access fields of a [`MaybeUninit`] - -use std::{ - mem::MaybeUninit, - ops::{Deref, DerefMut}, -}; - -pub struct MaybeUninitField<'a, T, U> { - parent: &'a mut MaybeUninit, - child: *mut U, -} - -impl<'a, T, U> MaybeUninitField<'a, T, U> { - #[doc(hidden)] - pub const fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { - MaybeUninitField { parent, child } - } - - /// Return a constant pointer to the containing object of the field. - /// - /// Because the `MaybeUninitField` remembers the containing object, - /// it is possible to use it in foreign APIs that initialize the - /// child. - pub const fn parent(f: &Self) -> *const T { - f.parent.as_ptr() - } - - /// Return a mutable pointer to the containing object. - /// - /// Because the `MaybeUninitField` remembers the containing object, - /// it is possible to use it in foreign APIs that initialize the - /// child. - pub const fn parent_mut(f: &mut Self) -> *mut T { - f.parent.as_mut_ptr() - } -} - -impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { - type Target = MaybeUninit; - - fn deref(&self) -> &MaybeUninit { - // SAFETY: self.child was obtained by dereferencing a valid mutable - // reference; the content of the memory may be invalid or uninitialized - // but MaybeUninit<_> makes no assumption on it - unsafe { &*(self.child.cast()) } - } -} - -impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { - fn deref_mut(&mut self) -> &mut MaybeUninit { - // SAFETY: self.child was obtained by dereferencing a valid mutable - // reference; the content of the memory may be invalid or uninitialized - // but MaybeUninit<_> makes no assumption on it - unsafe { &mut *(self.child.cast()) } - } -} - -/// ``` -/// #[derive(Debug)] -/// struct S { -/// x: u32, -/// y: u32, -/// } -/// -/// # use std::mem::MaybeUninit; -/// # use qemu_api::{assert_match, uninit_field_mut}; -/// -/// let mut s: MaybeUninit = MaybeUninit::zeroed(); -/// uninit_field_mut!(s, x).write(5); -/// let s = unsafe { s.assume_init() }; -/// assert_match!(s, S { x: 5, y: 0 }); -/// ``` -#[macro_export] -macro_rules! uninit_field_mut { - ($container:expr, $($field:tt)+) => {{ - let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container; - let container_ptr__ = container__.as_mut_ptr(); - - // SAFETY: the container is not used directly, only through a MaybeUninit<>, - // so the safety is delegated to the caller and to final invocation of - // assume_init() - let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) }; - $crate::uninit::MaybeUninitField::new(container__, target__) - }}; -} diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index ce42b031bc..06aac3a73f 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -35,14 +35,15 @@ use std::{ ptr::{addr_of, NonNull}, }; -pub use crate::bindings::{MigrationPriority, VMStateField}; -use crate::{ - bindings::{self, VMStateFlags}, +use common::{ callbacks::FnCall, errno::{into_neg_errno, Errno}, - zeroable::Zeroable, + Zeroable, }; +use crate::bindings::{self, VMStateFlags}; +pub use crate::bindings::{MigrationPriority, VMStateField}; + /// This macro is used to call a function with a generic argument bound /// to the type of a field. The function must take a /// [`PhantomData`]`` argument; `T` is the type of @@ -271,7 +272,7 @@ macro_rules! impl_vmstate_transparent { impl_vmstate_transparent!(std::cell::Cell where T: VMState); impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); impl_vmstate_transparent!(std::pin::Pin where T: VMState); -impl_vmstate_transparent!(crate::cell::Opaque where T: VMState); +impl_vmstate_transparent!(::common::Opaque where T: VMState); #[macro_export] macro_rules! impl_vmstate_bitsized { @@ -324,7 +325,7 @@ macro_rules! impl_vmstate_c_struct { vmsd: ::std::ptr::addr_of!($vmsd), size: ::std::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..$crate::zeroable::Zeroable::ZERO + ..common::zeroable::Zeroable::ZERO }; } }; @@ -367,7 +368,7 @@ macro_rules! vmstate_unused { size: $size, info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, flags: $crate::bindings::VMStateFlags::VMS_BUFFER, - ..$crate::zeroable::Zeroable::ZERO + ..::common::Zeroable::ZERO } }}; } @@ -390,7 +391,7 @@ pub type VMSFieldExistCb = unsafe extern "C" fn( #[macro_export] macro_rules! vmstate_exist_fn { ($struct_name:ty, $test_fn:expr) => {{ - const fn test_cb_builder__ $crate::callbacks::FnCall<(&'a T, u8), bool>>( + const fn test_cb_builder__ ::common::callbacks::FnCall<(&'a T, u8), bool>>( _phantom: ::core::marker::PhantomData, ) -> $crate::vmstate::VMSFieldExistCb { const { assert!(F::IS_SOME) }; @@ -414,7 +415,7 @@ macro_rules! vmstate_fields { $($field),*, $crate::bindings::VMStateField { flags: $crate::bindings::VMStateFlags::VMS_END, - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } ]; _FIELDS.as_ptr() @@ -433,7 +434,7 @@ macro_rules! vmstate_validate { | $crate::bindings::VMStateFlags::VMS_ARRAY.0, ), num: 0, // 0 elements: no data, only run test_fn callback - ..$crate::zeroable::Zeroable::ZERO + ..::common::zeroable::Zeroable::ZERO } }; } @@ -455,7 +456,7 @@ macro_rules! impl_vmstate_struct { vmsd: ::core::ptr::addr_of!(*VMSD), size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..$crate::zeroable::Zeroable::ZERO + ..common::Zeroable::ZERO } }; } diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs deleted file mode 100644 index d8239d0856..0000000000 --- a/rust/qemu-api/src/zeroable.rs +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Defines a trait for structs that can be safely initialized with zero bytes. - -/// Encapsulates the requirement that -/// `MaybeUninit::::zeroed().assume_init()` does not cause undefined -/// behavior. -/// -/// # Safety -/// -/// Do not add this trait to a type unless all-zeroes is a valid value for the -/// type. In particular, raw pointers can be zero, but references and -/// `NonNull` cannot. -pub unsafe trait Zeroable: Default { - /// Return a value of Self whose memory representation consists of all - /// zeroes, with the possible exclusion of padding bytes. - const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; -} - -// bindgen does not derive Default here -#[allow(clippy::derivable_impls)] -impl Default for crate::bindings::VMStateFlags { - fn default() -> Self { - Self(0) - } -} - -unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::Property {} -unsafe impl Zeroable for crate::bindings::VMStateFlags {} -unsafe impl Zeroable for crate::bindings::VMStateField {} -unsafe impl Zeroable for crate::bindings::VMStateDescription {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} -unsafe impl Zeroable for crate::bindings::MemTxAttrs {} -unsafe impl Zeroable for crate::bindings::CharBackend {} diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 2c0670ba0e..d9e5bcc498 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -9,12 +9,13 @@ use std::{ slice, }; +use common::Opaque; use qemu_api::{ bindings::{ vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, }, - cell::{BqlCell, Opaque}, + cell::BqlCell, impl_vmstate_forward, impl_vmstate_struct, vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, -- cgit 1.4.1 From 59869b4d58854190f09a79c5392d60fdc0b55d45 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:50 +0200 Subject: rust: split "util" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-7-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 17 +- rust/Cargo.toml | 5 +- rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 7 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 6 +- rust/meson.build | 1 + rust/qemu-api-macros/src/lib.rs | 2 +- rust/qemu-api-macros/src/tests.rs | 2 +- rust/qemu-api/Cargo.toml | 4 +- rust/qemu-api/build.rs | 50 +---- rust/qemu-api/meson.build | 14 +- rust/qemu-api/src/bindings.rs | 1 + rust/qemu-api/src/error.rs | 417 -------------------------------------- rust/qemu-api/src/lib.rs | 6 - rust/qemu-api/src/log.rs | 151 -------------- rust/qemu-api/src/module.rs | 43 ---- rust/qemu-api/src/prelude.rs | 2 - rust/qemu-api/src/qdev.rs | 4 +- rust/qemu-api/src/sysbus.rs | 2 +- rust/qemu-api/src/timer.rs | 125 ------------ rust/qemu-api/src/vmstate.rs | 2 +- rust/qemu-api/tests/tests.rs | 2 +- rust/qemu-api/wrapper.h | 6 - rust/util/Cargo.toml | 23 +++ rust/util/build.rs | 49 +++++ rust/util/meson.build | 55 +++++ rust/util/src/bindings.rs | 25 +++ rust/util/src/error.rs | 416 +++++++++++++++++++++++++++++++++++++ rust/util/src/lib.rs | 9 + rust/util/src/log.rs | 151 ++++++++++++++ rust/util/src/module.rs | 43 ++++ rust/util/src/timer.rs | 125 ++++++++++++ rust/util/wrapper.h | 32 +++ 37 files changed, 969 insertions(+), 833 deletions(-) mode change 100644 => 120000 rust/qemu-api/build.rs delete mode 100644 rust/qemu-api/src/error.rs delete mode 100644 rust/qemu-api/src/log.rs delete mode 100644 rust/qemu-api/src/module.rs delete mode 100644 rust/qemu-api/src/timer.rs create mode 100644 rust/util/Cargo.toml create mode 100644 rust/util/build.rs create mode 100644 rust/util/meson.build create mode 100644 rust/util/src/bindings.rs create mode 100644 rust/util/src/error.rs create mode 100644 rust/util/src/lib.rs create mode 100644 rust/util/src/log.rs create mode 100644 rust/util/src/module.rs create mode 100644 rust/util/src/timer.rs create mode 100644 rust/util/wrapper.h (limited to 'rust/qemu-api/src/timer.rs') diff --git a/MAINTAINERS b/MAINTAINERS index cee5a34206..3d7b47873f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3519,6 +3519,7 @@ F: rust/common/ F: rust/qemu-api F: rust/qemu-api-macros F: rust/rustfmt.toml +F: rust/util/ F: scripts/get-wraps-from-cargo-registry.py Rust-related patches CC here diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 71e8c7ed62..757c03cbde 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -73,6 +73,7 @@ dependencies = [ "common", "qemu_api", "qemu_api_macros", + "util", ] [[package]] @@ -100,6 +101,7 @@ dependencies = [ "common", "qemu_api", "qemu_api_macros", + "util", ] [[package]] @@ -138,11 +140,9 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ - "anyhow", "common", - "foreign", - "libc", "qemu_api_macros", + "util", ] [[package]] @@ -180,6 +180,17 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "util" +version = "0.1.0" +dependencies = [ + "anyhow", + "common", + "foreign", + "libc", + "qemu_api_macros", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c0426d4243..cfdd535e3b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,7 @@ members = [ "qemu-api", "hw/char/pl011", "hw/timer/hpet", + "util", ] [workspace.package] @@ -24,9 +25,7 @@ foreign = "~0.3.1" libc = "0.2.162" [workspace.lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = [ - 'cfg(MESON)', -] } +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] } # Occasionally, we may need to silence warnings and clippy lints that # were only introduced in newer Rust compiler versions. Do not croak diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 6d15f107df..0cf9943fe8 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -17,6 +17,7 @@ bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } bits = { path = "../../../bits" } common = { path = "../../../common" } +util = { path = "../../../util" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index c4a9f531f7..8a931a4d03 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -9,6 +9,7 @@ _libpl011_rs = static_library( bits_rs, common_rs, qemu_api_rs, + util_rs, qemu_api_macros, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index b4aa6c45f8..ab38d57fc4 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -9,8 +9,6 @@ use qemu_api::{ chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, impl_vmstate_struct, irq::{IRQState, InterruptSource}, - log::Log, - log_mask_ln, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, @@ -19,6 +17,7 @@ use qemu_api::{ vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused, }; +use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; @@ -172,7 +171,7 @@ impl ObjectImpl for PL011State { impl DeviceImpl for PL011State { const VMSTATE: Option> = Some(VMSTATE_PL011); - const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); + const REALIZE: Option util::Result<()>> = Some(Self::realize); } impl ResettablePhasesImpl for PL011State { @@ -623,7 +622,7 @@ impl PL011State { } } - fn realize(&self) -> qemu_api::Result<()> { + fn realize(&self) -> util::Result<()> { self.char_backend .enable_handlers(self, Self::can_receive, Self::receive, Self::event); Ok(()) diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index ba7354f07e..dd9a5ed3d4 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true [dependencies] common = { path = "../../../common" } +util = { path = "../../../util" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index c91d330439..ca09660bf4 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -6,6 +6,7 @@ _libhpet_rs = static_library( dependencies: [ common_rs, qemu_api_rs, + util_rs, qemu_api_macros, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 72375d3155..2be180fded 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -27,10 +27,10 @@ use qemu_api::{ qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, - timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, }; +use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; @@ -728,7 +728,7 @@ impl HPETState { } } - fn realize(&self) -> qemu_api::Result<()> { + fn realize(&self) -> util::Result<()> { if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS { Err(format!( "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}" @@ -1013,7 +1013,7 @@ unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { impl DeviceImpl for HPETState { const VMSTATE: Option> = Some(VMSTATE_HPET); - const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); + const REALIZE: Option util::Result<()>> = Some(Self::realize); } impl ResettablePhasesImpl for HPETState { diff --git a/rust/meson.build b/rust/meson.build index 402f8d6600..a9d715e6e9 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -25,6 +25,7 @@ genrs = [] subdir('common') subdir('qemu-api-macros') subdir('bits') +subdir('util') subdir('qemu-api') subdir('hw') diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 49003a4780..67650a9a26 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -100,7 +100,7 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); - ::qemu_api::module_init! { + ::util::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); } diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index ef2806368d..8e71ac6e67 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -170,7 +170,7 @@ fn test_derive_object() { _unused, ::qemu_api::qom::ParentField<::ParentType> ); - ::qemu_api::module_init! { + ::util::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&::TYPE_INFO); } diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 2e0e204491..fbfb894421 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,10 +15,8 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } +util = { path = "../util" } qemu_api_macros = { path = "../qemu-api-macros" } -anyhow = { workspace = true } -foreign = { workspace = true } -libc = { workspace = true } [features] default = ["debug_cell"] diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs deleted file mode 100644 index 5654d1d562..0000000000 --- a/rust/qemu-api/build.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_file; -#[cfg(windows)] -use std::os::windows::fs::symlink_file; -use std::{env, fs::remove_file, io::Result, path::Path}; - -fn main() -> Result<()> { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { - let sub = get_rust_subdir(manifest_dir).unwrap(); - format!("{root}/{sub}/bindings.inc.rs") - } else { - // Placing bindings.inc.rs in the source directory is supported - // but not documented or encouraged. - format!("{manifest_dir}/src/bindings.inc.rs") - }; - - let file = Path::new(&file); - if !Path::new(&file).exists() { - panic!(concat!( - "\n", - " No generated C bindings found! Maybe you wanted one of\n", - " `make clippy`, `make rustfmt`, `make rustdoc`?\n", - "\n", - " For other uses of `cargo`, start a subshell with\n", - " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", - " the top of the build tree." - )); - } - - let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = format!("{out_dir}/bindings.inc.rs"); - let dest_path = Path::new(&dest_path); - if dest_path.symlink_metadata().is_ok() { - remove_file(dest_path)?; - } - symlink_file(file, dest_path)?; - - println!("cargo:rerun-if-changed=build.rs"); - Ok(()) -} - -fn get_rust_subdir(path: &str) -> Option<&str> { - path.find("/rust").map(|index| &path[index + 1..]) -} diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs new file mode 120000 index 0000000000..71a3167885 --- /dev/null +++ b/rust/qemu-api/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 64af3caef5..7734f656a2 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -14,10 +14,8 @@ c_enums = [ 'MigrationPolicy', 'MigrationPriority', 'QEMUChrEvent', - 'QEMUClockType', 'ResetType', 'device_endian', - 'module_init_type', ] _qemu_api_bindgen_args = [] foreach enum : c_enums @@ -31,6 +29,7 @@ foreach enum : c_bitfields _qemu_api_bindgen_args += ['--bitfield-enum', enum] endforeach +_qemu_api_bindgen_args += ['--blocklist-type', 'Error'] # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # # Rust bindings generation with `bindgen` might fail in some cases where the @@ -55,16 +54,12 @@ _qemu_api_rs = static_library( 'src/bindings.rs', 'src/cell.rs', 'src/chardev.rs', - 'src/error.rs', 'src/irq.rs', - 'src/log.rs', 'src/memory.rs', - 'src/module.rs', 'src/prelude.rs', 'src/qdev.rs', 'src/qom.rs', 'src/sysbus.rs', - 'src/timer.rs', 'src/vmstate.rs', ], {'.' : _qemu_api_bindings_inc_rs}, @@ -72,13 +67,10 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, + dependencies: [anyhow_rs, common_rs, foreign_rs, libc_rs, qemu_api_macros, util_rs, qom, hwcore, chardev, migration], ) -rust.test('rust-qemu-api-tests', _qemu_api_rs, - suite: ['unit', 'rust']) - qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) @@ -98,7 +90,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, qemu_api_rs]), + dependencies: [common_rs, util_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 3acdd903b5..aedf42b652 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -21,6 +21,7 @@ //! `bindgen`-generated declarations. use common::Zeroable; +use util::bindings::Error; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs deleted file mode 100644 index 8bac3cbec8..0000000000 --- a/rust/qemu-api/src/error.rs +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Error propagation for QEMU Rust code -//! -//! This module contains [`Error`], the bridge between Rust errors and -//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) -//! struct. -//! -//! For FFI code, [`Error`] provides functions to simplify conversion between -//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: -//! -//! * [`ok_or_propagate`](crate::Error::ok_or_propagate), -//! [`bool_or_propagate`](crate::Error::bool_or_propagate), -//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build -//! a C return value while also propagating an error condition -//! -//! * [`err_or_else`](crate::Error::err_or_else) and -//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result` -//! -//! This module is most commonly used at the boundary between C and Rust code; -//! other code will usually access it through the -//! [`qemu_api::Result`](crate::Result) type alias, and will use the -//! [`std::error::Error`] interface to let C errors participate in Rust's error -//! handling functionality. -//! -//! Rust code can also create use this module to create an error object that -//! will be passed up to C code, though in most cases this will be done -//! transparently through the `?` operator. Errors can be constructed from a -//! simple error string, from an [`anyhow::Error`] to pass any other Rust error -//! type up to C code, or from a combination of the two. -//! -//! The third case, corresponding to [`Error::with_error`], is the only one that -//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar -//! to how QEMU's C code handles errno values, the string and the -//! `anyhow::Error` object will be concatenated with `:` as the separator. - -use std::{ - borrow::Cow, - ffi::{c_char, c_int, c_void, CStr}, - fmt::{self, Display}, - panic, ptr, -}; - -use foreign::{prelude::*, OwnedPointer}; - -use crate::bindings; - -pub type Result = std::result::Result; - -#[derive(Debug)] -pub struct Error { - msg: Option>, - /// Appends the print string of the error to the msg if not None - cause: Option, - file: &'static str, - line: u32, -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.cause.as_ref().map(AsRef::as_ref) - } - - #[allow(deprecated)] - fn description(&self) -> &str { - self.msg - .as_deref() - .or_else(|| self.cause.as_deref().map(std::error::Error::description)) - .expect("no message nor cause?") - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut prefix = ""; - if let Some(ref msg) = self.msg { - write!(f, "{msg}")?; - prefix = ": "; - } - if let Some(ref cause) = self.cause { - write!(f, "{prefix}{cause}")?; - } else if prefix.is_empty() { - panic!("no message nor cause?"); - } - Ok(()) - } -} - -impl From for Error { - #[track_caller] - fn from(msg: String) -> Self { - let location = panic::Location::caller(); - Error { - msg: Some(Cow::Owned(msg)), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From<&'static str> for Error { - #[track_caller] - fn from(msg: &'static str) -> Self { - let location = panic::Location::caller(); - Error { - msg: Some(Cow::Borrowed(msg)), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From for Error { - #[track_caller] - fn from(error: anyhow::Error) -> Self { - let location = panic::Location::caller(); - Error { - msg: None, - cause: Some(error), - file: location.file(), - line: location.line(), - } - } -} - -impl Error { - /// Create a new error, prepending `msg` to the - /// description of `cause` - #[track_caller] - pub fn with_error(msg: impl Into>, cause: impl Into) -> Self { - let location = panic::Location::caller(); - Error { - msg: Some(msg.into()), - cause: Some(cause.into()), - file: location.file(), - line: location.line(), - } - } - - /// Consume a result, returning `false` if it is an error and - /// `true` if it is successful. The error is propagated into - /// `errp` like the C API `error_propagate` would do. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { - // SAFETY: caller guarantees errp is valid - unsafe { Self::ok_or_propagate(result, errp) }.is_some() - } - - /// Consume a result, returning a `NULL` pointer if it is an error and - /// a C representation of the contents if it is successful. This is - /// similar to the C API `error_propagate`, but it panics if `*errp` - /// is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - /// - /// See [`propagate`](Error::propagate) for more information. - #[must_use] - pub unsafe fn ptr_or_propagate( - result: Result, - errp: *mut *mut bindings::Error, - ) -> *mut T::Foreign { - // SAFETY: caller guarantees errp is valid - unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() - } - - /// Consume a result in the same way as `self.ok()`, but also propagate - /// a possible error into `errp`. This is similar to the C API - /// `error_propagate`, but it panics if `*errp` is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - /// - /// See [`propagate`](Error::propagate) for more information. - pub unsafe fn ok_or_propagate( - result: Result, - errp: *mut *mut bindings::Error, - ) -> Option { - result.map_err(|err| unsafe { err.propagate(errp) }).ok() - } - - /// Equivalent of the C function `error_propagate`. Fill `*errp` - /// with the information container in `self` if `errp` is not NULL; - /// then consume it. - /// - /// This is similar to the C API `error_propagate`, but it panics if - /// `*errp` is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; it can be - /// `NULL` or it can point to any of: - /// * `error_abort` - /// * `error_fatal` - /// * a local variable of (C) type `Error *` - /// - /// Typically `errp` is received from C code and need not be - /// checked further at the Rust↔C boundary. - pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { - if errp.is_null() { - return; - } - - // SAFETY: caller guarantees that errp and *errp are valid - unsafe { - assert_eq!(*errp, ptr::null_mut()); - bindings::error_propagate(errp, self.clone_to_foreign_ptr()); - } - } - - /// Convert a C `Error*` into a Rust `Result`, using - /// `Ok(())` if `c_error` is NULL. Free the `Error*`. - /// - /// # Safety - /// - /// `c_error` must be `NULL` or valid; typically it was initialized - /// with `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { - // SAFETY: caller guarantees c_error is valid - unsafe { Self::err_or_else(c_error, || ()) } - } - - /// Convert a C `Error*` into a Rust `Result`, calling `f()` to - /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. - /// - /// # Safety - /// - /// `c_error` must be `NULL` or point to a valid C [`struct - /// Error`](bindings::Error); typically it was initialized with - /// `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_else T>( - c_error: *mut bindings::Error, - f: F, - ) -> Result { - // SAFETY: caller guarantees c_error is valid - let err = unsafe { Option::::from_foreign(c_error) }; - match err { - None => Ok(f()), - Some(err) => Err(err), - } - } -} - -impl FreeForeign for Error { - type Foreign = bindings::Error; - - unsafe fn free_foreign(p: *mut bindings::Error) { - // SAFETY: caller guarantees p is valid - unsafe { - bindings::error_free(p); - } - } -} - -impl CloneToForeign for Error { - fn clone_to_foreign(&self) -> OwnedPointer { - // SAFETY: all arguments are controlled by this function - unsafe { - let err: *mut c_void = libc::malloc(std::mem::size_of::()); - let err: &mut bindings::Error = &mut *err.cast(); - *err = bindings::Error { - msg: format!("{self}").clone_to_foreign_ptr(), - err_class: bindings::ERROR_CLASS_GENERIC_ERROR, - src_len: self.file.len() as c_int, - src: self.file.as_ptr().cast::(), - line: self.line as c_int, - func: ptr::null_mut(), - hint: ptr::null_mut(), - }; - OwnedPointer::new(err) - } - } -} - -impl FromForeign for Error { - unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { - // SAFETY: caller guarantees c_error is valid - unsafe { - let error = &*c_error; - let file = if error.src_len < 0 { - // NUL-terminated - CStr::from_ptr(error.src).to_str() - } else { - // Can become str::from_utf8 with Rust 1.87.0 - std::str::from_utf8(std::slice::from_raw_parts( - &*error.src.cast::(), - error.src_len as usize, - )) - }; - - Error { - msg: FromForeign::cloned_from_foreign(error.msg), - cause: None, - file: file.unwrap(), - line: error.line as u32, - } - } - } -} - -#[cfg(test)] -mod tests { - use std::ffi::CStr; - - use anyhow::anyhow; - use common::assert_match; - use foreign::OwnedPointer; - - use super::*; - use crate::bindings; - - #[track_caller] - fn error_for_test(msg: &CStr) -> OwnedPointer { - // SAFETY: all arguments are controlled by this function - let location = panic::Location::caller(); - unsafe { - let err: *mut c_void = libc::malloc(std::mem::size_of::()); - let err: &mut bindings::Error = &mut *err.cast(); - *err = bindings::Error { - msg: msg.clone_to_foreign_ptr(), - err_class: bindings::ERROR_CLASS_GENERIC_ERROR, - src_len: location.file().len() as c_int, - src: location.file().as_ptr().cast::(), - line: location.line() as c_int, - func: ptr::null_mut(), - hint: ptr::null_mut(), - }; - OwnedPointer::new(err) - } - } - - unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { - unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } - } - - #[test] - #[allow(deprecated)] - fn test_description() { - use std::error::Error; - - assert_eq!(super::Error::from("msg").description(), "msg"); - assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); - } - - #[test] - fn test_display() { - assert_eq!(&*format!("{}", Error::from("msg")), "msg"); - assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); - assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); - - assert_eq!( - &*format!("{}", Error::with_error("msg", anyhow!("cause"))), - "msg: cause" - ); - } - - #[test] - fn test_bool_or_propagate() { - unsafe { - let mut local_err: *mut bindings::Error = ptr::null_mut(); - - assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); - assert_eq!(local_err, ptr::null_mut()); - - let my_err = Error::from("msg"); - assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); - assert_ne!(local_err, ptr::null_mut()); - assert_eq!(error_get_pretty(local_err), c"msg"); - bindings::error_free(local_err); - } - } - - #[test] - fn test_ptr_or_propagate() { - unsafe { - let mut local_err: *mut bindings::Error = ptr::null_mut(); - - let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); - assert_eq!(String::from_foreign(ret), "abc"); - assert_eq!(local_err, ptr::null_mut()); - - let my_err = Error::from("msg"); - assert_eq!( - Error::ptr_or_propagate(Err::(my_err), &mut local_err), - ptr::null_mut() - ); - assert_ne!(local_err, ptr::null_mut()); - assert_eq!(error_get_pretty(local_err), c"msg"); - bindings::error_free(local_err); - } - } - - #[test] - fn test_err_or_unit() { - unsafe { - let result = Error::err_or_unit(ptr::null_mut()); - assert_match!(result, Ok(())); - - let err = error_for_test(c"msg"); - let err = Error::err_or_unit(err.into_inner()).unwrap_err(); - assert_eq!(&*format!("{err}"), "msg"); - } - } -} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 6232e39e71..54b252fb2c 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -15,19 +15,13 @@ pub mod prelude; pub mod cell; pub mod chardev; -pub mod error; pub mod irq; -pub mod log; pub mod memory; -pub mod module; pub mod qdev; pub mod qom; pub mod sysbus; -pub mod timer; pub mod vmstate; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this // crate). extern crate self as qemu_api; - -pub use error::{Error, Result}; diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs deleted file mode 100644 index d07f6272dc..0000000000 --- a/rust/qemu-api/src/log.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2025 Bernhard Beschow -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for QEMU's logging infrastructure - -use std::{ - io::{self, Write}, - ptr::NonNull, -}; - -use common::errno; - -use crate::bindings; - -#[repr(u32)] -/// Represents specific error categories within QEMU's logging system. -/// -/// The `Log` enum provides a Rust abstraction for logging errors, corresponding -/// to a subset of the error categories defined in the C implementation. -pub enum Log { - /// Log invalid access caused by the guest. - /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. - GuestError = bindings::LOG_GUEST_ERROR, - - /// Log guest access of unimplemented functionality. - /// Corresponds to `LOG_UNIMP` in the C implementation. - Unimp = bindings::LOG_UNIMP, -} - -/// A RAII guard for QEMU's logging infrastructure. Creating the guard -/// locks the log file, and dropping it (letting it go out of scope) unlocks -/// the file. -/// -/// As long as the guard lives, it can be written to using [`std::io::Write`]. -/// -/// The locking is recursive, therefore owning a guard does not prevent -/// using [`log_mask_ln!()`](crate::log_mask_ln). -pub struct LogGuard(NonNull); - -impl LogGuard { - /// Return a RAII guard that writes to QEMU's logging infrastructure. - /// The log file is locked while the guard exists, ensuring that there - /// is no tearing of the messages. - /// - /// Return `None` if the log file is closed and could not be opened. - /// Do *not* use `unwrap()` on the result; failure can be handled simply - /// by not logging anything. - /// - /// # Examples - /// - /// ``` - /// # use qemu_api::log::LogGuard; - /// # use std::io::Write; - /// if let Some(mut log) = LogGuard::new() { - /// writeln!(log, "test"); - /// } - /// ``` - pub fn new() -> Option { - let f = unsafe { bindings::qemu_log_trylock() }.cast(); - NonNull::new(f).map(Self) - } - - /// Writes a formatted string into the log, returning any error encountered. - /// - /// This method is primarily used by the - /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it - /// to be called explicitly. It is public because it is the only way to - /// examine the error, which `log_mask_ln!()` ignores - /// - /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end. - pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> { - if let Some(mut log) = Self::new() { - log.write_fmt(args)?; - } - Ok(()) - } -} - -impl Write for LogGuard { - fn write(&mut self, bytes: &[u8]) -> io::Result { - let ret = unsafe { - bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr()) - }; - errno::into_io_result(ret) - } - - fn flush(&mut self) -> io::Result<()> { - // Do nothing, dropping the guard takes care of flushing - Ok(()) - } -} - -impl Drop for LogGuard { - fn drop(&mut self) { - unsafe { - bindings::qemu_log_unlock(self.0.as_ptr()); - } - } -} - -/// A macro to log messages conditionally based on a provided mask. -/// -/// The `log_mask_ln` macro checks whether the given mask matches the current -/// log level and, if so, formats and logs the message. It is the Rust -/// counterpart of the `qemu_log_mask()` macro in the C implementation. -/// -/// Errors from writing to the log are ignored. -/// -/// # Parameters -/// -/// - `$mask`: A log level mask. This should be a variant of the `Log` enum. -/// - `$fmt`: A format string following the syntax and rules of the `format!` -/// macro. It specifies the structure of the log message. -/// - `$args`: Optional arguments to be interpolated into the format string. -/// -/// # Example -/// -/// ``` -/// use qemu_api::{log::Log, log_mask_ln}; -/// -/// let error_address = 0xbad; -/// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range"); -/// ``` -/// -/// It is also possible to use printf-style formatting, as well as having a -/// trailing `,`: -/// -/// ``` -/// use qemu_api::{log::Log, log_mask_ln}; -/// -/// let error_address = 0xbad; -/// log_mask_ln!( -/// Log::GuestError, -/// "Address 0x{:x} out of range", -/// error_address, -/// ); -/// ``` -#[macro_export] -macro_rules! log_mask_ln { - ($mask:expr, $fmt:tt $($args:tt)*) => {{ - // Type assertion to enforce type `Log` for $mask - let _: Log = $mask; - - if unsafe { - (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 - } { - _ = ::qemu_api::log::LogGuard::log_fmt( - format_args!("{}\n", format_args!($fmt $($args)*))); - } - }}; -} diff --git a/rust/qemu-api/src/module.rs b/rust/qemu-api/src/module.rs deleted file mode 100644 index fa5cea3598..0000000000 --- a/rust/qemu-api/src/module.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Macro to register blocks of code that run as QEMU starts up. - -#[macro_export] -macro_rules! module_init { - ($type:ident => $body:block) => { - const _: () = { - #[used] - #[cfg_attr( - not(any(target_vendor = "apple", target_os = "windows")), - link_section = ".init_array" - )] - #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")] - #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] - pub static LOAD_MODULE: extern "C" fn() = { - extern "C" fn init_fn() { - $body - } - - extern "C" fn ctor_fn() { - unsafe { - $crate::bindings::register_module_init( - Some(init_fn), - $crate::bindings::module_init_type::$type, - ); - } - } - - ctor_fn - }; - }; - }; - - // shortcut because it's quite common that $body needs unsafe {} - ($type:ident => unsafe $body:block) => { - $crate::module_init! { - $type => { unsafe { $body } } - } - }; -} diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index dcfe71497f..3d771481e4 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,8 +7,6 @@ pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; -pub use crate::log_mask_ln; - pub use crate::qdev::DeviceMethods; pub use crate::qom::InterfaceType; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 8be125fae4..d87479ce13 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -11,12 +11,12 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use common::{callbacks::FnCall, Opaque}; +use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, cell::bql_locked, chardev::Chardev, - error::{Error, Result}, impl_vmstate_c_struct, irq::InterruptSource, prelude::*, @@ -183,7 +183,7 @@ pub trait DeviceImpl: /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_realize_fn( dev: *mut bindings::DeviceState, - errp: *mut *mut bindings::Error, + errp: *mut *mut util::bindings::Error, ) { let state = NonNull::new(dev).unwrap().cast::(); let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 4a5b4cbbf6..2dbfc31dbd 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -114,7 +114,7 @@ where unsafe { bindings::sysbus_realize( self.upcast().as_mut_ptr(), - addr_of_mut!(bindings::error_fatal), + addr_of_mut!(util::bindings::error_fatal), ); } } diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs deleted file mode 100644 index 383e1a6e77..0000000000 --- a/rust/qemu-api/src/timer.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ - ffi::{c_int, c_void}, - pin::Pin, -}; - -use common::{callbacks::FnCall, Opaque}; - -use crate::bindings::{ - self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType, -}; - -/// A safe wrapper around [`bindings::QEMUTimer`]. -#[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] -pub struct Timer(Opaque); - -unsafe impl Send for Timer {} -unsafe impl Sync for Timer {} - -#[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] -pub struct TimerListGroup(Opaque); - -unsafe impl Send for TimerListGroup {} -unsafe impl Sync for TimerListGroup {} - -impl Timer { - pub const MS: u32 = bindings::SCALE_MS; - pub const US: u32 = bindings::SCALE_US; - pub const NS: u32 = bindings::SCALE_NS; - - /// Create a `Timer` struct without initializing it. - /// - /// # Safety - /// - /// The timer must be initialized before it is armed with - /// [`modify`](Self::modify). - pub const unsafe fn new() -> Self { - // SAFETY: requirements relayed to callers of Timer::new - Self(unsafe { Opaque::zeroed() }) - } - - /// Create a new timer with the given attributes. - pub fn init_full<'timer, 'opaque: 'timer, T, F>( - self: Pin<&'timer mut Self>, - timer_list_group: Option<&TimerListGroup>, - clk_type: ClockType, - scale: u32, - attributes: u32, - _cb: F, - opaque: &'opaque T, - ) where - F: for<'a> FnCall<(&'a T,)>, - { - const { assert!(F::IS_SOME) }; - - /// timer expiration callback - unsafe extern "C" fn rust_timer_handler FnCall<(&'a T,)>>( - opaque: *mut c_void, - ) { - // SAFETY: the opaque was passed as a reference to `T`. - F::call((unsafe { &*(opaque.cast::()) },)) - } - - let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::; - - // SAFETY: the opaque outlives the timer - unsafe { - timer_init_full( - self.as_mut_ptr(), - if let Some(g) = timer_list_group { - g as *const TimerListGroup as *mut _ - } else { - ::core::ptr::null_mut() - }, - clk_type.id, - scale as c_int, - attributes as c_int, - Some(timer_cb), - (opaque as *const T).cast::().cast_mut(), - ) - } - } - - pub fn modify(&self, expire_time: u64) { - // SAFETY: the only way to obtain a Timer safely is via methods that - // take a Pin<&mut Self>, therefore the timer is pinned - unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) } - } - - pub fn delete(&self) { - // SAFETY: the only way to obtain a Timer safely is via methods that - // take a Pin<&mut Self>, therefore the timer is pinned - unsafe { timer_del(self.as_mut_ptr()) } - } -} - -// FIXME: use something like PinnedDrop from the pinned_init crate -impl Drop for Timer { - fn drop(&mut self) { - self.delete() - } -} - -pub struct ClockType { - id: QEMUClockType, -} - -impl ClockType { - pub fn get_ns(&self) -> u64 { - // SAFETY: cannot be created outside this module, therefore id - // is valid - (unsafe { qemu_clock_get_ns(self.id) }) as u64 - } -} - -pub const CLOCK_VIRTUAL: ClockType = ClockType { - id: QEMUClockType::QEMU_CLOCK_VIRTUAL, -}; - -pub const NANOSECONDS_PER_SECOND: u64 = 1000000000; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 06aac3a73f..37e47cc4c6 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -315,7 +315,7 @@ impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); impl_vmstate_scalar!(vmstate_info_uint64, u64); -impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); +impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer); #[macro_export] macro_rules! impl_vmstate_c_struct { diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 1349568741..70ef4a80d5 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -5,7 +5,6 @@ use std::{ffi::CStr, ptr::addr_of}; use qemu_api::{ - bindings::{module_call_init, module_init_type}, cell::{self, BqlCell}, prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, @@ -13,6 +12,7 @@ use qemu_api::{ sysbus::SysBusDevice, vmstate::{VMStateDescription, VMStateDescriptionBuilder}, }; +use util::bindings::{module_call_init, module_init_type}; mod vmstate_tests; diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index 15a1b19847..cc7112406b 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -48,9 +48,6 @@ typedef enum memory_order { #endif /* __CLANG_STDATOMIC_H */ #include "qemu/osdep.h" -#include "qemu/log.h" -#include "qemu/log-for-trace.h" -#include "qemu/module.h" #include "qemu-io.h" #include "system/system.h" #include "hw/sysbus.h" @@ -61,11 +58,8 @@ typedef enum memory_order { #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/irq.h" -#include "qapi/error.h" -#include "qapi/error-internal.h" #include "migration/vmstate.h" #include "chardev/char-serial.h" #include "exec/memattrs.h" -#include "qemu/timer.h" #include "system/address-spaces.h" #include "hw/char/pl011.h" diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml new file mode 100644 index 0000000000..637df61060 --- /dev/null +++ b/rust/util/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "util" +version = "0.1.0" +description = "Rust bindings for QEMU/util" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +anyhow = { workspace = true } +foreign = { workspace = true } +libc = { workspace = true } +common = { path = "../common" } +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/util/build.rs b/rust/util/build.rs new file mode 100644 index 0000000000..5654d1d562 --- /dev/null +++ b/rust/util/build.rs @@ -0,0 +1,49 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_file; +#[cfg(windows)] +use std::os::windows::fs::symlink_file; +use std::{env, fs::remove_file, io::Result, path::Path}; + +fn main() -> Result<()> { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { + let sub = get_rust_subdir(manifest_dir).unwrap(); + format!("{root}/{sub}/bindings.inc.rs") + } else { + // Placing bindings.inc.rs in the source directory is supported + // but not documented or encouraged. + format!("{manifest_dir}/src/bindings.inc.rs") + }; + + let file = Path::new(&file); + if !Path::new(&file).exists() { + panic!(concat!( + "\n", + " No generated C bindings found! Maybe you wanted one of\n", + " `make clippy`, `make rustfmt`, `make rustdoc`?\n", + "\n", + " For other uses of `cargo`, start a subshell with\n", + " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", + " the top of the build tree." + )); + } + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = format!("{out_dir}/bindings.inc.rs"); + let dest_path = Path::new(&dest_path); + if dest_path.symlink_metadata().is_ok() { + remove_file(dest_path)?; + } + symlink_file(file, dest_path)?; + + println!("cargo:rerun-if-changed=build.rs"); + Ok(()) +} + +fn get_rust_subdir(path: &str) -> Option<&str> { + path.find("/rust").map(|index| &path[index + 1..]) +} diff --git a/rust/util/meson.build b/rust/util/meson.build new file mode 100644 index 0000000000..56e929349b --- /dev/null +++ b/rust/util/meson.build @@ -0,0 +1,55 @@ +_util_bindgen_args = [] +c_enums = [ + 'module_init_type', + 'QEMUClockType', +] +foreach enum : c_enums + _util_bindgen_args += ['--rustified-enum', enum] +endforeach + +# +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_util_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _util_bindgen_args, +) + +_util_rs = static_library( + 'util', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/error.rs', + 'src/log.rs', + 'src/module.rs', + 'src/timer.rs', + ], + {'.': _util_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qemu_api_macros, qom, qemuutil], +) + +util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-util-rs-doctests', + _util_rs, + protocol: 'rust', + dependencies: util_rs, + suite: ['doc', 'rust'] +) diff --git a/rust/util/src/bindings.rs b/rust/util/src/bindings.rs new file mode 100644 index 0000000000..9ffff12cde --- /dev/null +++ b/rust/util/src/bindings.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/util/src/error.rs b/rust/util/src/error.rs new file mode 100644 index 0000000000..bfa5a8685b --- /dev/null +++ b/rust/util/src/error.rs @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Error propagation for QEMU Rust code +//! +//! This module contains [`Error`], the bridge between Rust errors and +//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) +//! struct. +//! +//! For FFI code, [`Error`] provides functions to simplify conversion between +//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: +//! +//! * [`ok_or_propagate`](crate::Error::ok_or_propagate), +//! [`bool_or_propagate`](crate::Error::bool_or_propagate), +//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build +//! a C return value while also propagating an error condition +//! +//! * [`err_or_else`](crate::Error::err_or_else) and +//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result` +//! +//! This module is most commonly used at the boundary between C and Rust code; +//! other code will usually access it through the +//! [`utils::Result`](crate::Result) type alias, and will use the +//! [`std::error::Error`] interface to let C errors participate in Rust's error +//! handling functionality. +//! +//! Rust code can also create use this module to create an error object that +//! will be passed up to C code, though in most cases this will be done +//! transparently through the `?` operator. Errors can be constructed from a +//! simple error string, from an [`anyhow::Error`] to pass any other Rust error +//! type up to C code, or from a combination of the two. +//! +//! The third case, corresponding to [`Error::with_error`], is the only one that +//! requires mentioning [`utils::Error`](crate::Error) explicitly. Similar +//! to how QEMU's C code handles errno values, the string and the +//! `anyhow::Error` object will be concatenated with `:` as the separator. + +use std::{ + borrow::Cow, + ffi::{c_char, c_int, c_void, CStr}, + fmt::{self, Display}, + panic, ptr, +}; + +use foreign::{prelude::*, OwnedPointer}; + +use crate::bindings; + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub struct Error { + msg: Option>, + /// Appends the print string of the error to the msg if not None + cause: Option, + file: &'static str, + line: u32, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.cause.as_ref().map(AsRef::as_ref) + } + + #[allow(deprecated)] + fn description(&self) -> &str { + self.msg + .as_deref() + .or_else(|| self.cause.as_deref().map(std::error::Error::description)) + .expect("no message nor cause?") + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut prefix = ""; + if let Some(ref msg) = self.msg { + write!(f, "{msg}")?; + prefix = ": "; + } + if let Some(ref cause) = self.cause { + write!(f, "{prefix}{cause}")?; + } else if prefix.is_empty() { + panic!("no message nor cause?"); + } + Ok(()) + } +} + +impl From for Error { + #[track_caller] + fn from(msg: String) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Owned(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From<&'static str> for Error { + #[track_caller] + fn from(msg: &'static str) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Borrowed(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(error: anyhow::Error) -> Self { + let location = panic::Location::caller(); + Error { + msg: None, + cause: Some(error), + file: location.file(), + line: location.line(), + } + } +} + +impl Error { + /// Create a new error, prepending `msg` to the + /// description of `cause` + #[track_caller] + pub fn with_error(msg: impl Into>, cause: impl Into) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(msg.into()), + cause: Some(cause.into()), + file: location.file(), + line: location.line(), + } + } + + /// Consume a result, returning `false` if it is an error and + /// `true` if it is successful. The error is propagated into + /// `errp` like the C API `error_propagate` would do. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.is_some() + } + + /// Consume a result, returning a `NULL` pointer if it is an error and + /// a C representation of the contents if it is successful. This is + /// similar to the C API `error_propagate`, but it panics if `*errp` + /// is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + #[must_use] + pub unsafe fn ptr_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> *mut T::Foreign { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() + } + + /// Consume a result in the same way as `self.ok()`, but also propagate + /// a possible error into `errp`. This is similar to the C API + /// `error_propagate`, but it panics if `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + pub unsafe fn ok_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> Option { + result.map_err(|err| unsafe { err.propagate(errp) }).ok() + } + + /// Equivalent of the C function `error_propagate`. Fill `*errp` + /// with the information container in `self` if `errp` is not NULL; + /// then consume it. + /// + /// This is similar to the C API `error_propagate`, but it panics if + /// `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; it can be + /// `NULL` or it can point to any of: + /// * `error_abort` + /// * `error_fatal` + /// * a local variable of (C) type `Error *` + /// + /// Typically `errp` is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { + if errp.is_null() { + return; + } + + // SAFETY: caller guarantees that errp and *errp are valid + unsafe { + assert_eq!(*errp, ptr::null_mut()); + bindings::error_propagate(errp, self.clone_to_foreign_ptr()); + } + } + + /// Convert a C `Error*` into a Rust `Result`, using + /// `Ok(())` if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or valid; typically it was initialized + /// with `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { + // SAFETY: caller guarantees c_error is valid + unsafe { Self::err_or_else(c_error, || ()) } + } + + /// Convert a C `Error*` into a Rust `Result`, calling `f()` to + /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or point to a valid C [`struct + /// Error`](bindings::Error); typically it was initialized with + /// `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_else T>( + c_error: *mut bindings::Error, + f: F, + ) -> Result { + // SAFETY: caller guarantees c_error is valid + let err = unsafe { Option::::from_foreign(c_error) }; + match err { + None => Ok(f()), + Some(err) => Err(err), + } + } +} + +impl FreeForeign for Error { + type Foreign = bindings::Error; + + unsafe fn free_foreign(p: *mut bindings::Error) { + // SAFETY: caller guarantees p is valid + unsafe { + bindings::error_free(p); + } + } +} + +impl CloneToForeign for Error { + fn clone_to_foreign(&self) -> OwnedPointer { + // SAFETY: all arguments are controlled by this function + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: format!("{self}").clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: self.file.len() as c_int, + src: self.file.as_ptr().cast::(), + line: self.line as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } +} + +impl FromForeign for Error { + unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { + // SAFETY: caller guarantees c_error is valid + unsafe { + let error = &*c_error; + let file = if error.src_len < 0 { + // NUL-terminated + CStr::from_ptr(error.src).to_str() + } else { + // Can become str::from_utf8 with Rust 1.87.0 + std::str::from_utf8(std::slice::from_raw_parts( + &*error.src.cast::(), + error.src_len as usize, + )) + }; + + Error { + msg: FromForeign::cloned_from_foreign(error.msg), + cause: None, + file: file.unwrap(), + line: error.line as u32, + } + } + } +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use anyhow::anyhow; + use common::assert_match; + use foreign::OwnedPointer; + + use super::*; + + #[track_caller] + fn error_for_test(msg: &CStr) -> OwnedPointer { + // SAFETY: all arguments are controlled by this function + let location = panic::Location::caller(); + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: msg.clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: location.file().len() as c_int, + src: location.file().as_ptr().cast::(), + line: location.line() as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } + + unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { + unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } + } + + #[test] + #[allow(deprecated)] + fn test_description() { + use std::error::Error; + + assert_eq!(super::Error::from("msg").description(), "msg"); + assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); + } + + #[test] + fn test_display() { + assert_eq!(&*format!("{}", Error::from("msg")), "msg"); + assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); + assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); + + assert_eq!( + &*format!("{}", Error::with_error("msg", anyhow!("cause"))), + "msg: cause" + ); + } + + #[test] + fn test_bool_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_ptr_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); + assert_eq!(String::from_foreign(ret), "abc"); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert_eq!( + Error::ptr_or_propagate(Err::(my_err), &mut local_err), + ptr::null_mut() + ); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_err_or_unit() { + unsafe { + let result = Error::err_or_unit(ptr::null_mut()); + assert_match!(result, Ok(())); + + let err = error_for_test(c"msg"); + let err = Error::err_or_unit(err.into_inner()).unwrap_err(); + assert_eq!(&*format!("{err}"), "msg"); + } + } +} diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs new file mode 100644 index 0000000000..16c89b9517 --- /dev/null +++ b/rust/util/src/lib.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; +pub mod error; +pub mod log; +pub mod module; +pub mod timer; + +pub use error::{Error, Result}; diff --git a/rust/util/src/log.rs b/rust/util/src/log.rs new file mode 100644 index 0000000000..af9a3e9123 --- /dev/null +++ b/rust/util/src/log.rs @@ -0,0 +1,151 @@ +// Copyright 2025 Bernhard Beschow +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for QEMU's logging infrastructure + +use std::{ + io::{self, Write}, + ptr::NonNull, +}; + +use common::errno; + +use crate::bindings; + +#[repr(u32)] +/// Represents specific error categories within QEMU's logging system. +/// +/// The `Log` enum provides a Rust abstraction for logging errors, corresponding +/// to a subset of the error categories defined in the C implementation. +pub enum Log { + /// Log invalid access caused by the guest. + /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. + GuestError = bindings::LOG_GUEST_ERROR, + + /// Log guest access of unimplemented functionality. + /// Corresponds to `LOG_UNIMP` in the C implementation. + Unimp = bindings::LOG_UNIMP, +} + +/// A RAII guard for QEMU's logging infrastructure. Creating the guard +/// locks the log file, and dropping it (letting it go out of scope) unlocks +/// the file. +/// +/// As long as the guard lives, it can be written to using [`std::io::Write`]. +/// +/// The locking is recursive, therefore owning a guard does not prevent +/// using [`log_mask_ln!()`](crate::log_mask_ln). +pub struct LogGuard(NonNull); + +impl LogGuard { + /// Return a RAII guard that writes to QEMU's logging infrastructure. + /// The log file is locked while the guard exists, ensuring that there + /// is no tearing of the messages. + /// + /// Return `None` if the log file is closed and could not be opened. + /// Do *not* use `unwrap()` on the result; failure can be handled simply + /// by not logging anything. + /// + /// # Examples + /// + /// ``` + /// # use util::log::LogGuard; + /// # use std::io::Write; + /// if let Some(mut log) = LogGuard::new() { + /// writeln!(log, "test"); + /// } + /// ``` + pub fn new() -> Option { + let f = unsafe { bindings::qemu_log_trylock() }.cast(); + NonNull::new(f).map(Self) + } + + /// Writes a formatted string into the log, returning any error encountered. + /// + /// This method is primarily used by the + /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it + /// to be called explicitly. It is public because it is the only way to + /// examine the error, which `log_mask_ln!()` ignores + /// + /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end. + pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> { + if let Some(mut log) = Self::new() { + log.write_fmt(args)?; + } + Ok(()) + } +} + +impl Write for LogGuard { + fn write(&mut self, bytes: &[u8]) -> io::Result { + let ret = unsafe { + bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr()) + }; + errno::into_io_result(ret) + } + + fn flush(&mut self) -> io::Result<()> { + // Do nothing, dropping the guard takes care of flushing + Ok(()) + } +} + +impl Drop for LogGuard { + fn drop(&mut self) { + unsafe { + bindings::qemu_log_unlock(self.0.as_ptr()); + } + } +} + +/// A macro to log messages conditionally based on a provided mask. +/// +/// The `log_mask_ln` macro checks whether the given mask matches the current +/// log level and, if so, formats and logs the message. It is the Rust +/// counterpart of the `qemu_log_mask()` macro in the C implementation. +/// +/// Errors from writing to the log are ignored. +/// +/// # Parameters +/// +/// - `$mask`: A log level mask. This should be a variant of the `Log` enum. +/// - `$fmt`: A format string following the syntax and rules of the `format!` +/// macro. It specifies the structure of the log message. +/// - `$args`: Optional arguments to be interpolated into the format string. +/// +/// # Example +/// +/// ``` +/// use util::{log::Log, log_mask_ln}; +/// +/// let error_address = 0xbad; +/// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range"); +/// ``` +/// +/// It is also possible to use printf-style formatting, as well as having a +/// trailing `,`: +/// +/// ``` +/// use util::{log::Log, log_mask_ln}; +/// +/// let error_address = 0xbad; +/// log_mask_ln!( +/// Log::GuestError, +/// "Address 0x{:x} out of range", +/// error_address, +/// ); +/// ``` +#[macro_export] +macro_rules! log_mask_ln { + ($mask:expr, $fmt:tt $($args:tt)*) => {{ + // Type assertion to enforce type `Log` for $mask + let _: $crate::log::Log = $mask; + + if unsafe { + ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 + } { + _ = $crate::log::LogGuard::log_fmt( + format_args!("{}\n", format_args!($fmt $($args)*))); + } + }}; +} diff --git a/rust/util/src/module.rs b/rust/util/src/module.rs new file mode 100644 index 0000000000..06c45fc142 --- /dev/null +++ b/rust/util/src/module.rs @@ -0,0 +1,43 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Macro to register blocks of code that run as QEMU starts up. + +#[macro_export] +macro_rules! module_init { + ($type:ident => $body:block) => { + const _: () = { + #[used] + #[cfg_attr( + not(any(target_vendor = "apple", target_os = "windows")), + link_section = ".init_array" + )] + #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")] + #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] + pub static LOAD_MODULE: extern "C" fn() = { + extern "C" fn init_fn() { + $body + } + + extern "C" fn ctor_fn() { + unsafe { + $crate::bindings::register_module_init( + Some(init_fn), + $crate::bindings::module_init_type::$type, + ); + } + } + + ctor_fn + }; + }; + }; + + // shortcut because it's quite common that $body needs unsafe {} + ($type:ident => unsafe $body:block) => { + ::util::module_init! { + $type => { unsafe { $body } } + } + }; +} diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs new file mode 100644 index 0000000000..383e1a6e77 --- /dev/null +++ b/rust/util/src/timer.rs @@ -0,0 +1,125 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + ffi::{c_int, c_void}, + pin::Pin, +}; + +use common::{callbacks::FnCall, Opaque}; + +use crate::bindings::{ + self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType, +}; + +/// A safe wrapper around [`bindings::QEMUTimer`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Timer(Opaque); + +unsafe impl Send for Timer {} +unsafe impl Sync for Timer {} + +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct TimerListGroup(Opaque); + +unsafe impl Send for TimerListGroup {} +unsafe impl Sync for TimerListGroup {} + +impl Timer { + pub const MS: u32 = bindings::SCALE_MS; + pub const US: u32 = bindings::SCALE_US; + pub const NS: u32 = bindings::SCALE_NS; + + /// Create a `Timer` struct without initializing it. + /// + /// # Safety + /// + /// The timer must be initialized before it is armed with + /// [`modify`](Self::modify). + pub const unsafe fn new() -> Self { + // SAFETY: requirements relayed to callers of Timer::new + Self(unsafe { Opaque::zeroed() }) + } + + /// Create a new timer with the given attributes. + pub fn init_full<'timer, 'opaque: 'timer, T, F>( + self: Pin<&'timer mut Self>, + timer_list_group: Option<&TimerListGroup>, + clk_type: ClockType, + scale: u32, + attributes: u32, + _cb: F, + opaque: &'opaque T, + ) where + F: for<'a> FnCall<(&'a T,)>, + { + const { assert!(F::IS_SOME) }; + + /// timer expiration callback + unsafe extern "C" fn rust_timer_handler FnCall<(&'a T,)>>( + opaque: *mut c_void, + ) { + // SAFETY: the opaque was passed as a reference to `T`. + F::call((unsafe { &*(opaque.cast::()) },)) + } + + let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::; + + // SAFETY: the opaque outlives the timer + unsafe { + timer_init_full( + self.as_mut_ptr(), + if let Some(g) = timer_list_group { + g as *const TimerListGroup as *mut _ + } else { + ::core::ptr::null_mut() + }, + clk_type.id, + scale as c_int, + attributes as c_int, + Some(timer_cb), + (opaque as *const T).cast::().cast_mut(), + ) + } + } + + pub fn modify(&self, expire_time: u64) { + // SAFETY: the only way to obtain a Timer safely is via methods that + // take a Pin<&mut Self>, therefore the timer is pinned + unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) } + } + + pub fn delete(&self) { + // SAFETY: the only way to obtain a Timer safely is via methods that + // take a Pin<&mut Self>, therefore the timer is pinned + unsafe { timer_del(self.as_mut_ptr()) } + } +} + +// FIXME: use something like PinnedDrop from the pinned_init crate +impl Drop for Timer { + fn drop(&mut self) { + self.delete() + } +} + +pub struct ClockType { + id: QEMUClockType, +} + +impl ClockType { + pub fn get_ns(&self) -> u64 { + // SAFETY: cannot be created outside this module, therefore id + // is valid + (unsafe { qemu_clock_get_ns(self.id) }) as u64 + } +} + +pub const CLOCK_VIRTUAL: ClockType = ClockType { + id: QEMUClockType::QEMU_CLOCK_VIRTUAL, +}; + +pub const NANOSECONDS_PER_SECOND: u64 = 1000000000; diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h new file mode 100644 index 0000000000..b9ed68a01d --- /dev/null +++ b/rust/util/wrapper.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qapi/error-internal.h" +#include "qemu/log-for-trace.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" -- cgit 1.4.1