diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-02-14 08:18:56 -0500 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-02-14 08:18:56 -0500 |
commit | b4b0880c3aae98e5c08d4d785b0fe16f0b780081 (patch) | |
tree | a6b916597ebcc01c6de58b0aa84a9cce4893a294 /rust/qemu-api | |
parent | ce315328f8e9bf5201db4217f3ffe0784110aa4b (diff) | |
parent | 4dafba778aa3e5f5fd3b2c6333afd7650dcf54e2 (diff) | |
download | qemu-b4b0880c3aae98e5c08d4d785b0fe16f0b780081.zip qemu-b4b0880c3aae98e5c08d4d785b0fe16f0b780081.tar.gz qemu-b4b0880c3aae98e5c08d4d785b0fe16f0b780081.tar.bz2 |
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* rust: more qdev bindings
* rust: HPET device model with timer and GPIO bindings
* rust: small cleanups and fixes; run doctests during CI
* ui/sdl2: reenable the SDL2 Windows keyboard hook procedure
# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmet6qkUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroO4yQgAjSpJ8DChoEVrm6xgCUGPkC7VlI0A
# 3WimcgiTUCUVqiywvLmObHRv9ld/b9mJ+2v/actDy39qioN3i3+RGpyeSRcysITd
# 2AWQVOe6JuVfEyN+ihYq3yS3v1meDhzZbOzRNHgbTX20rMy/HWJFIvQbK4abQaVI
# j8zaPYIjcfcH/ScEmmha88l6PJDMPy7fCEzQWx41oHKkQ8w4rhmarn9f3WcXB/SN
# bCvm2NmkJFPsU/TCynWz7YSjrLWCsWjiDgxoDD1295QoeEvfcuD8Z6vPIA9BttGx
# MUgcrXi4KnJI8W9gm5jAiKq+DSxFX6f7AwUDfb2l+Vrkq84s7bu9UVNQqA==
# =/vpW
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 13 Feb 2025 07:50:49 EST
# gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg: issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1
# Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83
* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (27 commits)
ui/sdl2: reenable the SDL2 Windows keyboard hook procedure
rust: fix doctests
rust: vmstate: remove redundant link targets
rust: qemu_api: add a documentation header for all modules
i386: enable rust hpet for pc when rust is enabled
rust/timer/hpet: add qom and qdev APIs support
rust/timer/hpet: add basic HPET timer and HPETState
rust/timer/hpet: define hpet_fw_cfg
rust: add bindings for timer
rust: add bindings for memattrs
rust: add bindings for gpio_{in|out} initialization
rust/irq: Add a helper to convert [InterruptSource] to pointer
rust/qdev: add the macro to define bit property
i386/fw_cfg: move hpet_cfg definition to hpet.c
rust: pl011: convert pl011_create to safe Rust
rust: chardev, qdev: add bindings to qdev_prop_set_chr
rust: irq: define ObjectType for IRQState
rust: bindings for MemoryRegionOps
rust: bindings: add Send and Sync markers for types that have bindings
rust: qdev: switch from legacy reset to Resettable
...
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust/qemu-api')
-rw-r--r-- | rust/qemu-api/meson.build | 3 | ||||
-rw-r--r-- | rust/qemu-api/src/assertions.rs | 4 | ||||
-rw-r--r-- | rust/qemu-api/src/bindings.rs | 48 | ||||
-rw-r--r-- | rust/qemu-api/src/c_str.rs | 8 | ||||
-rw-r--r-- | rust/qemu-api/src/callbacks.rs | 97 | ||||
-rw-r--r-- | rust/qemu-api/src/chardev.rs | 19 | ||||
-rw-r--r-- | rust/qemu-api/src/irq.rs | 23 | ||||
-rw-r--r-- | rust/qemu-api/src/lib.rs | 3 | ||||
-rw-r--r-- | rust/qemu-api/src/memory.rs | 203 | ||||
-rw-r--r-- | rust/qemu-api/src/offset_of.rs | 7 | ||||
-rw-r--r-- | rust/qemu-api/src/prelude.rs | 6 | ||||
-rw-r--r-- | rust/qemu-api/src/qdev.rs | 276 | ||||
-rw-r--r-- | rust/qemu-api/src/qom.rs | 226 | ||||
-rw-r--r-- | rust/qemu-api/src/sysbus.rs | 41 | ||||
-rw-r--r-- | rust/qemu-api/src/timer.rs | 98 | ||||
-rw-r--r-- | rust/qemu-api/src/vmstate.rs | 13 | ||||
-rw-r--r-- | rust/qemu-api/src/zeroable.rs | 12 | ||||
-rw-r--r-- | rust/qemu-api/tests/tests.rs | 45 |
18 files changed, 1065 insertions, 67 deletions
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 60944a6..2e9c107 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -20,14 +20,17 @@ _qemu_api_rs = static_library( '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 index 6e42046..fa1a18d 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -2,9 +2,13 @@ // 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). 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 378e520..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,7 +22,8 @@ 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 [`SysBusDeviceMethods::init_irq`], and @@ -43,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) { @@ -78,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 { @@ -88,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 3cf9371..ed1a8f9 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -18,12 +18,15 @@ 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 2dc86e1..fbf0ee2 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -2,16 +2,22 @@ // 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; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index f4c75c7..3a7aa4d 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -4,19 +4,89 @@ //! Bindings to create devices and access device functionality from Rust. -use std::{ffi::CStr, ptr::NonNull}; +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). @@ -25,13 +95,6 @@ pub trait DeviceImpl { /// with the function pointed to by `REALIZE`. const REALIZE: Option<fn(&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(&Self)> = None; - /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants /// is unstable until Rust 1.83.0. @@ -59,29 +122,36 @@ unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp 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) { - let mut state = NonNull::new(dev).unwrap().cast::<T>(); - T::RESET.unwrap()(unsafe { state.as_mut() }); +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; } @@ -92,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 @@ -143,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 f50ee37..3d5ab2d 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -56,6 +56,7 @@ use std::{ ffi::CStr, fmt, + mem::ManuallyDrop, ops::{Deref, DerefMut}, os::raw::c_void, ptr::NonNull, @@ -63,7 +64,13 @@ use std::{ 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`). @@ -256,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`] @@ -280,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>, { @@ -610,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>`. /// @@ -641,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 e6762b5..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 { @@ -42,10 +45,10 @@ where /// 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: &bindings::MemoryRegion) { + fn init_mmio(&self, iomem: &MemoryRegion) { assert!(bql_locked()); unsafe { - bindings::sysbus_init_mmio(self.as_mut_ptr(), addr_of!(*iomem) as *mut _); + bindings::sysbus_init_mmio(self.as_mut_ptr(), iomem.as_mut_ptr()); } } @@ -59,6 +62,34 @@ where 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 6ac432c..24a4dc8 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -29,6 +29,8 @@ use core::{marker::PhantomData, mem, ptr::NonNull}; pub use crate::bindings::{VMStateDescription, VMStateField}; use crate::{ bindings::{self, VMStateFlags}, + prelude::*, + qom::Owned, zeroable::Zeroable, }; @@ -189,9 +191,9 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags /// * scalar types (integer and `bool`) /// * the C struct `QEMUTimer` /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, -/// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) +/// [`BqlCell`], [`BqlRefCell`] /// * a raw pointer to any of the above -/// * a `NonNull` pointer or a `Box` for 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 @@ -292,7 +294,7 @@ impl VMStateField { /// # Examples /// /// ``` -/// # use qemu_api::vmstate::impl_vmstate_forward; +/// # use qemu_api::impl_vmstate_forward; /// pub struct Fifo([u8; 16]); /// impl_vmstate_forward!(Fifo); /// ``` @@ -398,6 +400,7 @@ 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 @@ -466,11 +469,11 @@ macro_rules! vmstate_clock { $crate::assert_field_type!( $struct_name, $field_name, - core::ptr::NonNull<$crate::bindings::Clock> + $crate::qom::Owned<$crate::bindings::Clock> ); $crate::offset_of!($struct_name, $field_name) }, - size: ::core::mem::size_of::<*const $crate::bindings::Clock>(), + 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 diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 7b04947..47b6977 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later +//! Defines a trait for structs that can be safely initialized with zero bytes. + /// Encapsulates the requirement that /// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined /// behavior. This trait in principle could be implemented as just: /// /// ``` -/// pub unsafe trait Zeroable { +/// pub unsafe trait Zeroable: Default { /// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() }; /// } /// ``` @@ -56,6 +58,7 @@ pub unsafe trait Zeroable: Default { /// ## 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): @@ -77,10 +80,11 @@ macro_rules! const_zero { } /// A wrapper to implement the `Zeroable` trait through the `const_zero` macro. +#[macro_export] macro_rules! impl_zeroable { ($type:ty) => { - unsafe impl Zeroable for $type { - const ZERO: Self = unsafe { const_zero!($type) }; + unsafe impl $crate::zeroable::Zeroable for $type { + const ZERO: Self = unsafe { $crate::const_zero!($type) }; } }; } @@ -100,3 +104,5 @@ 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 5c3e75e..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,7 +13,7 @@ use qemu_api::{ cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, + qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, @@ -62,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 @@ -102,6 +103,7 @@ impl ObjectImpl for DummyChildState { const ABSTRACT: bool = false; } +impl ResettablePhasesImpl for DummyChildState {} impl DeviceImpl for DummyChildState {} impl ClassInitImpl<DummyClass> for DummyChildState { @@ -132,22 +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()); - object_unref(object_new(DummyChildState::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 @@ -162,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()); } } |