diff options
Diffstat (limited to 'rust/qemu-api/src')
-rw-r--r-- | rust/qemu-api/src/bindings.rs | 1 | ||||
-rw-r--r-- | rust/qemu-api/src/bitops.rs | 2 | ||||
-rw-r--r-- | rust/qemu-api/src/c_str.rs | 61 | ||||
-rw-r--r-- | rust/qemu-api/src/cell.rs | 28 | ||||
-rw-r--r-- | rust/qemu-api/src/chardev.rs | 5 | ||||
-rw-r--r-- | rust/qemu-api/src/error.rs | 416 | ||||
-rw-r--r-- | rust/qemu-api/src/irq.rs | 6 | ||||
-rw-r--r-- | rust/qemu-api/src/lib.rs | 12 | ||||
-rw-r--r-- | rust/qemu-api/src/log.rs | 73 | ||||
-rw-r--r-- | rust/qemu-api/src/memory.rs | 15 | ||||
-rw-r--r-- | rust/qemu-api/src/offset_of.rs | 168 | ||||
-rw-r--r-- | rust/qemu-api/src/prelude.rs | 2 | ||||
-rw-r--r-- | rust/qemu-api/src/qdev.rs | 70 | ||||
-rw-r--r-- | rust/qemu-api/src/qom.rs | 204 | ||||
-rw-r--r-- | rust/qemu-api/src/timer.rs | 6 | ||||
-rw-r--r-- | rust/qemu-api/src/uninit.rs | 85 | ||||
-rw-r--r-- | rust/qemu-api/src/vmstate.rs | 16 | ||||
-rw-r--r-- | rust/qemu-api/src/zeroable.rs | 106 |
18 files changed, 871 insertions, 405 deletions
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 3c1d297..057de4b 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -11,6 +11,7 @@ clippy::restriction, clippy::style, clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, clippy::useless_transmute, clippy::missing_safety_doc )] diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs index 023ec1a..b1e3a53 100644 --- a/rust/qemu-api/src/bitops.rs +++ b/rust/qemu-api/src/bitops.rs @@ -1,5 +1,5 @@ // Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu <zhai1.liu@intel.com> +// Author(s): Zhao Liu <zhao1.liu@intel.com> // SPDX-License-Identifier: GPL-2.0-or-later //! This module provides bit operation extensions to integer types. diff --git a/rust/qemu-api/src/c_str.rs b/rust/qemu-api/src/c_str.rs deleted file mode 100644 index 3fa61b5..0000000 --- a/rust/qemu-api/src/c_str.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini <pbonzini@redhat.com> -// SPDX-License-Identifier: GPL-2.0-or-later - -#![doc(hidden)] -//! This module provides a macro to define a constant of type -//! [`CStr`](std::ffi::CStr), for compatibility with versions of -//! Rust that lack `c""` literals. -//! -//! Documentation is hidden because it only exposes macros, which -//! are exported directly from `qemu_api`. - -#[macro_export] -/// Given a string constant _without_ embedded or trailing NULs, return -/// a `CStr`. -/// -/// Needed for compatibility with Rust <1.77. -macro_rules! c_str { - ($str:expr) => {{ - const STRING: &str = concat!($str, "\0"); - const BYTES: &[u8] = STRING.as_bytes(); - - // "for" is not allowed in const context... oh well, - // everybody loves some lisp. This could be turned into - // a procedural macro if this is a problem; alternatively - // Rust 1.72 makes CStr::from_bytes_with_nul a const function. - const fn f(b: &[u8], i: usize) { - if i == b.len() - 1 { - } else if b[i] == 0 { - panic!("c_str argument contains NUL") - } else { - f(b, i + 1) - } - } - f(BYTES, 0); - - // SAFETY: absence of NULs apart from the final byte was checked above - unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(BYTES) } - }}; -} - -#[cfg(test)] -mod tests { - use std::ffi::CStr; - - use crate::c_str; - - #[test] - fn test_cstr_macro() { - let good = c_str!("đŸ¦€"); - let good_bytes = b"\xf0\x9f\xa6\x80\0"; - assert_eq!(good.to_bytes_with_nul(), good_bytes); - } - - #[test] - fn test_cstr_macro_const() { - const GOOD: &CStr = c_str!("đŸ¦€"); - const GOOD_BYTES: &[u8] = b"\xf0\x9f\xa6\x80\0"; - assert_eq!(GOOD.to_bytes_with_nul(), GOOD_BYTES); - } -} diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index ab0785a..27063b0 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -77,13 +77,13 @@ //! //! ``` //! # use qemu_api::prelude::*; -//! # use qemu_api::{c_str, cell::BqlRefCell, irq::InterruptSource, irq::IRQState}; +//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState}; //! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; //! # const N_GPIOS: usize = 8; //! # struct PL061Registers { /* ... */ } //! # unsafe impl ObjectType for PL061State { //! # type Class = <SysBusDevice as ObjectType>::Class; -//! # const TYPE_NAME: &'static std::ffi::CStr = c_str!("pl061"); +//! # const TYPE_NAME: &'static std::ffi::CStr = c"pl061"; //! # } //! struct PL061State { //! parent_obj: ParentField<SysBusDevice>, @@ -225,27 +225,23 @@ use crate::bindings; /// An internal function that is used by doctests. pub fn bql_start_test() { - if cfg!(MESON) { - // SAFETY: integration tests are run with --test-threads=1, while - // unit tests and doctests are not multithreaded and do not have - // any BQL-protected data. Just set bql_locked to true. - unsafe { - bindings::rust_bql_mock_lock(); - } + // SAFETY: integration tests are run with --test-threads=1, while + // unit tests and doctests are not multithreaded and do not have + // any BQL-protected data. Just set bql_locked to true. + unsafe { + bindings::rust_bql_mock_lock(); } } pub fn bql_locked() -> bool { // SAFETY: the function does nothing but return a thread-local bool - !cfg!(MESON) || unsafe { bindings::bql_locked() } + unsafe { bindings::bql_locked() } } fn bql_block_unlock(increase: bool) { - if cfg!(MESON) { - // SAFETY: this only adjusts a counter - unsafe { - bindings::bql_block_unlock(increase); - } + // SAFETY: this only adjusts a counter + unsafe { + bindings::bql_block_unlock(increase); } } @@ -1016,7 +1012,7 @@ impl<T> Opaque<T> { /// Returns a raw pointer to the opaque data. pub const fn as_ptr(&self) -> *const T { - self.as_mut_ptr() as *const _ + self.as_mut_ptr().cast_const() } /// Returns a raw pointer to the opaque data that can be passed to a diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 11e6c45..6e0590d 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -10,11 +10,10 @@ //! called. use std::{ - ffi::CStr, + ffi::{c_int, c_void, CStr}, fmt::{self, Debug}, io::{self, ErrorKind, Write}, marker::PhantomPinned, - os::raw::{c_int, c_void}, ptr::addr_of_mut, slice, }; @@ -161,7 +160,7 @@ impl CharBackend { receive_cb, event_cb, None, - (owner as *const T as *mut T).cast::<c_void>(), + (owner as *const T).cast_mut().cast::<c_void>(), core::ptr::null_mut(), true, ); diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs new file mode 100644 index 0000000..e114fc4 --- /dev/null +++ b/rust/qemu-api/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 +//! [`qemu_api::Result`](crate::Result) type alias, and will use the +//! [`std::error::Error`] interface to let C errors participate in Rust's error +//! handling functionality. +//! +//! Rust code can also create use this module to create an error object that +//! will be passed up to C code, though in most cases this will be done +//! transparently through the `?` operator. Errors can be constructed from a +//! simple error string, from an [`anyhow::Error`] to pass any other Rust error +//! type up to C code, or from a combination of the two. +//! +//! The third case, corresponding to [`Error::with_error`], is the only one that +//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar +//! to how QEMU's C code handles errno values, the string and the +//! `anyhow::Error` object will be concatenated with `:` as the separator. + +use std::{ + borrow::Cow, + ffi::{c_char, c_int, c_void, CStr}, + fmt::{self, Display}, + panic, ptr, +}; + +use foreign::{prelude::*, OwnedPointer}; + +use crate::bindings; + +pub type Result<T> = std::result::Result<T, Error>; + +#[derive(Debug)] +pub struct Error { + msg: Option<Cow<'static, str>>, + /// Appends the print string of the error to the msg if not None + cause: Option<anyhow::Error>, + file: &'static str, + line: u32, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.cause.as_ref().map(AsRef::as_ref) + } + + #[allow(deprecated)] + fn description(&self) -> &str { + self.msg + .as_deref() + .or_else(|| self.cause.as_deref().map(std::error::Error::description)) + .expect("no message nor cause?") + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut prefix = ""; + if let Some(ref msg) = self.msg { + write!(f, "{msg}")?; + prefix = ": "; + } + if let Some(ref cause) = self.cause { + write!(f, "{prefix}{cause}")?; + } else if prefix.is_empty() { + panic!("no message nor cause?"); + } + Ok(()) + } +} + +impl From<String> for Error { + #[track_caller] + fn from(msg: String) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Owned(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From<&'static str> for Error { + #[track_caller] + fn from(msg: &'static str) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Borrowed(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From<anyhow::Error> for Error { + #[track_caller] + fn from(error: anyhow::Error) -> Self { + let location = panic::Location::caller(); + Error { + msg: None, + cause: Some(error), + file: location.file(), + line: location.line(), + } + } +} + +impl Error { + /// Create a new error, prepending `msg` to the + /// description of `cause` + #[track_caller] + pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(msg.into()), + cause: Some(cause.into()), + file: location.file(), + line: location.line(), + } + } + + /// Consume a result, returning `false` if it is an error and + /// `true` if it is successful. The error is propagated into + /// `errp` like the C API `error_propagate` would do. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.is_some() + } + + /// Consume a result, returning a `NULL` pointer if it is an error and + /// a C representation of the contents if it is successful. This is + /// similar to the C API `error_propagate`, but it panics if `*errp` + /// is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + #[must_use] + pub unsafe fn ptr_or_propagate<T: CloneToForeign>( + result: Result<T>, + errp: *mut *mut bindings::Error, + ) -> *mut T::Foreign { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() + } + + /// Consume a result in the same way as `self.ok()`, but also propagate + /// a possible error into `errp`. This is similar to the C API + /// `error_propagate`, but it panics if `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + pub unsafe fn ok_or_propagate<T>( + result: Result<T>, + errp: *mut *mut bindings::Error, + ) -> Option<T> { + result.map_err(|err| unsafe { err.propagate(errp) }).ok() + } + + /// Equivalent of the C function `error_propagate`. Fill `*errp` + /// with the information container in `self` if `errp` is not NULL; + /// then consume it. + /// + /// This is similar to the C API `error_propagate`, but it panics if + /// `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; it can be + /// `NULL` or it can point to any of: + /// * `error_abort` + /// * `error_fatal` + /// * a local variable of (C) type `Error *` + /// + /// Typically `errp` is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { + if errp.is_null() { + return; + } + + // SAFETY: caller guarantees that errp and *errp are valid + unsafe { + assert_eq!(*errp, ptr::null_mut()); + bindings::error_propagate(errp, self.clone_to_foreign_ptr()); + } + } + + /// Convert a C `Error*` into a Rust `Result`, using + /// `Ok(())` if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or valid; typically it was initialized + /// with `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { + // SAFETY: caller guarantees c_error is valid + unsafe { Self::err_or_else(c_error, || ()) } + } + + /// Convert a C `Error*` into a Rust `Result`, calling `f()` to + /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or point to a valid C [`struct + /// Error`](bindings::Error); typically it was initialized with + /// `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_else<T, F: FnOnce() -> T>( + c_error: *mut bindings::Error, + f: F, + ) -> Result<T> { + // SAFETY: caller guarantees c_error is valid + let err = unsafe { Option::<Self>::from_foreign(c_error) }; + match err { + None => Ok(f()), + Some(err) => Err(err), + } + } +} + +impl FreeForeign for Error { + type Foreign = bindings::Error; + + unsafe fn free_foreign(p: *mut bindings::Error) { + // SAFETY: caller guarantees p is valid + unsafe { + bindings::error_free(p); + } + } +} + +impl CloneToForeign for Error { + fn clone_to_foreign(&self) -> OwnedPointer<Self> { + // SAFETY: all arguments are controlled by this function + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: format!("{self}").clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: self.file.len() as c_int, + src: self.file.as_ptr().cast::<c_char>(), + line: self.line as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } +} + +impl FromForeign for Error { + unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { + // SAFETY: caller guarantees c_error is valid + unsafe { + let error = &*c_error; + let file = if error.src_len < 0 { + // NUL-terminated + CStr::from_ptr(error.src).to_str() + } else { + // Can become str::from_utf8 with Rust 1.87.0 + std::str::from_utf8(std::slice::from_raw_parts( + &*error.src.cast::<u8>(), + error.src_len as usize, + )) + }; + + Error { + msg: FromForeign::cloned_from_foreign(error.msg), + cause: None, + file: file.unwrap(), + line: error.line as u32, + } + } + } +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use anyhow::anyhow; + use foreign::OwnedPointer; + + use super::*; + use crate::{assert_match, bindings}; + + #[track_caller] + fn error_for_test(msg: &CStr) -> OwnedPointer<Error> { + // SAFETY: all arguments are controlled by this function + let location = panic::Location::caller(); + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: msg.clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: location.file().len() as c_int, + src: location.file().as_ptr().cast::<c_char>(), + line: location.line() as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } + + unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { + unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } + } + + #[test] + #[allow(deprecated)] + fn test_description() { + use std::error::Error; + + assert_eq!(super::Error::from("msg").description(), "msg"); + assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); + } + + #[test] + fn test_display() { + assert_eq!(&*format!("{}", Error::from("msg")), "msg"); + assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); + assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); + + assert_eq!( + &*format!("{}", Error::with_error("msg", anyhow!("cause"))), + "msg: cause" + ); + } + + #[test] + fn test_bool_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_ptr_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); + assert_eq!(String::from_foreign(ret), "abc"); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert_eq!( + Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err), + ptr::null_mut() + ); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_err_or_unit() { + unsafe { + let result = Error::err_or_unit(ptr::null_mut()); + assert_match!(result, Ok(())); + + let err = error_for_test(c"msg"); + let err = Error::err_or_unit(err.into_inner()).unwrap_err(); + assert_eq!(&*format!("{err}"), "msg"); + } + } +} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 1222d4f..1526e6f 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -4,7 +4,11 @@ //! Bindings for interrupt sources -use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr}; +use std::{ + ffi::{c_int, CStr}, + marker::PhantomData, + ptr, +}; use crate::{ bindings::{self, qemu_set_irq}, diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 05f38b5..86dcd8e 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -15,27 +15,30 @@ pub mod prelude; pub mod assertions; pub mod bitops; -pub mod c_str; pub mod callbacks; pub mod cell; pub mod chardev; pub mod errno; +pub mod error; pub mod irq; +pub mod log; pub mod memory; pub mod module; -pub mod offset_of; pub mod qdev; pub mod qom; pub mod sysbus; pub mod timer; +pub mod uninit; pub mod vmstate; pub mod zeroable; use std::{ alloc::{GlobalAlloc, Layout}, - os::raw::c_void, + ffi::c_void, }; +pub use error::{Error, Result}; + #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] extern "C" { fn g_aligned_alloc0( @@ -165,6 +168,3 @@ unsafe impl GlobalAlloc for QemuAllocator { } } } - -#[cfg(has_offset_of)] -pub use core::mem::offset_of; diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs new file mode 100644 index 0000000..d6c3d6c --- /dev/null +++ b/rust/qemu-api/src/log.rs @@ -0,0 +1,73 @@ +// Copyright 2025 Bernhard Beschow <shentey@gmail.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for QEMU's logging infrastructure + +#[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 = crate::bindings::LOG_GUEST_ERROR, + + /// Log guest access of unimplemented functionality. + /// Corresponds to `LOG_UNIMP` in the C implementation. + Unimp = crate::bindings::LOG_UNIMP, +} + +/// 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. +/// +/// # 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 + } { + let formatted_string = format!("{}\n", format_args!($fmt $($args)*)); + let c_string = std::ffi::CString::new(formatted_string).unwrap(); + + unsafe { + ::qemu_api::bindings::qemu_log(c_string.as_ptr()); + } + } + }}; +} diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index fdb1ea1..e40fad6 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -5,9 +5,8 @@ //! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` use std::{ - ffi::{CStr, CString}, + ffi::{c_uint, c_void, CStr, CString}, marker::PhantomData, - os::raw::{c_uint, c_void}, }; pub use bindings::{hwaddr, MemTxAttrs}; @@ -17,6 +16,7 @@ use crate::{ callbacks::FnCall, cell::Opaque, prelude::*, + uninit::MaybeUninitField, zeroable::Zeroable, }; @@ -148,7 +148,7 @@ impl MemoryRegion { #[inline(always)] unsafe fn do_init_io( slot: *mut bindings::MemoryRegion, - owner: *mut Object, + owner: *mut bindings::Object, ops: &'static bindings::MemoryRegionOps, name: &'static str, size: u64, @@ -157,7 +157,7 @@ impl MemoryRegion { let cstr = CString::new(name).unwrap(); memory_region_init_io( slot, - owner.cast::<bindings::Object>(), + owner, ops, owner.cast::<c_void>(), cstr.as_ptr(), @@ -167,16 +167,15 @@ impl MemoryRegion { } pub fn init_io<T: IsA<Object>>( - &mut self, - owner: *mut T, + this: &mut MaybeUninitField<'_, T, Self>, ops: &'static MemoryRegionOps<T>, name: &'static str, size: u64, ) { unsafe { Self::do_init_io( - self.0.as_mut_ptr(), - owner.cast::<Object>(), + this.as_mut_ptr().cast(), + MaybeUninitField::parent_mut(this).cast(), &ops.0, name, size, diff --git a/rust/qemu-api/src/offset_of.rs b/rust/qemu-api/src/offset_of.rs deleted file mode 100644 index 373229b..0000000 --- a/rust/qemu-api/src/offset_of.rs +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: MIT - -#![doc(hidden)] -//! This module provides macros that emulate the functionality of -//! `core::mem::offset_of` on older versions of Rust. -//! -//! Documentation is hidden because it only exposes macros, which -//! are exported directly from `qemu_api`. - -/// This macro provides the same functionality as `core::mem::offset_of`, -/// except that only one level of field access is supported. The declaration -/// of the struct must be wrapped with `with_offsets! { }`. -/// -/// It is needed because `offset_of!` was only stabilized in Rust 1.77. -#[cfg(not(has_offset_of))] -#[macro_export] -macro_rules! offset_of { - ($Container:ty, $field:ident) => { - <$Container>::OFFSET_TO__.$field - }; -} - -/// A wrapper for struct declarations, that allows using `offset_of!` in -/// versions of Rust prior to 1.77 -#[macro_export] -macro_rules! with_offsets { - // This method to generate field offset constants comes from: - // - // https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df - // - // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla - ( - $(#[$struct_meta:meta])* - $struct_vis:vis - struct $StructName:ident { - $( - $(#[$field_meta:meta])* - $field_vis:vis - $field_name:ident : $field_ty:ty - ),* - $(,)? - } - ) => ( - #[cfg(not(has_offset_of))] - const _: () = { - struct StructOffsetsHelper<T>(std::marker::PhantomData<T>); - const END_OF_PREV_FIELD: usize = 0; - - // populate StructOffsetsHelper<T> with associated consts, - // one for each field - $crate::with_offsets! { - @struct $StructName - @names [ $($field_name)* ] - @tys [ $($field_ty ,)*] - } - - // now turn StructOffsetsHelper<T>'s consts into a single struct, - // applying field visibility. This provides better error messages - // than if offset_of! used StructOffsetsHelper::<T> directly. - pub - struct StructOffsets { - $( - $field_vis - $field_name: usize, - )* - } - impl $StructName { - pub - const OFFSET_TO__: StructOffsets = StructOffsets { - $( - $field_name: StructOffsetsHelper::<$StructName>::$field_name, - )* - }; - } - }; - ); - - ( - @struct $StructName:ident - @names [] - @tys [] - ) => (); - - ( - @struct $StructName:ident - @names [$field_name:ident $($other_names:tt)*] - @tys [$field_ty:ty , $($other_tys:tt)*] - ) => ( - #[allow(non_local_definitions)] - #[allow(clippy::modulo_one)] - impl StructOffsetsHelper<$StructName> { - #[allow(nonstandard_style)] - const $field_name: usize = { - const ALIGN: usize = std::mem::align_of::<$field_ty>(); - const TRAIL: usize = END_OF_PREV_FIELD % ALIGN; - END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL }) - }; - } - const _: () = { - const END_OF_PREV_FIELD: usize = - StructOffsetsHelper::<$StructName>::$field_name + - std::mem::size_of::<$field_ty>() - ; - $crate::with_offsets! { - @struct $StructName - @names [$($other_names)*] - @tys [$($other_tys)*] - } - }; - ); -} - -#[cfg(test)] -mod tests { - use crate::offset_of; - - #[repr(C)] - struct Foo { - a: u16, - b: u32, - c: u64, - d: u16, - } - - #[repr(C)] - struct Bar { - pub a: u16, - pub b: u64, - c: Foo, - d: u64, - } - - crate::with_offsets! { - #[repr(C)] - struct Bar { - pub a: u16, - pub b: u64, - c: Foo, - d: u64, - } - } - - #[repr(C)] - pub struct Baz { - b: u32, - a: u8, - } - crate::with_offsets! { - #[repr(C)] - pub struct Baz { - b: u32, - a: u8, - } - } - - #[test] - fn test_offset_of() { - const OFFSET_TO_C: usize = offset_of!(Bar, c); - - assert_eq!(offset_of!(Bar, a), 0); - assert_eq!(offset_of!(Bar, b), 8); - assert_eq!(OFFSET_TO_C, 16); - assert_eq!(offset_of!(Bar, d), 40); - - assert_eq!(offset_of!(Baz, b), 0); - assert_eq!(offset_of!(Baz, a), 4); - } -} diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 43bfcd5..8f9e23e 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -11,6 +11,8 @@ pub use crate::cell::BqlRefCell; pub use crate::errno; +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 18b4a9b..36f02fb 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -5,21 +5,21 @@ //! Bindings to create devices and access device functionality from Rust. use std::{ - ffi::{CStr, CString}, - os::raw::{c_int, c_void}, + ffi::{c_int, c_void, CStr, CString}, ptr::NonNull, }; pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use crate::{ - bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass}, + bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, callbacks::FnCall, cell::{bql_locked, Opaque}, chardev::Chardev, + error::{Error, Result}, irq::InterruptSource, prelude::*, - qom::{ObjectClass, ObjectImpl, Owned}, + qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, vmstate::VMStateDescription, }; @@ -109,7 +109,7 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> { /// /// If not `None`, the parent class's `realize` method is overridden /// with the function pointed to by `REALIZE`. - const REALIZE: Option<fn(&Self)> = None; + const REALIZE: Option<fn(&Self) -> Result<()>> = None; /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants @@ -135,10 +135,13 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> { /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>( dev: *mut bindings::DeviceState, - _errp: *mut *mut Error, + errp: *mut *mut bindings::Error, ) { let state = NonNull::new(dev).unwrap().cast::<T>(); - T::REALIZE.unwrap()(unsafe { state.as_ref() }); + let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); + unsafe { + Error::ok_or_propagate(result, errp); + } } unsafe impl InterfaceType for ResettableClass { @@ -191,7 +194,7 @@ macro_rules! define_property { // use associated function syntax for type checking name: ::std::ffi::CStr::as_ptr($name), info: $prop, - offset: $crate::offset_of!($state, $field) as isize, + offset: ::std::mem::offset_of!($state, $field) as isize, bitnr: $bitnr, set_default: true, defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, @@ -203,7 +206,7 @@ macro_rules! define_property { // use associated function syntax for type checking name: ::std::ffi::CStr::as_ptr($name), info: $prop, - offset: $crate::offset_of!($state, $field) as isize, + 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 @@ -214,7 +217,7 @@ macro_rules! define_property { // use associated function syntax for type checking name: ::std::ffi::CStr::as_ptr($name), info: $prop, - offset: $crate::offset_of!($state, $field) as isize, + offset: ::std::mem::offset_of!($state, $field) as isize, set_default: false, ..$crate::zeroable::Zeroable::ZERO } @@ -244,15 +247,9 @@ unsafe impl ObjectType for DeviceState { } qom_isa!(DeviceState: Object); -/// Trait for methods exposed by the [`DeviceState`] class. The methods can be -/// called on all objects that have the trait `IsA<DeviceState>`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA`. -pub trait DeviceMethods: ObjectDeref -where - Self::Target: IsA<DeviceState>, -{ +/// Initialization methods take a [`ParentInit`] and can be called as +/// associated functions. +impl DeviceState { /// Add an input clock named `name`. Invoke the callback with /// `self` as the first parameter for the events that are requested. /// @@ -263,12 +260,15 @@ where /// which Rust code has a reference to a child object) it would be /// possible for this function to return a `&Clock` too. #[inline] - fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>( - &self, + pub fn init_clock_in<T: DeviceImpl, F: for<'a> FnCall<(&'a T, ClockEvent)>>( + this: &mut ParentInit<T>, name: &str, _cb: &F, events: ClockEvent, - ) -> Owned<Clock> { + ) -> Owned<Clock> + where + T::ParentType: IsA<DeviceState>, + { fn do_init_clock_in( dev: &DeviceState, name: &str, @@ -284,10 +284,10 @@ where unsafe { let cstr = CString::new(name).unwrap(); let clk = bindings::qdev_init_clock_in( - dev.as_mut_ptr(), + dev.0.as_mut_ptr(), cstr.as_ptr(), cb, - dev.as_void_ptr(), + dev.0.as_void_ptr(), events.0, ); @@ -304,12 +304,12 @@ where // SAFETY: the opaque is "this", which is indeed a pointer to T F::call((unsafe { &*(opaque.cast::<T>()) }, event)) } - Some(rust_clock_cb::<Self::Target, F>) + Some(rust_clock_cb::<T, F>) } else { None }; - do_init_clock_in(self.upcast(), name, cb, events) + do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events) } /// Add an output clock named `name`. @@ -321,16 +321,30 @@ where /// which Rust code has a reference to a child object) it would be /// possible for this function to return a `&Clock` too. #[inline] - fn init_clock_out(&self, name: &str) -> Owned<Clock> { + pub fn init_clock_out<T: DeviceImpl>(this: &mut ParentInit<T>, name: &str) -> Owned<Clock> + where + T::ParentType: IsA<DeviceState>, + { unsafe { let cstr = CString::new(name).unwrap(); - let clk = bindings::qdev_init_clock_out(self.upcast().as_mut_ptr(), cstr.as_ptr()); + let dev: &mut DeviceState = this.upcast_mut(); + let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr()); let clk: &Clock = Clock::from_raw(clk); Owned::from(clk) } } +} +/// Trait for methods exposed by the [`DeviceState`] class. The methods can be +/// called on all objects that have the trait `IsA<DeviceState>`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA`. +pub trait DeviceMethods: ObjectDeref +where + Self::Target: IsA<DeviceState>, +{ fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) { assert!(bql_locked()); let c_propname = CString::new(propname).unwrap(); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index f1b4022..e20ee01 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -93,11 +93,11 @@ //! without incurring into violations of orphan rules for traits. use std::{ - ffi::CStr, + ffi::{c_void, CStr}, fmt, - mem::ManuallyDrop, + marker::PhantomData, + mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, - os::raw::c_void, ptr::NonNull, }; @@ -207,13 +207,190 @@ impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> { } } +/// This struct knows that the superclasses of the object have already been +/// initialized. +/// +/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a +/// technique that is found in several crates, the main ones probably being +/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) +/// and `generativity`. +/// +/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to +/// the lifetime argument `'init`. This, together with the `for<'...>` in +/// `[ParentInit::with]`, block any attempt of the compiler to be creative when +/// operating on types of type `ParentInit` and to extend their lifetimes. In +/// particular, it ensures that the `ParentInit` cannot be made to outlive the +/// `rust_instance_init()` function that creates it, and therefore that the +/// `&'init T` reference is valid. +/// +/// This implementation of the same concept, without the QOM baggage, can help +/// understanding the effect: +/// +/// ``` +/// use std::marker::PhantomData; +/// +/// #[derive(PartialEq, Eq)] +/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData<fn(&'closure ()) -> &'closure ()>); +/// +/// impl<'closure, T: Copy> Jail<'closure, T> { +/// fn get(&self) -> T { +/// *self.0 +/// } +/// +/// #[inline] +/// fn with<U>(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { +/// let parent_init = Jail(&v, PhantomData); +/// f(parent_init) +/// } +/// } +/// ``` +/// +/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the +/// closure: +/// +/// ```ignore +/// let x = 42; +/// let escape = Jail::with(&x, |token1| { +/// println!("{}", token1.get()); +/// // fails to compile... +/// token1 +/// }); +/// // ... so you cannot do this: +/// println!("{}", escape.get()); +/// ``` +/// +/// Likewise, in the QOM case the `ParentInit` cannot be moved out of +/// `instance_init()`. Without this trick it would be possible to stash a +/// `ParentInit` and use it later to access uninitialized memory. +/// +/// Here is another example, showing how separately-created "identities" stay +/// isolated: +/// +/// ```ignore +/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { +/// fn clone(&self) -> Jail<'closure, T> { +/// Jail(self.0, PhantomData) +/// } +/// } +/// +/// fn main() { +/// Jail::with(42, |token1| { +/// // this works and returns true: the clone has the same "identity" +/// println!("{}", token1 == token1.clone()); +/// Jail::with(42, |token2| { +/// // here the outer token remains accessible... +/// println!("{}", token1.get()); +/// // ... but the two are separate: this fails to compile: +/// println!("{}", token1 == token2); +/// }); +/// }); +/// } +/// ``` +pub struct ParentInit<'init, T>( + &'init mut MaybeUninit<T>, + PhantomData<fn(&'init ()) -> &'init ()>, +); + +impl<'init, T> ParentInit<'init, T> { + #[inline] + pub fn with(obj: &'init mut MaybeUninit<T>, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { + let parent_init = ParentInit(obj, PhantomData); + f(parent_init) + } +} + +impl<T: ObjectType> ParentInit<'_, T> { + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object_ptr().cast_mut() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub fn as_object_ptr(&self) -> *const bindings::Object { + self.0.as_ptr().cast() + } +} + +impl<'a, T: ObjectImpl> ParentInit<'a, T> { + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast<U: ObjectType>(&self) -> &'a U + where + T::ParentType: IsA<U>, + { + // SAFETY: soundness is declared via IsA<U>, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &*(self.0.as_ptr().cast::<U>()) } + } + + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast_mut<U: ObjectType>(&mut self) -> &'a mut U + where + T::ParentType: IsA<U>, + { + // SAFETY: soundness is declared via IsA<U>, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &mut *(self.0.as_mut_ptr().cast::<U>()) } + } +} + +impl<T> Deref for ParentInit<'_, T> { + type Target = MaybeUninit<T>; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<T> DerefMut for ParentInit<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut bindings::Object) { - let mut state = NonNull::new(obj).unwrap().cast::<T>(); + let mut state = NonNull::new(obj).unwrap().cast::<MaybeUninit<T>>(); + // SAFETY: obj is an instance of T, since rust_instance_init<T> // is called from QOM core as the instance_init function // for class T unsafe { - T::INSTANCE_INIT.unwrap()(state.as_mut()); + ParentInit::with(state.as_mut(), |parent_init| { + T::INSTANCE_INIT.unwrap()(parent_init); + }); } } @@ -292,7 +469,7 @@ pub unsafe trait ObjectType: Sized { } /// Return the receiver as a const raw pointer to Object. - /// This is preferrable to `as_object_mut_ptr()` if a C + /// This is preferable to `as_object_mut_ptr()` if a C /// function only needs a `const Object *`. fn as_object_ptr(&self) -> *const bindings::Object { self.as_object().as_ptr() @@ -389,7 +566,7 @@ where { #[allow(clippy::as_ptr_cast_mut)] { - self.as_ptr::<U>() as *mut _ + self.as_ptr::<U>().cast_mut() } } } @@ -480,13 +657,13 @@ pub trait ObjectImpl: ObjectType + IsA<Object> { /// /// FIXME: The argument is not really a valid reference. `&mut /// MaybeUninit<Self>` would be a better description. - const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = None; + const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = None; /// Function that is called to finish initialization of an object, once /// `INSTANCE_INIT` functions have been called. const INSTANCE_POST_INIT: Option<fn(&Self)> = None; - /// Called on descendent classes after all parent class initialization + /// Called on descendant classes after all parent class initialization /// has occurred, but before the class itself is initialized. This /// is only useful if a class is not a leaf, and can be used to undo /// the effects of copying the contents of the parent's class struct @@ -535,9 +712,10 @@ pub trait ObjectImpl: ObjectType + IsA<Object> { /// While `klass`'s parent class is initialized on entry, the other fields /// are all zero; it is therefore assumed that all fields in `T` can be /// zeroed, otherwise it would not be possible to provide the class as a - /// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable) - /// to T; this is more easily done once Zeroable does not require a manual - /// implementation (Rust 1.75.0). + /// `&mut T`. TODO: it may be possible to add an unsafe trait that checks + /// that all fields *after the parent class* (but not the parent class + /// itself) are Zeroable. This unsafe trait can be added via a derive + /// macro. const CLASS_INIT: fn(&mut Self::Class); } @@ -638,7 +816,7 @@ impl<T: ObjectType> Owned<T> { // SAFETY NOTE: while NonNull requires a mutable pointer, only // Deref is implemented so the pointer passed to from_raw // remains const - Owned(NonNull::new(ptr as *mut T).unwrap()) + Owned(NonNull::new(ptr.cast_mut()).unwrap()) } /// Obtain a raw C pointer from a reference. `src` is consumed diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index e769f8b..0a2d111 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -1,9 +1,9 @@ // Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu <zhai1.liu@intel.com> +// Author(s): Zhao Liu <zhao1.liu@intel.com> // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - os::raw::{c_int, c_void}, + ffi::{c_int, c_void}, pin::Pin, }; @@ -81,7 +81,7 @@ impl Timer { scale as c_int, attributes as c_int, Some(timer_cb), - (opaque as *const T).cast::<c_void>() as *mut c_void, + (opaque as *const T).cast::<c_void>().cast_mut(), ) } } diff --git a/rust/qemu-api/src/uninit.rs b/rust/qemu-api/src/uninit.rs new file mode 100644 index 0000000..04123b4 --- /dev/null +++ b/rust/qemu-api/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<T>, + child: *mut U, +} + +impl<'a, T, U> MaybeUninitField<'a, T, U> { + #[doc(hidden)] + pub fn new(parent: &'a mut MaybeUninit<T>, 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 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 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<U>; + + fn deref(&self) -> &MaybeUninit<U> { + // 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<U> { + // 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<S> = 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 9ae97c3..812f390 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -9,7 +9,7 @@ //! * [`vmstate_unused!`](crate::vmstate_unused) and //! [`vmstate_of!`](crate::vmstate_of), which are used to express the //! migration format for a struct. This is based on the [`VMState`] trait, -//! which is defined by all migrateable types. +//! which is defined by all migratable types. //! //! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and //! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with @@ -25,7 +25,7 @@ //! functionality that is missing from `vmstate_of!`. use core::{marker::PhantomData, mem, ptr::NonNull}; -use std::os::raw::{c_int, c_void}; +use std::ffi::{c_int, c_void}; pub use crate::bindings::{VMStateDescription, VMStateField}; use crate::{ @@ -205,8 +205,8 @@ macro_rules! vmstate_of { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, - offset: $crate::offset_of!($struct_name, $field_name), - $(num_offset: $crate::offset_of!($struct_name, $num),)? + offset: ::std::mem::offset_of!($struct_name, $field_name), + $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? // The calls to `call_func_with_field!` are the magic that // computes most of the VMStateField from the type of the field. @@ -427,7 +427,7 @@ unsafe impl<T: VMState, const N: usize> VMState for [T; N] { macro_rules! vmstate_unused { ($size:expr) => {{ $crate::bindings::VMStateField { - name: $crate::c_str!("unused").as_ptr(), + name: c"unused".as_ptr(), size: $size, info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, flags: $crate::bindings::VMStateFlags::VMS_BUFFER, @@ -483,10 +483,10 @@ macro_rules! vmstate_struct { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, - $(num_offset: $crate::offset_of!($struct_name, $num),)? + $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? offset: { $crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?); - $crate::offset_of!($struct_name, $field_name) + ::std::mem::offset_of!($struct_name, $field_name) }, size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, @@ -518,7 +518,7 @@ macro_rules! vmstate_clock { $field_name, $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)? ); - $crate::offset_of!($struct_name, $field_name) + ::std::mem::offset_of!($struct_name, $field_name) }, size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), flags: $crate::bindings::VMStateFlags( diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index a3415a2..d8239d0 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -4,89 +4,17 @@ /// Encapsulates the requirement that /// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined -/// behavior. This trait in principle could be implemented as just: -/// -/// ``` -/// pub unsafe trait Zeroable: Default { -/// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() }; -/// } -/// ``` -/// -/// The need for a manual implementation is only because `zeroed()` cannot -/// be used as a `const fn` prior to Rust 1.75.0. Once we can assume a new -/// enough version of the compiler, we could provide a `#[derive(Zeroable)]` -/// macro to check at compile-time that all struct fields are Zeroable, and -/// use the above blanket implementation of the `ZERO` constant. +/// behavior. /// /// # Safety /// -/// Because the implementation of `ZERO` is manual, it does not make -/// any assumption on the safety of `zeroed()`. However, other users of the -/// trait could use it that way. Do not add this trait to a type unless -/// all-zeroes is a valid value for the type. In particular, remember that -/// raw pointers can be zero, but references and `NonNull<T>` cannot +/// Do not add this trait to a type unless all-zeroes is a valid value for the +/// type. In particular, raw pointers can be zero, but references and +/// `NonNull<T>` cannot. pub unsafe trait Zeroable: Default { - const ZERO: Self; -} - -/// A macro that acts similarly to [`core::mem::zeroed()`], only is const -/// -/// ## Safety -/// -/// Similar to `core::mem::zeroed()`, except this zeroes padding bits. Zeroed -/// padding usually isn't relevant to safety, but might be if a C union is used. -/// -/// Just like for `core::mem::zeroed()`, an all zero byte pattern might not -/// be a valid value for a type, as is the case for references `&T` and `&mut -/// T`. Reference types trigger a (denied by default) lint and cause immediate -/// undefined behavior if the lint is ignored -/// -/// ```rust compile_fail -/// use const_zero::const_zero; -/// // error: any use of this value will cause an error -/// // note: `#[deny(const_err)]` on by default -/// const STR: &str = unsafe{const_zero!(&'static str)}; -/// ``` -/// -/// `const_zero` does not work on unsized types: -/// -/// ```rust compile_fail -/// use const_zero::const_zero; -/// // error[E0277]: the size for values of type `[u8]` cannot be known at compilation time -/// const BYTES: [u8] = unsafe{const_zero!([u8])}; -/// ``` -/// ## Differences with `core::mem::zeroed` -/// -/// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't -#[macro_export] -macro_rules! const_zero { - // This macro to produce a type-generic zero constant is taken from the - // const_zero crate (v0.1.1): - // - // https://docs.rs/const-zero/latest/src/const_zero/lib.rs.html - // - // and used under MIT license - ($type_:ty) => {{ - const TYPE_SIZE: ::core::primitive::usize = ::core::mem::size_of::<$type_>(); - union TypeAsBytes { - bytes: [::core::primitive::u8; TYPE_SIZE], - inner: ::core::mem::ManuallyDrop<$type_>, - } - const ZERO_BYTES: TypeAsBytes = TypeAsBytes { - bytes: [0; TYPE_SIZE], - }; - ::core::mem::ManuallyDrop::<$type_>::into_inner(ZERO_BYTES.inner) - }}; -} - -/// A wrapper to implement the `Zeroable` trait through the `const_zero` macro. -#[macro_export] -macro_rules! impl_zeroable { - ($type:ty) => { - unsafe impl $crate::zeroable::Zeroable for $type { - const ZERO: Self = unsafe { $crate::const_zero!($type) }; - } - }; + /// Return a value of Self whose memory representation consists of all + /// zeroes, with the possible exclusion of padding bytes. + const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() }; } // bindgen does not derive Default here @@ -97,13 +25,13 @@ impl Default for crate::bindings::VMStateFlags { } } -impl_zeroable!(crate::bindings::Property__bindgen_ty_1); -impl_zeroable!(crate::bindings::Property); -impl_zeroable!(crate::bindings::VMStateFlags); -impl_zeroable!(crate::bindings::VMStateField); -impl_zeroable!(crate::bindings::VMStateDescription); -impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1); -impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2); -impl_zeroable!(crate::bindings::MemoryRegionOps); -impl_zeroable!(crate::bindings::MemTxAttrs); -impl_zeroable!(crate::bindings::CharBackend); +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 {} |