aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api
diff options
context:
space:
mode:
Diffstat (limited to 'rust/qemu-api')
-rw-r--r--rust/qemu-api/Cargo.toml1
-rw-r--r--rust/qemu-api/meson.build6
-rw-r--r--rust/qemu-api/src/assertions.rs94
-rw-r--r--rust/qemu-api/src/bindings.rs48
-rw-r--r--rust/qemu-api/src/c_str.rs8
-rw-r--r--rust/qemu-api/src/callbacks.rs97
-rw-r--r--rust/qemu-api/src/chardev.rs19
-rw-r--r--rust/qemu-api/src/irq.rs26
-rw-r--r--rust/qemu-api/src/lib.rs5
-rw-r--r--rust/qemu-api/src/memory.rs203
-rw-r--r--rust/qemu-api/src/offset_of.rs7
-rw-r--r--rust/qemu-api/src/prelude.rs10
-rw-r--r--rust/qemu-api/src/qdev.rs284
-rw-r--r--rust/qemu-api/src/qom.rs330
-rw-r--r--rust/qemu-api/src/sysbus.rs60
-rw-r--r--rust/qemu-api/src/timer.rs98
-rw-r--r--rust/qemu-api/src/vmstate.rs655
-rw-r--r--rust/qemu-api/src/zeroable.rs120
-rw-r--r--rust/qemu-api/tests/tests.rs99
19 files changed, 1796 insertions, 374 deletions
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 4aa22f3..a51dd14 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -12,6 +12,7 @@ resolver = "2"
publish = false
keywords = []
categories = []
+rust-version = "1.63.0"
[dependencies]
qemu_api_macros = { path = "../qemu-api-macros" }
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index ccb20f3..2e9c107 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -7,7 +7,7 @@ if rustc.version().version_compare('>=1.77.0')
_qemu_api_cfg += ['--cfg', 'has_offset_of']
endif
if get_option('debug_mutex')
- _qemu_api_cfg += ['--feature', 'debug_cell']
+ _qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
endif
_qemu_api_rs = static_library(
@@ -15,18 +15,22 @@ _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/c_str.rs',
'src/irq.rs',
+ 'src/memory.rs',
'src/module.rs',
'src/offset_of.rs',
'src/prelude.rs',
'src/qdev.rs',
'src/qom.rs',
'src/sysbus.rs',
+ 'src/timer.rs',
'src/vmstate.rs',
'src/zeroable.rs',
],
diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs
new file mode 100644
index 0000000..fa1a18d
--- /dev/null
+++ b/rust/qemu-api/src/assertions.rs
@@ -0,0 +1,94 @@
+// 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 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<T> 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!(<Box<u32> as Deref>::Target, u32);
+/// ```
+///
+/// Different types will cause a compile failure
+///
+/// ```compile_fail
+/// # use qemu_api::assert_same_type;
+/// assert_same_type!(&Box<u32>, &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, U>(_: T)
+ where
+ T: $crate::assertions::EqType<Itself = U>,
+ {
+ }
+ 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 {
+ ($t:ty, $i:tt, $ti:ty) => {
+ const _: () = {
+ #[allow(unused)]
+ fn assert_field_type(v: $t) {
+ fn types_must_be_equal<T, U>(_: T)
+ where
+ T: $crate::assertions::EqType<Itself = U>,
+ {
+ }
+ types_must_be_equal::<_, $ti>(v.$i);
+ }
+ };
+ };
+}
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 8a9b821..d286863 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -15,15 +15,63 @@
clippy::missing_safety_doc
)]
+//! `bindgen`-generated declarations.
+
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+// SAFETY: these are implemented in C; the bindings need to assert that the
+// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
+unsafe impl Send for BusState {}
+unsafe impl Sync for BusState {}
+
+unsafe impl Send for CharBackend {}
+unsafe impl Sync for CharBackend {}
+
+unsafe impl Send for Chardev {}
+unsafe impl Sync for Chardev {}
+
+unsafe impl Send for Clock {}
+unsafe impl Sync for Clock {}
+
+unsafe impl Send for DeviceState {}
+unsafe impl Sync for DeviceState {}
+
+unsafe impl Send for MemoryRegion {}
+unsafe impl Sync for MemoryRegion {}
+
+unsafe impl Send for ObjectClass {}
+unsafe impl Sync for ObjectClass {}
+
+unsafe impl Send for Object {}
+unsafe impl Sync for Object {}
+
+unsafe impl Send for SysBusDevice {}
+unsafe impl Sync for SysBusDevice {}
+
+// SAFETY: this is a pure data struct
+unsafe impl Send for CoalescedMemoryRange {}
+unsafe impl Sync for CoalescedMemoryRange {}
+
+// SAFETY: these are constants and vtables; the Send and Sync requirements
+// are deferred to the unsafe callbacks that they contain
+unsafe impl Send for MemoryRegionOps {}
+unsafe impl Sync for MemoryRegionOps {}
+
unsafe impl Send for Property {}
unsafe impl Sync for Property {}
+
+unsafe impl Send for TypeInfo {}
unsafe impl Sync for TypeInfo {}
+
+unsafe impl Send for VMStateDescription {}
unsafe impl Sync for VMStateDescription {}
+
+unsafe impl Send for VMStateField {}
unsafe impl Sync for VMStateField {}
+
+unsafe impl Send for VMStateInfo {}
unsafe impl Sync for VMStateInfo {}
diff --git a/rust/qemu-api/src/c_str.rs b/rust/qemu-api/src/c_str.rs
index 4cd96da..3fa61b5 100644
--- a/rust/qemu-api/src/c_str.rs
+++ b/rust/qemu-api/src/c_str.rs
@@ -2,6 +2,14 @@
// 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`.
diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs
index 314f9dc..9642a16 100644
--- a/rust/qemu-api/src/callbacks.rs
+++ b/rust/qemu-api/src/callbacks.rs
@@ -79,6 +79,31 @@ use std::{mem, ptr::NonNull};
/// call_it(&move |_| String::from(x), "hello workd");
/// ```
///
+/// `()` can be used to indicate "no function":
+///
+/// ```
+/// # use qemu_api::callbacks::FnCall;
+/// fn optional<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option<String> {
+/// 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<F: for<'a> 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
@@ -93,10 +118,70 @@ pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
/// Rust 1.79.0+.
const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 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<F: for<'a> 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
+ ///
+ /// You can use `IS_SOME` to catch this at compile time:
+ ///
+ /// ```compile_fail
+ /// # use qemu_api::callbacks::FnCall;
+ /// fn call_it<F: for<'a> 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<Args, R> FnCall<Args, R> 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,
@@ -106,6 +191,8 @@ macro_rules! impl_call {
where
F: 'static + Sync + Sized + Fn($($args, )*) -> R,
{
+ const IS_SOME: bool = true;
+
#[inline(always)]
fn call(a: ($($args,)*)) -> R {
let _: () = Self::ASSERT_ZERO_SIZED;
@@ -141,4 +228,14 @@ mod tests {
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/chardev.rs b/rust/qemu-api/src/chardev.rs
new file mode 100644
index 0000000..74cfb63
--- /dev/null
+++ b/rust/qemu-api/src/chardev.rs
@@ -0,0 +1,19 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Bindings for character devices
+
+use std::ffi::CStr;
+
+use crate::{bindings, prelude::*};
+
+pub type Chardev = bindings::Chardev;
+pub type ChardevClass = bindings::ChardevClass;
+
+unsafe impl ObjectType for Chardev {
+ type Class = ChardevClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CHARDEV) };
+}
+qom_isa!(Chardev: Object);
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index 6258141..d1c9dc9 100644
--- a/rust/qemu-api/src/irq.rs
+++ b/rust/qemu-api/src/irq.rs
@@ -5,11 +5,12 @@
//! Bindings for interrupt sources
use core::ptr;
-use std::{marker::PhantomData, os::raw::c_int};
+use std::{ffi::CStr, marker::PhantomData, os::raw::c_int};
use crate::{
- bindings::{qemu_set_irq, IRQState},
+ bindings::{self, qemu_set_irq},
prelude::*,
+ qom::ObjectClass,
};
/// Interrupt sources are used by devices to pass changes to a value (typically
@@ -21,11 +22,11 @@ use crate::{
/// method sends a `true` value to the sink. If the guest has to see a
/// different polarity, that change is performed by the board between the
/// device and the interrupt controller.
-///
+pub type IRQState = bindings::IRQState;
+
/// Interrupts are implemented as a pointer to the interrupt "sink", which has
/// type [`IRQState`]. A device exposes its source as a QOM link property using
-/// a function such as
-/// [`SysBusDevice::init_irq`](crate::sysbus::SysBusDevice::init_irq), and
+/// a function such as [`SysBusDeviceMethods::init_irq`], and
/// initially leaves the pointer to a NULL value, representing an unconnected
/// interrupt. To connect it, whoever creates the device fills the pointer with
/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because
@@ -44,6 +45,9 @@ where
_marker: PhantomData<T>,
}
+// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken
+unsafe impl<T> Sync for InterruptSource<T> where c_int: From<T> {}
+
impl InterruptSource<bool> {
/// Send a low (`false`) value to the interrupt sink.
pub fn lower(&self) {
@@ -79,6 +83,11 @@ where
pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState {
self.cell.as_ptr()
}
+
+ pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut IRQState {
+ assert!(!slice.is_empty());
+ slice[0].as_ptr()
+ }
}
impl Default for InterruptSource {
@@ -89,3 +98,10 @@ impl Default for InterruptSource {
}
}
}
+
+unsafe impl ObjectType for IRQState {
+ type Class = ObjectClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) };
+}
+qom_isa!(IRQState: Object);
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 4b43e02..ed1a8f9 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
+#![deny(clippy::missing_const_for_fn)]
#[rustfmt::skip]
pub mod bindings;
@@ -12,16 +13,20 @@ pub mod bindings;
#[rustfmt::skip]
pub mod prelude;
+pub mod assertions;
pub mod bitops;
pub mod c_str;
pub mod callbacks;
pub mod cell;
+pub mod chardev;
pub mod irq;
+pub mod memory;
pub mod module;
pub mod offset_of;
pub mod qdev;
pub mod qom;
pub mod sysbus;
+pub mod timer;
pub mod vmstate;
pub mod zeroable;
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
new file mode 100644
index 0000000..682951a
--- /dev/null
+++ b/rust/qemu-api/src/memory.rs
@@ -0,0 +1,203 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs`
+
+use std::{
+ ffi::{CStr, CString},
+ marker::{PhantomData, PhantomPinned},
+ os::raw::{c_uint, c_void},
+ ptr::addr_of,
+};
+
+pub use bindings::{hwaddr, MemTxAttrs};
+
+use crate::{
+ bindings::{self, device_endian, memory_region_init_io},
+ callbacks::FnCall,
+ prelude::*,
+ zeroable::Zeroable,
+};
+
+pub struct MemoryRegionOps<T>(
+ bindings::MemoryRegionOps,
+ // Note: quite often you'll see PhantomData<fn(&T)> mentioned when discussing
+ // covariance and contravariance; you don't need any of those to understand
+ // this usage of PhantomData. Quite simply, MemoryRegionOps<T> *logically*
+ // holds callbacks that take an argument of type &T, except the type is erased
+ // before the callback is stored in the bindings::MemoryRegionOps field.
+ // The argument of PhantomData is a function pointer in order to represent
+ // that relationship; while that will also provide desirable and safe variance
+ // for T, variance is not the point but just a consequence.
+ PhantomData<fn(&T)>,
+);
+
+// SAFETY: When a *const T is passed to the callbacks, the call itself
+// is done in a thread-safe manner. The invocation is okay as long as
+// T itself is `Sync`.
+unsafe impl<T: Sync> Sync for MemoryRegionOps<T> {}
+
+#[derive(Clone)]
+pub struct MemoryRegionOpsBuilder<T>(bindings::MemoryRegionOps, PhantomData<fn(&T)>);
+
+unsafe extern "C" fn memory_region_ops_read_cb<T, F: for<'a> FnCall<(&'a T, hwaddr, u32), u64>>(
+ opaque: *mut c_void,
+ addr: hwaddr,
+ size: c_uint,
+) -> u64 {
+ F::call((unsafe { &*(opaque.cast::<T>()) }, addr, size))
+}
+
+unsafe extern "C" fn memory_region_ops_write_cb<T, F: for<'a> FnCall<(&'a T, hwaddr, u64, u32)>>(
+ opaque: *mut c_void,
+ addr: hwaddr,
+ data: u64,
+ size: c_uint,
+) {
+ F::call((unsafe { &*(opaque.cast::<T>()) }, addr, data, size))
+}
+
+impl<T> MemoryRegionOpsBuilder<T> {
+ #[must_use]
+ pub const fn read<F: for<'a> FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self {
+ self.0.read = Some(memory_region_ops_read_cb::<T, F>);
+ self
+ }
+
+ #[must_use]
+ pub const fn write<F: for<'a> FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self {
+ self.0.write = Some(memory_region_ops_write_cb::<T, F>);
+ self
+ }
+
+ #[must_use]
+ pub const fn big_endian(mut self) -> Self {
+ self.0.endianness = device_endian::DEVICE_BIG_ENDIAN;
+ self
+ }
+
+ #[must_use]
+ pub const fn little_endian(mut self) -> Self {
+ self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN;
+ self
+ }
+
+ #[must_use]
+ pub const fn native_endian(mut self) -> Self {
+ self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN;
+ self
+ }
+
+ #[must_use]
+ pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self {
+ self.0.valid.min_access_size = min;
+ self.0.valid.max_access_size = max;
+ self
+ }
+
+ #[must_use]
+ pub const fn valid_unaligned(mut self) -> Self {
+ self.0.valid.unaligned = true;
+ self
+ }
+
+ #[must_use]
+ pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self {
+ self.0.impl_.min_access_size = min;
+ self.0.impl_.max_access_size = max;
+ self
+ }
+
+ #[must_use]
+ pub const fn impl_unaligned(mut self) -> Self {
+ self.0.impl_.unaligned = true;
+ self
+ }
+
+ #[must_use]
+ pub const fn build(self) -> MemoryRegionOps<T> {
+ MemoryRegionOps::<T>(self.0, PhantomData)
+ }
+
+ #[must_use]
+ pub const fn new() -> Self {
+ Self(bindings::MemoryRegionOps::ZERO, PhantomData)
+ }
+}
+
+impl<T> Default for MemoryRegionOpsBuilder<T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// A safe wrapper around [`bindings::MemoryRegion`]. Compared to the
+/// underlying C struct it is marked as pinned because the QOM tree
+/// contains a pointer to it.
+pub struct MemoryRegion {
+ inner: bindings::MemoryRegion,
+ _pin: PhantomPinned,
+}
+
+impl MemoryRegion {
+ // inline to ensure that it is not included in tests, which only
+ // link to hwcore and qom. FIXME: inlining is actually the opposite
+ // of what we want, since this is the type-erased version of the
+ // init_io function below. Look into splitting the qemu_api crate.
+ #[inline(always)]
+ unsafe fn do_init_io(
+ slot: *mut bindings::MemoryRegion,
+ owner: *mut Object,
+ ops: &'static bindings::MemoryRegionOps,
+ name: &'static str,
+ size: u64,
+ ) {
+ unsafe {
+ let cstr = CString::new(name).unwrap();
+ memory_region_init_io(
+ slot,
+ owner.cast::<Object>(),
+ ops,
+ owner.cast::<c_void>(),
+ cstr.as_ptr(),
+ size,
+ );
+ }
+ }
+
+ pub fn init_io<T: IsA<Object>>(
+ &mut self,
+ owner: *mut T,
+ ops: &'static MemoryRegionOps<T>,
+ name: &'static str,
+ size: u64,
+ ) {
+ unsafe {
+ Self::do_init_io(&mut self.inner, owner.cast::<Object>(), &ops.0, name, size);
+ }
+ }
+
+ pub(crate) const fn as_mut_ptr(&self) -> *mut bindings::MemoryRegion {
+ addr_of!(self.inner) as *mut _
+ }
+}
+
+unsafe impl ObjectType for MemoryRegion {
+ type Class = bindings::MemoryRegionClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) };
+}
+qom_isa!(MemoryRegion: Object);
+
+/// A special `MemTxAttrs` constant, used to indicate that no memory
+/// attributes are specified.
+///
+/// Bus masters which don't specify any attributes will get this,
+/// which has all attribute bits clear except the topmost one
+/// (so that we can distinguish "all attributes deliberately clear"
+/// from "didn't specify" if necessary).
+pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs {
+ unspecified: true,
+ ..Zeroable::ZERO
+};
diff --git a/rust/qemu-api/src/offset_of.rs b/rust/qemu-api/src/offset_of.rs
index 075e98f..373229b 100644
--- a/rust/qemu-api/src/offset_of.rs
+++ b/rust/qemu-api/src/offset_of.rs
@@ -1,5 +1,12 @@
// 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! { }`.
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 6f32dee..fbf0ee2 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -2,17 +2,27 @@
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
// SPDX-License-Identifier: GPL-2.0-or-later
+//! 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::qdev::DeviceMethods;
+
+pub use crate::qom::InterfaceType;
pub use crate::qom::IsA;
pub use crate::qom::Object;
pub use crate::qom::ObjectCast;
pub use crate::qom::ObjectCastMut;
pub use crate::qom::ObjectDeref;
+pub use crate::qom::ObjectClassMethods;
pub use crate::qom::ObjectMethods;
pub use crate::qom::ObjectType;
pub use crate::qom_isa;
+
+pub use crate::sysbus::SysBusDeviceMethods;
+
+pub use crate::vmstate::VMState;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 686054e..3a7aa4d 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -4,33 +4,96 @@
//! Bindings to create devices and access device functionality from Rust.
-use std::ffi::CStr;
+use std::{
+ ffi::{CStr, CString},
+ os::raw::{c_int, c_void},
+ ptr::NonNull,
+};
-pub use bindings::{DeviceClass, DeviceState, Property};
+pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType};
use crate::{
- bindings::{self, Error},
+ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass},
+ callbacks::FnCall,
+ cell::bql_locked,
+ chardev::Chardev,
+ irq::InterruptSource,
prelude::*,
- qom::{ClassInitImpl, ObjectClass},
+ qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned},
vmstate::VMStateDescription,
};
+/// Trait providing the contents of the `ResettablePhases` struct,
+/// which is part of the QOM `Resettable` interface.
+pub trait ResettablePhasesImpl {
+ /// If not None, this is called when the object enters reset. It
+ /// can reset local state of the object, but it must not do anything that
+ /// has a side-effect on other objects, such as raising or lowering an
+ /// [`InterruptSource`], or reading or writing guest memory. It takes the
+ /// reset's type as argument.
+ const ENTER: Option<fn(&Self, ResetType)> = None;
+
+ /// If not None, this is called when the object for entry into reset, once
+ /// every object in the system which is being reset has had its
+ /// `ResettablePhasesImpl::ENTER` method called. At this point devices
+ /// can do actions that affect other objects.
+ ///
+ /// If in doubt, implement this method.
+ const HOLD: Option<fn(&Self, ResetType)> = None;
+
+ /// If not None, this phase is called when the object leaves the reset
+ /// state. Actions affecting other objects are permitted.
+ const EXIT: Option<fn(&Self, ResetType)> = None;
+}
+
+/// # Safety
+///
+/// We expect the FFI user of this function to pass a valid pointer that
+/// can be downcasted to type `T`. We also expect the device is
+/// readable/writeable from one thread at any time.
+unsafe extern "C" fn rust_resettable_enter_fn<T: ResettablePhasesImpl>(
+ obj: *mut Object,
+ typ: ResetType,
+) {
+ let state = NonNull::new(obj).unwrap().cast::<T>();
+ T::ENTER.unwrap()(unsafe { state.as_ref() }, typ);
+}
+
+/// # Safety
+///
+/// We expect the FFI user of this function to pass a valid pointer that
+/// can be downcasted to type `T`. We also expect the device is
+/// readable/writeable from one thread at any time.
+unsafe extern "C" fn rust_resettable_hold_fn<T: ResettablePhasesImpl>(
+ obj: *mut Object,
+ typ: ResetType,
+) {
+ let state = NonNull::new(obj).unwrap().cast::<T>();
+ T::HOLD.unwrap()(unsafe { state.as_ref() }, typ);
+}
+
+/// # Safety
+///
+/// We expect the FFI user of this function to pass a valid pointer that
+/// can be downcasted to type `T`. We also expect the device is
+/// readable/writeable from one thread at any time.
+unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>(
+ obj: *mut Object,
+ typ: ResetType,
+) {
+ let state = NonNull::new(obj).unwrap().cast::<T>();
+ T::EXIT.unwrap()(unsafe { state.as_ref() }, typ);
+}
+
/// Trait providing the contents of [`DeviceClass`].
-pub trait DeviceImpl {
+pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl {
/// _Realization_ is the second stage of device creation. It contains
/// all operations that depend on device properties and can fail (note:
/// this is not yet supported for Rust devices).
///
/// If not `None`, the parent class's `realize` method is overridden
/// with the function pointed to by `REALIZE`.
- const REALIZE: Option<fn(&mut Self)> = None;
-
- /// If not `None`, the parent class's `reset` method is overridden
- /// with the function pointed to by `RESET`.
- ///
- /// Rust does not yet support the three-phase reset protocol; this is
- /// usually okay for leaf classes.
- const RESET: Option<fn(&mut Self)> = None;
+ const REALIZE: Option<fn(&Self)> = None;
/// An array providing the properties that the user can set on the
/// device. Not a `const` because referencing statics in constants
@@ -55,35 +118,40 @@ pub trait DeviceImpl {
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) {
- assert!(!dev.is_null());
- let state = dev.cast::<T>();
- T::REALIZE.unwrap()(unsafe { &mut *state });
+ let state = NonNull::new(dev).unwrap().cast::<T>();
+ T::REALIZE.unwrap()(unsafe { state.as_ref() });
}
-/// # Safety
-///
-/// We expect the FFI user of this function to pass a valid pointer that
-/// can be downcasted to type `T`. We also expect the device is
-/// readable/writeable from one thread at any time.
-unsafe extern "C" fn rust_reset_fn<T: DeviceImpl>(dev: *mut DeviceState) {
- assert!(!dev.is_null());
- let state = dev.cast::<T>();
- T::RESET.unwrap()(unsafe { &mut *state });
+unsafe impl InterfaceType for ResettableClass {
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) };
+}
+
+impl<T> ClassInitImpl<ResettableClass> for T
+where
+ T: ResettablePhasesImpl,
+{
+ fn class_init(rc: &mut ResettableClass) {
+ if <T as ResettablePhasesImpl>::ENTER.is_some() {
+ rc.phases.enter = Some(rust_resettable_enter_fn::<T>);
+ }
+ if <T as ResettablePhasesImpl>::HOLD.is_some() {
+ rc.phases.hold = Some(rust_resettable_hold_fn::<T>);
+ }
+ if <T as ResettablePhasesImpl>::EXIT.is_some() {
+ rc.phases.exit = Some(rust_resettable_exit_fn::<T>);
+ }
+ }
}
impl<T> ClassInitImpl<DeviceClass> for T
where
- T: ClassInitImpl<ObjectClass> + DeviceImpl,
+ T: ClassInitImpl<ObjectClass> + ClassInitImpl<ResettableClass> + DeviceImpl,
{
fn class_init(dc: &mut DeviceClass) {
if <T as DeviceImpl>::REALIZE.is_some() {
dc.realize = Some(rust_realize_fn::<T>);
}
- if <T as DeviceImpl>::RESET.is_some() {
- unsafe {
- bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::<T>));
- }
- }
if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
dc.vmsd = vmsd;
}
@@ -94,12 +162,25 @@ where
}
}
+ ResettableClass::interface_init::<T, DeviceState>(dc);
<T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class);
}
}
#[macro_export]
macro_rules! define_property {
+ ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => {
+ $crate::bindings::Property {
+ // use associated function syntax for type checking
+ name: ::std::ffi::CStr::as_ptr($name),
+ info: $prop,
+ offset: $crate::offset_of!($state, $field) as isize,
+ bitnr: $bitnr,
+ set_default: true,
+ defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
+ ..$crate::zeroable::Zeroable::ZERO
+ }
+ };
($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => {
$crate::bindings::Property {
// use associated function syntax for type checking
@@ -145,3 +226,144 @@ unsafe impl ObjectType for DeviceState {
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
}
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>,
+{
+ /// Add an input clock named `name`. Invoke the callback with
+ /// `self` as the first parameter for the events that are requested.
+ ///
+ /// The resulting clock is added as a child of `self`, but it also
+ /// stays alive until after `Drop::drop` is called because C code
+ /// keeps an extra reference to it until `device_finalize()` calls
+ /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in
+ /// which Rust code has a reference to a child object) it would be
+ /// possible for this function to return a `&Clock` too.
+ #[inline]
+ fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>(
+ &self,
+ name: &str,
+ _cb: &F,
+ events: ClockEvent,
+ ) -> Owned<Clock> {
+ fn do_init_clock_in(
+ dev: *mut DeviceState,
+ name: &str,
+ cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
+ events: ClockEvent,
+ ) -> Owned<Clock> {
+ assert!(bql_locked());
+
+ // SAFETY: the clock is heap allocated, but qdev_init_clock_in()
+ // does not gift the reference to its caller; so use Owned::from to
+ // add one. The callback is disabled automatically when the clock
+ // is unparented, which happens before the device is finalized.
+ unsafe {
+ let cstr = CString::new(name).unwrap();
+ let clk = bindings::qdev_init_clock_in(
+ dev,
+ cstr.as_ptr(),
+ cb,
+ dev.cast::<c_void>(),
+ events.0,
+ );
+
+ Owned::from(&*clk)
+ }
+ }
+
+ let cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)> = if F::is_some() {
+ unsafe extern "C" fn rust_clock_cb<T, F: for<'a> FnCall<(&'a T, ClockEvent)>>(
+ opaque: *mut c_void,
+ event: ClockEvent,
+ ) {
+ // SAFETY: the opaque is "this", which is indeed a pointer to T
+ F::call((unsafe { &*(opaque.cast::<T>()) }, event))
+ }
+ Some(rust_clock_cb::<Self::Target, F>)
+ } else {
+ None
+ };
+
+ do_init_clock_in(self.as_mut_ptr(), name, cb, events)
+ }
+
+ /// Add an output clock named `name`.
+ ///
+ /// The resulting clock is added as a child of `self`, but it also
+ /// stays alive until after `Drop::drop` is called because C code
+ /// keeps an extra reference to it until `device_finalize()` calls
+ /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in
+ /// which Rust code has a reference to a child object) it would be
+ /// possible for this function to return a `&Clock` too.
+ #[inline]
+ fn init_clock_out(&self, name: &str) -> Owned<Clock> {
+ unsafe {
+ let cstr = CString::new(name).unwrap();
+ let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr());
+
+ Owned::from(&*clk)
+ }
+ }
+
+ fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
+ assert!(bql_locked());
+ let c_propname = CString::new(propname).unwrap();
+ unsafe {
+ bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr());
+ }
+ }
+
+ fn init_gpio_in<F: for<'a> FnCall<(&'a Self::Target, u32, u32)>>(
+ &self,
+ num_lines: u32,
+ _cb: F,
+ ) {
+ let _: () = F::ASSERT_IS_SOME;
+
+ unsafe extern "C" fn rust_irq_handler<T, F: for<'a> FnCall<(&'a T, u32, u32)>>(
+ opaque: *mut c_void,
+ line: c_int,
+ level: c_int,
+ ) {
+ // SAFETY: the opaque was passed as a reference to `T`
+ F::call((unsafe { &*(opaque.cast::<T>()) }, line as u32, level as u32))
+ }
+
+ let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) =
+ rust_irq_handler::<Self::Target, F>;
+
+ unsafe {
+ qdev_init_gpio_in(
+ self.as_mut_ptr::<DeviceState>(),
+ Some(gpio_in_cb),
+ num_lines as c_int,
+ );
+ }
+ }
+
+ fn init_gpio_out(&self, pins: &[InterruptSource]) {
+ unsafe {
+ qdev_init_gpio_out(
+ self.as_mut_ptr::<DeviceState>(),
+ InterruptSource::slice_as_ptr(pins),
+ pins.len() as c_int,
+ );
+ }
+ }
+}
+
+impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {}
+
+unsafe impl ObjectType for Clock {
+ type Class = ObjectClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
+}
+qom_isa!(Clock: Object);
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 7d5fbef..3d5ab2d 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -55,13 +55,22 @@
use std::{
ffi::CStr,
+ fmt,
+ mem::ManuallyDrop,
ops::{Deref, DerefMut},
os::raw::c_void,
+ ptr::NonNull,
};
pub use bindings::{Object, ObjectClass};
-use crate::bindings::{self, object_dynamic_cast, object_get_class, object_get_typename, TypeInfo};
+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,
+};
/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct
/// or indirect parent of `Self`).
@@ -105,32 +114,91 @@ macro_rules! qom_isa {
};
}
+/// This is the same as [`ManuallyDrop<T>`](std::mem::ManuallyDrop), though
+/// it hides the standard methods of `ManuallyDrop`.
+///
+/// The first field of an `ObjectType` must be of type `ParentField<T>`.
+/// (Technically, this is only necessary if there is at least one Rust
+/// superclass in the hierarchy). This is to ensure that the parent field is
+/// dropped after the subclass; this drop order is enforced by the C
+/// `object_deinit` function.
+///
+/// # Examples
+///
+/// ```ignore
+/// #[repr(C)]
+/// #[derive(qemu_api_macros::Object)]
+/// pub struct MyDevice {
+/// parent: ParentField<DeviceState>,
+/// ...
+/// }
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct ParentField<T: ObjectType>(std::mem::ManuallyDrop<T>);
+
+impl<T: ObjectType> Deref for ParentField<T> {
+ type Target = T;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<T: ObjectType> DerefMut for ParentField<T> {
+ #[inline(always)]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ self.0.fmt(f)
+ }
+}
+
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
+ let mut state = NonNull::new(obj).unwrap().cast::<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()(&mut *obj.cast::<T>()) }
+ unsafe {
+ T::INSTANCE_INIT.unwrap()(state.as_mut());
+ }
}
unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) {
+ let state = NonNull::new(obj).unwrap().cast::<T>();
// SAFETY: obj is an instance of T, since rust_instance_post_init<T>
// is called from QOM core as the instance_post_init function
// for class T
- //
- // FIXME: it's not really guaranteed that there are no backpointers to
- // obj; it's quite possible that they have been created by instance_init().
- // The receiver should be &self, not &mut self.
- T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::<T>() })
+ T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() });
}
unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>(
klass: *mut ObjectClass,
_data: *mut c_void,
) {
+ let mut klass = NonNull::new(klass)
+ .unwrap()
+ .cast::<<T as ObjectType>::Class>();
// SAFETY: klass is a T::Class, since rust_class_init<T>
// is called from QOM core as the class_init function
// for class T
- T::class_init(unsafe { &mut *klass.cast::<T::Class>() })
+ T::class_init(unsafe { klass.as_mut() })
+}
+
+unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
+ // SAFETY: obj is an instance of T, since drop_object<T> is called
+ // from the QOM core function object_deinit() as the instance_finalize
+ // function for class T. Note that while object_deinit() will drop the
+ // superclass field separately after this function returns, `T` must
+ // implement the unsafe trait ObjectType; the safety rules for the
+ // trait mandate that the parent field is manually dropped.
+ unsafe { std::ptr::drop_in_place(obj.cast::<T>()) }
}
/// Trait exposed by all structs corresponding to QOM objects.
@@ -151,11 +219,16 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>(
///
/// - the struct must be `#[repr(C)]`;
///
-/// - the first field of the struct must be of the instance struct corresponding
-/// to the superclass, which is `ObjectImpl::ParentType`
+/// - the first field of the struct must be of type
+/// [`ParentField<T>`](ParentField), where `T` is the parent type
+/// [`ObjectImpl::ParentType`]
///
-/// - likewise, the first field of the `Class` must be of the class struct
-/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`.
+/// - the first field of the `Class` must be of the class struct corresponding
+/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField`
+/// is not needed here.
+///
+/// In both cases, having a separate class type is not necessary if the subclass
+/// does not add any field.
pub unsafe trait ObjectType: Sized {
/// The QOM class object corresponding to this struct. This is used
/// to automatically generate a `class_init` method.
@@ -190,6 +263,47 @@ pub unsafe trait ObjectType: Sized {
}
}
+/// Trait exposed by all structs corresponding to QOM interfaces.
+/// Unlike `ObjectType`, it is implemented on the class type (which provides
+/// the vtable for the interfaces).
+///
+/// # Safety
+///
+/// `TYPE` must match the contents of the `TypeInfo` as found in the C code;
+/// right now, interfaces can only be declared in C.
+pub unsafe trait InterfaceType: Sized {
+ /// The name of the type, which can be passed to
+ /// `object_class_dynamic_cast()` to obtain the pointer to the vtable
+ /// for this interface.
+ const TYPE_NAME: &'static CStr;
+
+ /// Initialize the vtable for the interface; the generic argument `T` is the
+ /// type being initialized, while the generic argument `U` is the type that
+ /// lists the interface in its `TypeInfo`.
+ ///
+ /// # Panics
+ ///
+ /// Panic if the incoming argument if `T` does not implement the interface.
+ fn interface_init<
+ T: ObjectType + ClassInitImpl<Self> + ClassInitImpl<U::Class>,
+ U: ObjectType,
+ >(
+ klass: &mut U::Class,
+ ) {
+ unsafe {
+ // SAFETY: upcasting to ObjectClass is always valid, and the
+ // return type is either NULL or the argument itself
+ let result: *mut Self = object_class_dynamic_cast(
+ (klass as *mut U::Class).cast(),
+ Self::TYPE_NAME.as_ptr(),
+ )
+ .cast();
+
+ <T as ClassInitImpl<Self>>::class_init(result.as_mut().unwrap())
+ }
+ }
+}
+
/// This trait provides safe casting operations for QOM objects to raw pointers,
/// to be used for example for FFI. The trait can be applied to any kind of
/// reference or smart pointers, and enforces correctness through the [`IsA`]
@@ -214,10 +328,10 @@ where
///
/// # Safety
///
- /// This method is unsafe because it overrides const-ness of `&self`.
- /// Bindings to C APIs will use it a lot, but otherwise it should not
- /// be necessary.
- unsafe fn as_mut_ptr<U: ObjectType>(&self) -> *mut U
+ /// This method is safe because only the actual dereference of the pointer
+ /// has to be unsafe. Bindings to C APIs will use it a lot, but care has
+ /// to be taken because it overrides the const-ness of `&self`.
+ fn as_mut_ptr<U: ObjectType>(&self) -> *mut U
where
Self::Target: IsA<U>,
{
@@ -384,13 +498,12 @@ impl<T: ObjectType> ObjectCastMut for &mut T {}
/// Trait a type must implement to be registered with QEMU.
pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
- /// The parent of the type. This should match the first field of
- /// the struct that implements `ObjectImpl`:
+ /// The parent of the type. This should match the first field of the
+ /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper.
type ParentType: ObjectType;
/// Whether the object can be instantiated
const ABSTRACT: bool = false;
- const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
/// Function that is called to initialize an object. The parent class will
/// have already been initialized so the type is only responsible for
@@ -402,7 +515,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// Function that is called to finish initialization of an object, once
/// `INSTANCE_INIT` functions have been called.
- const INSTANCE_POST_INIT: Option<fn(&mut Self)> = None;
+ const INSTANCE_POST_INIT: Option<fn(&Self)> = None;
/// Called on descendent classes after all parent class initialization
/// has occurred, but before the class itself is initialized. This
@@ -426,7 +539,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
None => None,
Some(_) => Some(rust_instance_post_init::<Self>),
},
- instance_finalize: Self::INSTANCE_FINALIZE,
+ instance_finalize: Some(drop_object::<Self>),
abstract_: Self::ABSTRACT,
class_size: core::mem::size_of::<Self::Class>(),
class_init: Some(rust_class_init::<Self>),
@@ -524,11 +637,8 @@ pub trait ClassInitImpl<T> {
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) {
- unsafe {
- assert!(!dev.is_null());
- let state = core::ptr::NonNull::new_unchecked(dev.cast::<T>());
- T::UNPARENT.unwrap()(state.as_ref());
- }
+ let state = NonNull::new(dev).unwrap().cast::<T>();
+ T::UNPARENT.unwrap()(unsafe { state.as_ref() });
}
impl<T> ClassInitImpl<ObjectClass> for T
@@ -548,6 +658,166 @@ unsafe impl ObjectType for Object {
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) };
}
+/// A reference-counted pointer to a QOM object.
+///
+/// `Owned<T>` wraps `T` with automatic reference counting. It increases the
+/// reference count when created via [`Owned::from`] or cloned, and decreases
+/// it when dropped. This ensures that the reference count remains elevated
+/// as long as any `Owned<T>` references to it exist.
+///
+/// `Owned<T>` can be used for two reasons:
+/// * because the lifetime of the QOM object is unknown and someone else could
+/// take a reference (similar to `Arc<T>`, for example): in this case, the
+/// object can escape and outlive the Rust struct that contains the `Owned<T>`
+/// field;
+///
+/// * to ensure that the object stays alive until after `Drop::drop` is called
+/// on the Rust struct: in this case, the object will always die together with
+/// the Rust struct that contains the `Owned<T>` field.
+///
+/// Child properties are an example of the second case: in C, an object that
+/// is created with `object_initialize_child` will die *before*
+/// `instance_finalize` is called, whereas Rust expects the struct to have valid
+/// contents when `Drop::drop` is called. Therefore Rust structs that have
+/// child properties need to keep a reference to the child object. Right now
+/// this can be done with `Owned<T>`; in the future one might have a separate
+/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like
+/// `Owned`, but does not allow cloning.
+///
+/// Note that dropping an `Owned<T>` requires the big QEMU lock to be taken.
+#[repr(transparent)]
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Owned<T: ObjectType>(NonNull<T>);
+
+// The following rationale for safety is taken from Linux's kernel::sync::Arc.
+
+// SAFETY: It is safe to send `Owned<T>` to another thread when the underlying
+// `T` is `Sync` because it effectively means sharing `&T` (which is safe
+// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any
+// thread that has an `Owned<T>` may ultimately access `T` using a
+// mutable reference when the reference count reaches zero and `T` is dropped.
+unsafe impl<T: ObjectType + Send + Sync> Send for Owned<T> {}
+
+// SAFETY: It is safe to send `&Owned<T>` to another thread when the underlying
+// `T` is `Sync` because it effectively means sharing `&T` (which is safe
+// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any
+// thread that has a `&Owned<T>` may clone it and get an `Owned<T>` on that
+// thread, so the thread may ultimately access `T` using a mutable reference
+// when the reference count reaches zero and `T` is dropped.
+unsafe impl<T: ObjectType + Sync + Send> Sync for Owned<T> {}
+
+impl<T: ObjectType> Owned<T> {
+ /// Convert a raw C pointer into an owned reference to the QOM
+ /// object it points to. The object's reference count will be
+ /// decreased when the `Owned` is dropped.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `ptr` is NULL.
+ ///
+ /// # Safety
+ ///
+ /// The caller must indeed own a reference to the QOM object.
+ /// The object must not be embedded in another unless the outer
+ /// object is guaranteed to have a longer lifetime.
+ ///
+ /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed
+ /// back to `from_raw()` (assuming the original `Owned` was valid!),
+ /// since the owned reference remains there between the calls to
+ /// `into_raw()` and `from_raw()`.
+ pub unsafe fn from_raw(ptr: *const T) -> Self {
+ // SAFETY NOTE: while NonNull requires a mutable pointer, only
+ // Deref is implemented so the pointer passed to from_raw
+ // remains const
+ Owned(NonNull::new(ptr as *mut T).unwrap())
+ }
+
+ /// Obtain a raw C pointer from a reference. `src` is consumed
+ /// and the reference is leaked.
+ #[allow(clippy::missing_const_for_fn)]
+ pub fn into_raw(src: Owned<T>) -> *mut T {
+ let src = ManuallyDrop::new(src);
+ src.0.as_ptr()
+ }
+
+ /// Increase the reference count of a QOM object and return
+ /// a new owned reference to it.
+ ///
+ /// # Safety
+ ///
+ /// The object must not be embedded in another, unless the outer
+ /// object is guaranteed to have a longer lifetime.
+ pub unsafe fn from(obj: &T) -> Self {
+ unsafe {
+ object_ref(obj.as_object_mut_ptr().cast::<c_void>());
+
+ // SAFETY NOTE: while NonNull requires a mutable pointer, only
+ // Deref is implemented so the reference passed to from_raw
+ // remains shared
+ Owned(NonNull::new_unchecked(obj.as_mut_ptr()))
+ }
+ }
+}
+
+impl<T: ObjectType> Clone for Owned<T> {
+ fn clone(&self) -> Self {
+ // SAFETY: creation method is unsafe; whoever calls it has
+ // responsibility that the pointer is valid, and remains valid
+ // throughout the lifetime of the `Owned<T>` and its clones.
+ unsafe { Owned::from(self.deref()) }
+ }
+}
+
+impl<T: ObjectType> Deref for Owned<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: creation method is unsafe; whoever calls it has
+ // responsibility that the pointer is valid, and remains valid
+ // throughout the lifetime of the `Owned<T>` and its clones.
+ // With that guarantee, reference counting ensures that
+ // the object remains alive.
+ unsafe { &*self.0.as_ptr() }
+ }
+}
+impl<T: ObjectType> ObjectDeref for Owned<T> {}
+
+impl<T: ObjectType> Drop for Owned<T> {
+ fn drop(&mut self) {
+ assert!(bql_locked());
+ // SAFETY: creation method is unsafe, and whoever calls it has
+ // responsibility that the pointer is valid, and remains valid
+ // throughout the lifetime of the `Owned<T>` and its clones.
+ unsafe {
+ object_unref(self.as_object_mut_ptr().cast::<c_void>());
+ }
+ }
+}
+
+impl<T: IsA<Object>> fmt::Debug for Owned<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.deref().debug_fmt(f)
+ }
+}
+
+/// Trait for class methods exposed by the Object class. The methods can be
+/// called on all objects that have the trait `IsA<Object>`.
+///
+/// The trait should only be used through the blanket implementation,
+/// which guarantees safety via `IsA`
+pub trait ObjectClassMethods: IsA<Object> {
+ /// Return a new reference counted instance of this class
+ fn new() -> Owned<Self> {
+ assert!(bql_locked());
+ // SAFETY: the object created by object_new is allocated on
+ // the heap and has a reference count of 1
+ unsafe {
+ let obj = &*object_new(Self::TYPE_NAME.as_ptr());
+ Owned::from_raw(obj.unsafe_cast::<Self>())
+ }
+ }
+}
+
/// Trait for methods exposed by the Object class. The methods can be
/// called on all objects that have the trait `IsA<Object>`.
///
@@ -579,6 +849,14 @@ where
klass
}
+
+ /// Convenience function for implementing the Debug trait
+ fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_tuple(&self.typename())
+ .field(&(self as *const Self))
+ .finish()
+ }
}
+impl<T> ObjectClassMethods for T where T: IsA<Object> {}
impl<R: ObjectDeref> ObjectMethods for R where R::Target: IsA<Object> {}
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index 8193734..fa36e12 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -2,17 +2,20 @@
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{ffi::CStr, ptr::addr_of};
+//! Bindings to access `sysbus` functionality from Rust.
+
+use std::{ffi::CStr, ptr::addr_of_mut};
pub use bindings::{SysBusDevice, SysBusDeviceClass};
use crate::{
bindings,
cell::bql_locked,
- irq::InterruptSource,
+ irq::{IRQState, InterruptSource},
+ memory::MemoryRegion,
prelude::*,
qdev::{DeviceClass, DeviceState},
- qom::ClassInitImpl,
+ qom::{ClassInitImpl, Owned},
};
unsafe impl ObjectType for SysBusDevice {
@@ -32,20 +35,61 @@ where
}
}
-impl SysBusDevice {
- /// Return `self` cast to a mutable pointer, for use in calls to C code.
- const fn as_mut_ptr(&self) -> *mut SysBusDevice {
- addr_of!(*self) as *mut _
+/// Trait for methods of [`SysBusDevice`] and its subclasses.
+pub trait SysBusDeviceMethods: ObjectDeref
+where
+ Self::Target: IsA<SysBusDevice>,
+{
+ /// Expose a memory region to the board so that it can give it an address
+ /// in guest memory. Note that the ordering of calls to `init_mmio` is
+ /// important, since whoever creates the sysbus device will refer to the
+ /// region with a number that corresponds to the order of calls to
+ /// `init_mmio`.
+ fn init_mmio(&self, iomem: &MemoryRegion) {
+ assert!(bql_locked());
+ unsafe {
+ bindings::sysbus_init_mmio(self.as_mut_ptr(), iomem.as_mut_ptr());
+ }
}
/// Expose an interrupt source outside the device as a qdev GPIO output.
/// Note that the ordering of calls to `init_irq` is important, since
/// whoever creates the sysbus device will refer to the interrupts with
/// a number that corresponds to the order of calls to `init_irq`.
- pub fn init_irq(&self, irq: &InterruptSource) {
+ fn init_irq(&self, irq: &InterruptSource) {
assert!(bql_locked());
unsafe {
bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
}
}
+
+ // TODO: do we want a type like GuestAddress here?
+ fn mmio_map(&self, id: u32, addr: u64) {
+ assert!(bql_locked());
+ let id: i32 = id.try_into().unwrap();
+ unsafe {
+ bindings::sysbus_mmio_map(self.as_mut_ptr(), id, addr);
+ }
+ }
+
+ // Owned<> is used here because sysbus_connect_irq (via
+ // object_property_set_link) adds a reference to the IRQState,
+ // which can prolong its life
+ fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) {
+ assert!(bql_locked());
+ let id: i32 = id.try_into().unwrap();
+ unsafe {
+ bindings::sysbus_connect_irq(self.as_mut_ptr(), id, irq.as_mut_ptr());
+ }
+ }
+
+ fn sysbus_realize(&self) {
+ // TODO: return an Error
+ assert!(bql_locked());
+ unsafe {
+ bindings::sysbus_realize(self.as_mut_ptr(), addr_of_mut!(bindings::error_fatal));
+ }
+ }
}
+
+impl<R: ObjectDeref> SysBusDeviceMethods for R where R::Target: IsA<SysBusDevice> {}
diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs
new file mode 100644
index 0000000..a593538
--- /dev/null
+++ b/rust/qemu-api/src/timer.rs
@@ -0,0 +1,98 @@
+// Copyright (C) 2024 Intel Corporation.
+// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::os::raw::{c_int, c_void};
+
+use crate::{
+ bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType},
+ callbacks::FnCall,
+};
+
+pub type Timer = bindings::QEMUTimer;
+pub type TimerListGroup = bindings::QEMUTimerListGroup;
+
+impl Timer {
+ pub const MS: u32 = bindings::SCALE_MS;
+ pub const US: u32 = bindings::SCALE_US;
+ pub const NS: u32 = bindings::SCALE_NS;
+
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ const fn as_mut_ptr(&self) -> *mut Self {
+ self as *const Timer as *mut _
+ }
+
+ pub fn init_full<'timer, 'opaque: 'timer, T, F>(
+ &'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,)>,
+ {
+ let _: () = F::ASSERT_IS_SOME;
+
+ /// timer expiration callback
+ unsafe extern "C" fn rust_timer_handler<T, F: for<'a> FnCall<(&'a T,)>>(
+ opaque: *mut c_void,
+ ) {
+ // SAFETY: the opaque was passed as a reference to `T`.
+ F::call((unsafe { &*(opaque.cast::<T>()) },))
+ }
+
+ let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::<T, F>;
+
+ // SAFETY: the opaque outlives the timer
+ unsafe {
+ timer_init_full(
+ self,
+ if let Some(g) = timer_list_group {
+ g as *const TimerListGroup as *mut _
+ } else {
+ ::core::ptr::null_mut()
+ },
+ clk_type.id,
+ scale as c_int,
+ attributes as c_int,
+ Some(timer_cb),
+ (opaque as *const T).cast::<c_void>() as *mut c_void,
+ )
+ }
+ }
+
+ pub fn modify(&self, expire_time: u64) {
+ unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) }
+ }
+
+ pub fn delete(&self) {
+ unsafe { timer_del(self.as_mut_ptr()) }
+ }
+}
+
+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,
+};
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 63c897a..24a4dc8 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -4,277 +4,480 @@
//! Helper macros to declare migration state for device models.
//!
-//! Some macros are direct equivalents to the C macros declared in
-//! `include/migration/vmstate.h` while
-//! [`vmstate_subsections`](crate::vmstate_subsections) and
-//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when
-//! declaring a device model state struct.
+//! This module includes four families of macros:
+//!
+//! * [`vmstate_unused!`](crate::vmstate_unused) and
+//! [`vmstate_of!`](crate::vmstate_of), which are used to express the
+//! migration format for a struct. This is based on the [`VMState`] trait,
+//! which is defined by all migrateable types.
+//!
+//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and
+//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with
+//! the definition of the [`VMState`] trait (respectively for transparent
+//! structs and for `bilge`-defined types)
+//!
+//! * helper macros to declare a device model state struct, in particular
+//! [`vmstate_subsections`](crate::vmstate_subsections) and
+//! [`vmstate_fields`](crate::vmstate_fields).
+//!
+//! * direct equivalents to the C macros declared in
+//! `include/migration/vmstate.h`. These are not type-safe and only provide
+//! functionality that is missing from `vmstate_of!`.
-pub use crate::bindings::VMStateDescription;
+use core::{marker::PhantomData, mem, ptr::NonNull};
-#[doc(alias = "VMSTATE_UNUSED_BUFFER")]
+pub use crate::bindings::{VMStateDescription, VMStateField};
+use crate::{
+ bindings::{self, VMStateFlags},
+ prelude::*,
+ qom::Owned,
+ zeroable::Zeroable,
+};
+
+/// 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`]`<T>` argument; `T` is the type of
+/// field `$field` in the `$typ` type.
+///
+/// # Examples
+///
+/// ```
+/// # use qemu_api::call_func_with_field;
+/// # use core::marker::PhantomData;
+/// const fn size_of_field<T>(_: PhantomData<T>) -> usize {
+/// std::mem::size_of::<T>()
+/// }
+///
+/// struct Foo {
+/// x: u16,
+/// };
+/// // calls size_of_field::<u16>()
+/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2);
+/// ```
#[macro_export]
-macro_rules! vmstate_unused_buffer {
- ($field_exists_fn:expr, $version_id:expr, $size:expr) => {{
- $crate::bindings::VMStateField {
- name: c_str!("unused").as_ptr(),
- err_hint: ::core::ptr::null(),
- offset: 0,
- size: $size,
- start: 0,
- num: 0,
- num_offset: 0,
- size_offset: 0,
- info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
- flags: VMStateFlags::VMS_BUFFER,
- vmsd: ::core::ptr::null(),
- version_id: $version_id,
- struct_version_id: 0,
- field_exists: $field_exists_fn,
- }
- }};
+macro_rules! call_func_with_field {
+ // Based on the answer by user steffahn (Frank Steffahn) at
+ // https://users.rust-lang.org/t/inferring-type-of-field/122857
+ // and used under MIT license
+ ($func:expr, $typ:ty, $($field:tt).+) => {
+ $func(loop {
+ #![allow(unreachable_code)]
+ const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker::PhantomData }
+ // Unreachable code is exempt from checks on uninitialized values.
+ // Use that trick to infer the type of this PhantomData.
+ break ::core::marker::PhantomData;
+ break phantom__(&{ let value__: $typ; value__.$($field).+ });
+ })
+ };
}
-#[doc(alias = "VMSTATE_UNUSED_V")]
-#[macro_export]
-macro_rules! vmstate_unused_v {
- ($version_id:expr, $size:expr) => {{
- $crate::vmstate_unused_buffer!(None, $version_id, $size)
- }};
+/// Workaround for lack of `const_refs_static`: references to global variables
+/// can be included in a `static`, but not in a `const`; unfortunately, this
+/// is exactly what would go in the `VMStateField`'s `info` member.
+///
+/// This enum contains the contents of the `VMStateField`'s `info` member,
+/// but as an `enum` instead of a pointer.
+#[allow(non_camel_case_types)]
+pub enum VMStateFieldType {
+ null,
+ vmstate_info_bool,
+ vmstate_info_int8,
+ vmstate_info_int16,
+ vmstate_info_int32,
+ vmstate_info_int64,
+ vmstate_info_uint8,
+ vmstate_info_uint16,
+ vmstate_info_uint32,
+ vmstate_info_uint64,
+ vmstate_info_timer,
}
-#[doc(alias = "VMSTATE_UNUSED")]
+/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType`
+/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`.
#[macro_export]
-macro_rules! vmstate_unused {
- ($size:expr) => {{
- $crate::vmstate_unused_v!(0, $size)
- }};
+macro_rules! info_enum_to_ref {
+ ($e:expr) => {
+ unsafe {
+ match $e {
+ $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(),
+ $crate::vmstate::VMStateFieldType::vmstate_info_bool => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int8 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int16 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int32 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_int64 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64)
+ }
+ $crate::vmstate::VMStateFieldType::vmstate_info_timer => {
+ ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer)
+ }
+ }
+ }
+ };
+}
+
+/// A trait for types that can be included in a device's migration stream. It
+/// provides the base contents of a `VMStateField` (minus the name and offset).
+///
+/// # Safety
+///
+/// The contents of this trait go straight into structs that are parsed by C
+/// code and used to introspect into other structs. Generally, you don't need
+/// to implement it except via macros that do it for you, such as
+/// `impl_vmstate_bitsized!`.
+pub unsafe trait VMState {
+ /// The `info` member of a `VMStateField` is a pointer and as such cannot
+ /// yet be included in the [`BASE`](VMState::BASE) associated constant;
+ /// this is only allowed by Rust 1.83.0 and newer. For now, include the
+ /// member as an enum which is stored in a separate constant.
+ const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null;
+
+ /// The base contents of a `VMStateField` (minus the name and offset) for
+ /// the type that is implementing the trait.
+ const BASE: VMStateField;
+
+ /// A flag that is added to another field's `VMStateField` to specify the
+ /// length's type in a variable-sized array. If this is not a supported
+ /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it
+ /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a
+ /// compile-time error.
+ const VARRAY_FLAG: VMStateFlags = {
+ panic!("invalid type for variable-sized array");
+ };
+}
+
+/// Internal utility function to retrieve a type's `VMStateFieldType`;
+/// used by [`vmstate_of!`](crate::vmstate_of).
+pub const fn vmstate_scalar_type<T: VMState>(_: PhantomData<T>) -> VMStateFieldType {
+ T::SCALAR_TYPE
+}
+
+/// Internal utility function to retrieve a type's `VMStateField`;
+/// used by [`vmstate_of!`](crate::vmstate_of).
+pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField {
+ T::BASE
+}
+
+/// Internal utility function to retrieve a type's `VMStateFlags` when it
+/// is used as the element count of a `VMSTATE_VARRAY`; used by
+/// [`vmstate_of!`](crate::vmstate_of).
+pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags {
+ T::VARRAY_FLAG
}
-#[doc(alias = "VMSTATE_SINGLE_TEST")]
+/// Return the `VMStateField` for a field of a struct. The field must be
+/// visible in the current scope.
+///
+/// Only a limited set of types is supported out of the box:
+/// * scalar types (integer and `bool`)
+/// * the C struct `QEMUTimer`
+/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`,
+/// [`BqlCell`], [`BqlRefCell`]
+/// * a raw pointer to any of the above
+/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above
+/// * an array of any of the above
+///
+/// In order to support other types, the trait `VMState` must be implemented
+/// for them. The macros
+/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized)
+/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this.
#[macro_export]
-macro_rules! vmstate_single_test {
- ($field_name:ident, $struct_name:ty, $field_exists_fn:expr, $version_id:expr, $info:expr, $size:expr) => {{
+macro_rules! vmstate_of {
+ ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => {
$crate::bindings::VMStateField {
- name: ::core::concat!(::core::stringify!($field_name), 0)
+ name: ::core::concat!(::core::stringify!($field_name), "\0")
.as_bytes()
.as_ptr() as *const ::std::os::raw::c_char,
- err_hint: ::core::ptr::null(),
offset: $crate::offset_of!($struct_name, $field_name),
- size: $size,
- start: 0,
- num: 0,
- num_offset: 0,
- size_offset: 0,
- info: unsafe { $info },
- flags: VMStateFlags::VMS_SINGLE,
- vmsd: ::core::ptr::null(),
- version_id: $version_id,
- struct_version_id: 0,
- field_exists: $field_exists_fn,
+ $(.num_offset: $crate::offset_of!($struct_name, $num),)?
+ // The calls to `call_func_with_field!` are the magic that
+ // computes most of the VMStateField from the type of the field.
+ info: $crate::info_enum_to_ref!($crate::call_func_with_field!(
+ $crate::vmstate::vmstate_scalar_type,
+ $struct_name,
+ $field_name
+ )),
+ ..$crate::call_func_with_field!(
+ $crate::vmstate::vmstate_base,
+ $struct_name,
+ $field_name
+ )$(.with_varray_flag($crate::call_func_with_field!(
+ $crate::vmstate::vmstate_varray_flag,
+ $struct_name,
+ $num))
+ $(.with_varray_multiply($factor))?)?
}
- }};
+ };
}
-#[doc(alias = "VMSTATE_SINGLE")]
-#[macro_export]
-macro_rules! vmstate_single {
- ($field_name:ident, $struct_name:ty, $version_id:expr, $info:expr, $size:expr) => {{
- $crate::vmstate_single_test!($field_name, $struct_name, None, $version_id, $info, $size)
- }};
+impl VMStateFlags {
+ const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags(
+ VMStateFlags::VMS_VARRAY_INT32.0
+ | VMStateFlags::VMS_VARRAY_UINT8.0
+ | VMStateFlags::VMS_VARRAY_UINT16.0
+ | VMStateFlags::VMS_VARRAY_UINT32.0,
+ );
}
-#[doc(alias = "VMSTATE_UINT32_V")]
-#[macro_export]
-macro_rules! vmstate_uint32_v {
- ($field_name:ident, $struct_name:ty, $version_id:expr) => {{
- $crate::vmstate_single!(
- $field_name,
- $struct_name,
- $version_id,
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32),
- ::core::mem::size_of::<u32>()
- )
- }};
+// Add a couple builder-style methods to VMStateField, allowing
+// easy derivation of VMStateField constants from other types.
+impl VMStateField {
+ #[must_use]
+ pub const fn with_version_id(mut self, version_id: i32) -> Self {
+ assert!(version_id >= 0);
+ self.version_id = version_id;
+ self
+ }
+
+ #[must_use]
+ pub const fn with_array_flag(mut self, num: usize) -> Self {
+ assert!(num <= 0x7FFF_FFFFusize);
+ assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0);
+ assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0);
+ if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 {
+ self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0);
+ self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0);
+ }
+ self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0);
+ self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0);
+ self.num = num as i32;
+ self
+ }
+
+ #[must_use]
+ pub const fn with_pointer_flag(mut self) -> Self {
+ assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0);
+ self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0);
+ self
+ }
+
+ #[must_use]
+ pub const fn with_varray_flag<T: VMState>(mut self, flag: VMStateFlags) -> VMStateField {
+ assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0);
+ self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0);
+ self.flags = VMStateFlags(self.flags.0 | flag.0);
+ self
+ }
+
+ #[must_use]
+ pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField {
+ assert!(num <= 0x7FFF_FFFFu32);
+ self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0);
+ self.num = num as i32;
+ self
+ }
}
-#[doc(alias = "VMSTATE_UINT32")]
+/// This macro can be used (by just passing it a type) to forward the `VMState`
+/// trait to the first field of a tuple. This is a workaround for lack of
+/// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0.
+///
+/// # Examples
+///
+/// ```
+/// # use qemu_api::impl_vmstate_forward;
+/// pub struct Fifo([u8; 16]);
+/// impl_vmstate_forward!(Fifo);
+/// ```
#[macro_export]
-macro_rules! vmstate_uint32 {
- ($field_name:ident, $struct_name:ty) => {{
- $crate::vmstate_uint32_v!($field_name, $struct_name, 0)
- }};
+macro_rules! impl_vmstate_forward {
+ // This is similar to impl_vmstate_transparent below, but it
+ // uses the same trick as vmstate_of! to obtain the type of
+ // the first field of the tuple
+ ($tuple:ty) => {
+ unsafe impl $crate::vmstate::VMState for $tuple {
+ const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
+ $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0);
+ const BASE: $crate::bindings::VMStateField =
+ $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0);
+ }
+ };
}
-#[doc(alias = "VMSTATE_ARRAY")]
-#[macro_export]
-macro_rules! vmstate_array {
- ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr, $info:expr, $size:expr) => {{
- $crate::bindings::VMStateField {
- name: ::core::concat!(::core::stringify!($field_name), 0)
- .as_bytes()
- .as_ptr() as *const ::std::os::raw::c_char,
- err_hint: ::core::ptr::null(),
- offset: $crate::offset_of!($struct_name, $field_name),
- size: $size,
- start: 0,
- num: $length as _,
- num_offset: 0,
- size_offset: 0,
- info: unsafe { $info },
- flags: VMStateFlags::VMS_ARRAY,
- vmsd: ::core::ptr::null(),
- version_id: $version_id,
- struct_version_id: 0,
- field_exists: None,
+// Transparent wrappers: just use the internal type
+
+macro_rules! impl_vmstate_transparent {
+ ($type:ty where $base:tt: VMState $($where:tt)*) => {
+ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
+ const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE;
+ const BASE: VMStateField = VMStateField {
+ size: mem::size_of::<$type>(),
+ ..<$base as VMState>::BASE
+ };
+ const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG;
}
- }};
+ };
}
-#[doc(alias = "VMSTATE_UINT32_ARRAY_V")]
+impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
+impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
+impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
+impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
+
#[macro_export]
-macro_rules! vmstate_uint32_array_v {
- ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr) => {{
- $crate::vmstate_array!(
- $field_name,
- $struct_name,
- $length,
- $version_id,
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32),
- ::core::mem::size_of::<u32>()
- )
- }};
+macro_rules! impl_vmstate_bitsized {
+ ($type:ty) => {
+ unsafe impl $crate::vmstate::VMState for $type {
+ const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
+ <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
+ as ::bilge::prelude::Number>::UnderlyingType
+ as $crate::vmstate::VMState>::SCALAR_TYPE;
+ const BASE: $crate::bindings::VMStateField =
+ <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
+ as ::bilge::prelude::Number>::UnderlyingType
+ as $crate::vmstate::VMState>::BASE;
+ const VARRAY_FLAG: $crate::bindings::VMStateFlags =
+ <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
+ as ::bilge::prelude::Number>::UnderlyingType
+ as $crate::vmstate::VMState>::VARRAY_FLAG;
+ }
+ };
}
-#[doc(alias = "VMSTATE_UINT32_ARRAY")]
-#[macro_export]
-macro_rules! vmstate_uint32_array {
- ($field_name:ident, $struct_name:ty, $length:expr) => {{
- $crate::vmstate_uint32_array_v!($field_name, $struct_name, $length, 0)
- }};
+// Scalar types using predefined VMStateInfos
+
+macro_rules! impl_vmstate_scalar {
+ ($info:ident, $type:ty$(, $varray_flag:ident)?) => {
+ unsafe impl VMState for $type {
+ const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info;
+ const BASE: VMStateField = VMStateField {
+ size: mem::size_of::<$type>(),
+ flags: VMStateFlags::VMS_SINGLE,
+ ..Zeroable::ZERO
+ };
+ $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)?
+ }
+ };
}
-#[doc(alias = "VMSTATE_STRUCT_POINTER_V")]
-#[macro_export]
-macro_rules! vmstate_struct_pointer_v {
- ($field_name:ident, $struct_name:ty, $version_id:expr, $vmsd:expr, $type:ty) => {{
- $crate::bindings::VMStateField {
- name: ::core::concat!(::core::stringify!($field_name), 0)
- .as_bytes()
- .as_ptr() as *const ::std::os::raw::c_char,
- err_hint: ::core::ptr::null(),
- offset: $crate::offset_of!($struct_name, $field_name),
- size: ::core::mem::size_of::<*const $type>(),
- start: 0,
- num: 0,
- num_offset: 0,
- size_offset: 0,
- info: ::core::ptr::null(),
- flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0),
- vmsd: unsafe { $vmsd },
- version_id: $version_id,
- struct_version_id: 0,
- field_exists: None,
+impl_vmstate_scalar!(vmstate_info_bool, bool);
+impl_vmstate_scalar!(vmstate_info_int8, i8);
+impl_vmstate_scalar!(vmstate_info_int16, i16);
+impl_vmstate_scalar!(vmstate_info_int32, i32);
+impl_vmstate_scalar!(vmstate_info_int64, i64);
+impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8);
+impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16);
+impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
+impl_vmstate_scalar!(vmstate_info_uint64, u64);
+impl_vmstate_scalar!(vmstate_info_timer, bindings::QEMUTimer);
+
+// Pointer types using the underlying type's VMState plus VMS_POINTER
+// Note that references are not supported, though references to cells
+// could be allowed.
+
+macro_rules! impl_vmstate_pointer {
+ ($type:ty where $base:tt: VMState $($where:tt)*) => {
+ unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
+ const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
+ const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag();
}
- }};
+ };
+}
+
+impl_vmstate_pointer!(*const T where T: VMState);
+impl_vmstate_pointer!(*mut T where T: VMState);
+impl_vmstate_pointer!(NonNull<T> where T: VMState);
+
+// Unlike C pointers, Box is always non-null therefore there is no need
+// to specify VMS_ALLOC.
+impl_vmstate_pointer!(Box<T> where T: VMState);
+impl_vmstate_pointer!(Owned<T> where T: VMState + ObjectType);
+
+// Arrays using the underlying type's VMState plus
+// VMS_ARRAY/VMS_ARRAY_OF_POINTER
+
+unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
+ const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
+ const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N);
}
-#[doc(alias = "VMSTATE_ARRAY_OF_POINTER")]
+#[doc(alias = "VMSTATE_UNUSED")]
#[macro_export]
-macro_rules! vmstate_array_of_pointer {
- ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $info:expr, $type:ty) => {{
+macro_rules! vmstate_unused {
+ ($size:expr) => {{
$crate::bindings::VMStateField {
- name: ::core::concat!(::core::stringify!($field_name), 0)
- .as_bytes()
- .as_ptr() as *const ::std::os::raw::c_char,
- version_id: $version_id,
- num: $num as _,
- info: unsafe { $info },
- size: ::core::mem::size_of::<*const $type>(),
- flags: VMStateFlags(VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0),
- offset: $crate::offset_of!($struct_name, $field_name),
- err_hint: ::core::ptr::null(),
- start: 0,
- num_offset: 0,
- size_offset: 0,
- vmsd: ::core::ptr::null(),
- struct_version_id: 0,
- field_exists: None,
+ name: $crate::c_str!("unused").as_ptr(),
+ size: $size,
+ info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
+ flags: $crate::bindings::VMStateFlags::VMS_BUFFER,
+ ..$crate::zeroable::Zeroable::ZERO
}
}};
}
-#[doc(alias = "VMSTATE_ARRAY_OF_POINTER_TO_STRUCT")]
+// FIXME: including the `vmsd` field in a `const` is not possible without
+// the const_refs_static feature (stabilized in Rust 1.83.0). Without it,
+// it is not possible to use VMS_STRUCT in a transparent manner using
+// `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe,
+// VMSTATE_STRUCT includes $type only for documentation purposes; it
+// is checked against $field_name and $struct_name, but not against $vmsd
+// which is what really would matter.
+#[doc(alias = "VMSTATE_STRUCT")]
#[macro_export]
-macro_rules! vmstate_array_of_pointer_to_struct {
- ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $vmsd:expr, $type:ty) => {{
+macro_rules! vmstate_struct {
+ ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(,)?) => {
$crate::bindings::VMStateField {
- name: ::core::concat!(::core::stringify!($field_name), 0)
+ name: ::core::concat!(::core::stringify!($field_name), "\0")
.as_bytes()
.as_ptr() as *const ::std::os::raw::c_char,
- version_id: $version_id,
- num: $num as _,
+ $(.num_offset: $crate::offset_of!($struct_name, $num),)?
+ offset: {
+ $crate::assert_field_type!($struct_name, $field_name, $type);
+ $crate::offset_of!($struct_name, $field_name)
+ },
+ size: ::core::mem::size_of::<$type>(),
+ flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
vmsd: unsafe { $vmsd },
- size: ::core::mem::size_of::<*const $type>(),
- flags: VMStateFlags(
- VMStateFlags::VMS_ARRAY.0
- | VMStateFlags::VMS_STRUCT.0
- | VMStateFlags::VMS_ARRAY_OF_POINTER.0,
- ),
- offset: $crate::offset_of!($struct_name, $field_name),
- err_hint: ::core::ptr::null(),
- start: 0,
- num_offset: 0,
- size_offset: 0,
- vmsd: ::core::ptr::null(),
- struct_version_id: 0,
- field_exists: None,
+ ..$crate::zeroable::Zeroable::ZERO $(
+ .with_varray_flag($crate::call_func_with_field!(
+ $crate::vmstate::vmstate_varray_flag,
+ $struct_name,
+ $num))
+ $(.with_varray_multiply($factor))?)?
}
- }};
-}
-
-#[doc(alias = "VMSTATE_CLOCK_V")]
-#[macro_export]
-macro_rules! vmstate_clock_v {
- ($field_name:ident, $struct_name:ty, $version_id:expr) => {{
- $crate::vmstate_struct_pointer_v!(
- $field_name,
- $struct_name,
- $version_id,
- ::core::ptr::addr_of!($crate::bindings::vmstate_clock),
- $crate::bindings::Clock
- )
- }};
+ };
}
#[doc(alias = "VMSTATE_CLOCK")]
#[macro_export]
macro_rules! vmstate_clock {
- ($field_name:ident, $struct_name:ty) => {{
- $crate::vmstate_clock_v!($field_name, $struct_name, 0)
- }};
-}
-
-#[doc(alias = "VMSTATE_ARRAY_CLOCK_V")]
-#[macro_export]
-macro_rules! vmstate_array_clock_v {
- ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr) => {{
- $crate::vmstate_array_of_pointer_to_struct!(
- $field_name,
- $struct_name,
- $num,
- $version_id,
- ::core::ptr::addr_of!($crate::bindings::vmstate_clock),
- $crate::bindings::Clock
- )
- }};
-}
-
-#[doc(alias = "VMSTATE_ARRAY_CLOCK")]
-#[macro_export]
-macro_rules! vmstate_array_clock {
- ($field_name:ident, $struct_name:ty, $num:expr) => {{
- $crate::vmstate_array_clock_v!($field_name, $struct_name, $name, 0)
+ ($struct_name:ty, $field_name:ident) => {{
+ $crate::bindings::VMStateField {
+ name: ::core::concat!(::core::stringify!($field_name), "\0")
+ .as_bytes()
+ .as_ptr() as *const ::std::os::raw::c_char,
+ offset: {
+ $crate::assert_field_type!(
+ $struct_name,
+ $field_name,
+ $crate::qom::Owned<$crate::bindings::Clock>
+ );
+ $crate::offset_of!($struct_name, $field_name)
+ },
+ size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
+ flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0),
+ vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
+ ..$crate::zeroable::Zeroable::ZERO
+ }
}};
}
@@ -287,20 +490,8 @@ macro_rules! vmstate_fields {
static _FIELDS: &[$crate::bindings::VMStateField] = &[
$($field),*,
$crate::bindings::VMStateField {
- name: ::core::ptr::null(),
- err_hint: ::core::ptr::null(),
- offset: 0,
- size: 0,
- start: 0,
- num: 0,
- num_offset: 0,
- size_offset: 0,
- info: ::core::ptr::null(),
- flags: VMStateFlags::VMS_END,
- vmsd: ::core::ptr::null(),
- version_id: 0,
- struct_version_id: 0,
- field_exists: None,
+ flags: $crate::bindings::VMStateFlags::VMS_END,
+ ..$crate::zeroable::Zeroable::ZERO
}
];
_FIELDS.as_ptr()
diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs
index 6125aee..47b6977 100644
--- a/rust/qemu-api/src/zeroable.rs
+++ b/rust/qemu-api/src/zeroable.rs
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::ptr;
+//! Defines a trait for structs that can be safely initialized with zero bytes.
/// Encapsulates the requirement that
/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
@@ -29,58 +29,80 @@ pub unsafe trait Zeroable: Default {
const ZERO: Self;
}
-unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {
- const ZERO: Self = Self { i: 0 };
-}
-
-unsafe impl Zeroable for crate::bindings::Property {
- const ZERO: Self = Self {
- name: ptr::null(),
- info: ptr::null(),
- offset: 0,
- bitnr: 0,
- bitmask: 0,
- set_default: false,
- defval: Zeroable::ZERO,
- arrayoffset: 0,
- arrayinfo: ptr::null(),
- arrayfieldsize: 0,
- link_type: ptr::null(),
- };
+/// 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)
+ }};
}
-unsafe impl Zeroable for crate::bindings::VMStateDescription {
- const ZERO: Self = Self {
- name: ptr::null(),
- unmigratable: false,
- early_setup: false,
- version_id: 0,
- minimum_version_id: 0,
- priority: crate::bindings::MigrationPriority::MIG_PRI_DEFAULT,
- pre_load: None,
- post_load: None,
- pre_save: None,
- post_save: None,
- needed: None,
- dev_unplug_pending: None,
- fields: ptr::null(),
- subsections: ptr::null(),
+/// 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) };
+ }
};
}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {
- const ZERO: Self = Self {
- min_access_size: 0,
- max_access_size: 0,
- unaligned: false,
- accepts: None,
- };
+// 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::MemoryRegionOps__bindgen_ty_2 {
- const ZERO: Self = Self {
- min_access_size: 0,
- max_access_size: 0,
- unaligned: false,
- };
-}
+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);
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 1d2825b..92dbfb8 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -3,8 +3,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{
- ffi::CStr,
- os::raw::c_void,
+ ffi::{c_void, CStr},
ptr::{addr_of, addr_of_mut},
};
@@ -14,8 +13,8 @@ use qemu_api::{
cell::{self, BqlCell},
declare_properties, define_property,
prelude::*,
- qdev::{DeviceImpl, DeviceState, Property},
- qom::ObjectImpl,
+ qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
+ qom::{ClassInitImpl, ObjectImpl, ParentField},
vmstate::VMStateDescription,
zeroable::Zeroable,
};
@@ -31,12 +30,16 @@ pub static VMSTATE: VMStateDescription = VMStateDescription {
#[repr(C)]
#[derive(qemu_api_macros::Object)]
pub struct DummyState {
- parent: DeviceState,
+ parent: ParentField<DeviceState>,
migrate_clock: bool,
}
qom_isa!(DummyState: Object, DeviceState);
+pub struct DummyClass {
+ parent_class: <DeviceState as ObjectType>::Class,
+}
+
declare_properties! {
DUMMY_PROPERTIES,
define_property!(
@@ -49,7 +52,7 @@ declare_properties! {
}
unsafe impl ObjectType for DummyState {
- type Class = <DeviceState as ObjectType>::Class;
+ type Class = DummyClass;
const TYPE_NAME: &'static CStr = c_str!("dummy");
}
@@ -58,6 +61,8 @@ impl ObjectImpl for DummyState {
const ABSTRACT: bool = false;
}
+impl ResettablePhasesImpl for DummyState {}
+
impl DeviceImpl for DummyState {
fn properties() -> &'static [Property] {
&DUMMY_PROPERTIES
@@ -67,6 +72,52 @@ impl DeviceImpl for DummyState {
}
}
+// `impl<T> ClassInitImpl<DummyClass> for T` doesn't work since it violates
+// orphan rule.
+impl ClassInitImpl<DummyClass> for DummyState {
+ fn class_init(klass: &mut DummyClass) {
+ <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class);
+ }
+}
+
+#[derive(qemu_api_macros::offsets)]
+#[repr(C)]
+#[derive(qemu_api_macros::Object)]
+pub struct DummyChildState {
+ parent: ParentField<DummyState>,
+}
+
+qom_isa!(DummyChildState: Object, DeviceState, DummyState);
+
+pub struct DummyChildClass {
+ parent_class: <DummyState as ObjectType>::Class,
+}
+
+unsafe impl ObjectType for DummyChildState {
+ type Class = DummyChildClass;
+ const TYPE_NAME: &'static CStr = c_str!("dummy_child");
+}
+
+impl ObjectImpl for DummyChildState {
+ type ParentType = DummyState;
+ const ABSTRACT: bool = false;
+}
+
+impl ResettablePhasesImpl for DummyChildState {}
+impl DeviceImpl for DummyChildState {}
+
+impl ClassInitImpl<DummyClass> for DummyChildState {
+ fn class_init(klass: &mut DummyClass) {
+ <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class);
+ }
+}
+
+impl ClassInitImpl<DummyChildClass> for DummyChildState {
+ fn class_init(klass: &mut DummyChildClass) {
+ <Self as ClassInitImpl<DummyClass>>::class_init(&mut klass.parent_class);
+ }
+}
+
fn init_qom() {
static ONCE: BqlCell<bool> = BqlCell::new(false);
@@ -83,21 +134,26 @@ fn init_qom() {
/// Create and immediately drop an instance.
fn test_object_new() {
init_qom();
- unsafe {
- object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
- }
+ drop(DummyState::new());
+ drop(DummyChildState::new());
+}
+
+#[test]
+#[allow(clippy::redundant_clone)]
+/// Create, clone and then drop an instance.
+fn test_clone() {
+ init_qom();
+ let p = DummyState::new();
+ assert_eq!(p.clone().typename(), "dummy");
+ drop(p);
}
#[test]
/// Try invoking a method on an object.
fn test_typename() {
init_qom();
- let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
- let p_ref: &DummyState = unsafe { &*p };
- assert_eq!(p_ref.typename(), "dummy");
- unsafe {
- object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
- }
+ let p = DummyState::new();
+ assert_eq!(p.typename(), "dummy");
}
// a note on all "cast" tests: usually, especially for downcasts the desired
@@ -112,24 +168,23 @@ fn test_typename() {
/// Test casts on shared references.
fn test_cast() {
init_qom();
- let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
+ let p = DummyState::new();
+ let p_ptr: *mut DummyState = p.as_mut_ptr();
+ let p_ref: &mut DummyState = unsafe { &mut *p_ptr };
- let p_ref: &DummyState = unsafe { &*p };
let obj_ref: &Object = p_ref.upcast();
- assert_eq!(addr_of!(*obj_ref), p.cast());
+ assert_eq!(addr_of!(*obj_ref), p_ptr.cast());
let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast();
assert!(sbd_ref.is_none());
let dev_ref: Option<&DeviceState> = obj_ref.downcast();
- assert_eq!(addr_of!(*dev_ref.unwrap()), p.cast());
+ assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast());
// SAFETY: the cast is wrong, but the value is only used for comparison
unsafe {
let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast();
- assert_eq!(addr_of!(*sbd_ref), p.cast());
-
- object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
+ assert_eq!(addr_of!(*sbd_ref), p_ptr.cast());
}
}