aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2024-12-12 18:40:32 -0500
committerStefan Hajnoczi <stefanha@redhat.com>2024-12-12 18:40:32 -0500
commit1eec82cc06cd68b6ffaf13ba8337fac0080c7bce (patch)
tree25f66000339cca55497068c9f826d8c877185558 /rust/qemu-api
parent2a1823456c3ab500a90c3fe0cfcc5fc6f560282b (diff)
parent166e8a1fd15bfa527b25fc15ca315e572c0556d2 (diff)
downloadqemu-1eec82cc06cd68b6ffaf13ba8337fac0080c7bce.zip
qemu-1eec82cc06cd68b6ffaf13ba8337fac0080c7bce.tar.gz
qemu-1eec82cc06cd68b6ffaf13ba8337fac0080c7bce.tar.bz2
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* rust: better integration with clippy, rustfmt and rustdoc * rust: interior mutability types * rust: add a bit operations module * rust: first part of QOM rework * kvm: remove unnecessary #ifdef * clock: small cleanups, improve handling of Clock lifetimes # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmdZqFkUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroOzRwf/SYUD+CJCn2x7kUH/JG893jwN1WbJ # meGZ0PQDUpOZJFWg6T4g0MuW4O+Wevy2pF4SfGojgqaYxKBbTQVkeliDEMyNUxpr # vSKXego0K3pkX3cRDXNVTaXFbsHsMt/3pfzMQM6ocF9qbL+Emvx7Og6WdAcyJ4hc # lA17EHlnrWKUSnqN/Ow/pZXsa4ijCklXFFh4barfbdGVhMQc2QekUU45GsP2AvGT # NkXTQC05HqxBaAIDeSxbprDSzNihyT71dAooVoxqKboprPu5uoUSJwgaD8rADPr4 # EOfsz61V4mji+DWDcIzTtYoAdY41vVXI9lvCKOcCFkimA29xO0W6P7mG2w== # =JSh5 # -----END PGP SIGNATURE----- # gpg: Signature made Wed 11 Dec 2024 09:57:29 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: (49 commits) rust: qom: change the parent type to an associated type rust: qom: split ObjectType from ObjectImpl trait rust: qom: move bridge for TypeInfo functions out of pl011 rust: qdev: move bridge for realize and reset functions out of pl011 rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro rust: qom: move ClassInitImpl to the instance side rust: qom: convert type_info! macro to an associated const rust: qom: rename Class trait to ClassInitImpl rust: qom: add default definitions for ObjectImpl rust: add a bit operation module rust: add bindings for interrupt sources rust: define prelude rust: cell: add BQL-enforcing RefCell variant rust: cell: add BQL-enforcing Cell variant bql: check that the BQL is not dropped within marked sections qom/object: Remove type_register() script/codeconverter/qom_type_info: Deprecate MakeTypeRegisterStatic and MakeTypeRegisterNotStatic ui: Replace type_register() with type_register_static() target/xtensa: Replace type_register() with type_register_static() target/sparc: Replace type_register() with type_register_static() ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust/qemu-api')
-rw-r--r--rust/qemu-api/.gitignore2
-rw-r--r--rust/qemu-api/Cargo.toml8
-rw-r--r--rust/qemu-api/README.md10
-rw-r--r--rust/qemu-api/build.rs39
-rw-r--r--rust/qemu-api/meson.build14
-rw-r--r--rust/qemu-api/src/bindings.rs29
-rw-r--r--rust/qemu-api/src/bitops.rs119
-rw-r--r--rust/qemu-api/src/cell.rs822
-rw-r--r--rust/qemu-api/src/definitions.rs145
-rw-r--r--rust/qemu-api/src/device_class.rs126
-rw-r--r--rust/qemu-api/src/irq.rs91
-rw-r--r--rust/qemu-api/src/lib.rs29
-rw-r--r--rust/qemu-api/src/prelude.rs10
-rw-r--r--rust/qemu-api/src/sysbus.rs33
-rw-r--r--rust/qemu-api/src/zeroable.rs6
-rw-r--r--rust/qemu-api/tests/tests.rs43
16 files changed, 1402 insertions, 124 deletions
diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore
index b9e7e00..df6c216 100644
--- a/rust/qemu-api/.gitignore
+++ b/rust/qemu-api/.gitignore
@@ -1,2 +1,2 @@
# Ignore generated bindings file overrides.
-src/bindings.rs
+/src/bindings.inc.rs
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index cc716d7..4aa22f3 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -20,9 +20,9 @@ qemu_api_macros = { path = "../qemu-api-macros" }
version_check = "~0.9"
[features]
-default = []
+default = ["debug_cell"]
allocator = []
+debug_cell = []
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
- 'cfg(has_offset_of)'] }
+[lints]
+workspace = true
diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md
index 7588fa2..ed1b7ab 100644
--- a/rust/qemu-api/README.md
+++ b/rust/qemu-api/README.md
@@ -5,13 +5,15 @@ This library exports helper Rust types, Rust macros and C FFI bindings for inter
The C bindings can be generated with `bindgen`, using this build target:
```console
-$ ninja bindings.rs
+$ make bindings.inc.rs
```
## Generate Rust documentation
-To generate docs for this crate, including private items:
+Common Cargo tasks can be performed from the QEMU build directory
-```sh
-cargo doc --no-deps --document-private-items
+```console
+$ make clippy
+$ make rustfmt
+$ make rustdoc
```
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
index 20f8f71..471e6c6 100644
--- a/rust/qemu-api/build.rs
+++ b/rust/qemu-api/build.rs
@@ -2,17 +2,41 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::path::Path;
+#[cfg(unix)]
+use std::os::unix::fs::symlink as symlink_file;
+#[cfg(windows)]
+use std::os::windows::fs::symlink_file;
+use std::{env, fs::remove_file, io::Result, path::Path};
use version_check as rustc;
-fn main() {
- if !Path::new("src/bindings.rs").exists() {
- panic!(
- "No generated C bindings found! Either build them manually with bindgen or with meson \
- (`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson."
- );
+fn main() -> Result<()> {
+ // Placing bindings.inc.rs in the source directory is supported
+ // but not documented or encouraged.
+ let path = env::var("MESON_BUILD_ROOT")
+ .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
+
+ let file = format!("{}/bindings.inc.rs", path);
+ let file = Path::new(&file);
+ if !Path::new(&file).exists() {
+ panic!(concat!(
+ "\n",
+ " No generated C bindings found! Maybe you wanted one of\n",
+ " `make clippy`, `make rustfmt`, `make rustdoc`?\n",
+ "\n",
+ " For other uses of `cargo`, start a subshell with\n",
+ " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n",
+ " the top of the build tree."
+ ));
+ }
+
+ let out_dir = env::var("OUT_DIR").unwrap();
+ let dest_path = format!("{}/bindings.inc.rs", out_dir);
+ let dest_path = Path::new(&dest_path);
+ if dest_path.symlink_metadata().is_ok() {
+ remove_file(dest_path)?;
}
+ symlink_file(file, dest_path)?;
// Check for available rustc features
if rustc::is_min_version("1.77.0").unwrap_or(false) {
@@ -20,4 +44,5 @@ fn main() {
}
println!("cargo:rerun-if-changed=build.rs");
+ Ok(())
}
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 6f637af..adcee66 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -1,18 +1,30 @@
-_qemu_api_cfg = ['--cfg', 'MESON']
+_qemu_api_cfg = run_command(rustc_args,
+ '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+ capture: true, check: true).stdout().strip().splitlines()
+
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
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']
+endif
_qemu_api_rs = static_library(
'qemu_api',
structured_sources(
[
'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/bitops.rs',
+ 'src/cell.rs',
'src/c_str.rs',
'src/definitions.rs',
'src/device_class.rs',
+ 'src/irq.rs',
'src/offset_of.rs',
+ 'src/prelude.rs',
+ 'src/sysbus.rs',
'src/vmstate.rs',
'src/zeroable.rs',
],
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
new file mode 100644
index 0000000..8a9b821
--- /dev/null
+++ b/rust/qemu-api/src/bindings.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc
+)]
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+unsafe impl Send for Property {}
+unsafe impl Sync for Property {}
+unsafe impl Sync for TypeInfo {}
+unsafe impl Sync for VMStateDescription {}
+unsafe impl Sync for VMStateField {}
+unsafe impl Sync for VMStateInfo {}
diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
new file mode 100644
index 0000000..023ec1a
--- /dev/null
+++ b/rust/qemu-api/src/bitops.rs
@@ -0,0 +1,119 @@
+// Copyright (C) 2024 Intel Corporation.
+// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! This module provides bit operation extensions to integer types.
+//! It is usually included via the `qemu_api` prelude.
+
+use std::ops::{
+ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
+ Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign,
+};
+
+/// Trait for extensions to integer types
+pub trait IntegerExt:
+ Add<Self, Output = Self> + AddAssign<Self> +
+ BitAnd<Self, Output = Self> + BitAndAssign<Self> +
+ BitOr<Self, Output = Self> + BitOrAssign<Self> +
+ BitXor<Self, Output = Self> + BitXorAssign<Self> +
+ Copy +
+ Div<Self, Output = Self> + DivAssign<Self> +
+ Eq +
+ Mul<Self, Output = Self> + MulAssign<Self> +
+ Not<Output = Self> + Ord + PartialOrd +
+ Rem<Self, Output = Self> + RemAssign<Self> +
+ Shl<Self, Output = Self> + ShlAssign<Self> +
+ Shl<u32, Output = Self> + ShlAssign<u32> + // add more as needed
+ Shr<Self, Output = Self> + ShrAssign<Self> +
+ Shr<u32, Output = Self> + ShrAssign<u32> // add more as needed
+{
+ const BITS: u32;
+ const MAX: Self;
+ const MIN: Self;
+ const ONE: Self;
+ const ZERO: Self;
+
+ #[inline]
+ #[must_use]
+ fn bit(start: u32) -> Self
+ {
+ debug_assert!(start < Self::BITS);
+
+ Self::ONE << start
+ }
+
+ #[inline]
+ #[must_use]
+ fn mask(start: u32, length: u32) -> Self
+ {
+ /* FIXME: Implement a more elegant check with error handling support? */
+ debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start);
+
+ (Self::MAX >> (Self::BITS - length)) << start
+ }
+
+ #[inline]
+ #[must_use]
+ fn deposit<U: IntegerExt>(self, start: u32, length: u32,
+ fieldval: U) -> Self
+ where Self: From<U>
+ {
+ debug_assert!(length <= U::BITS);
+
+ let mask = Self::mask(start, length);
+ (self & !mask) | ((Self::from(fieldval) << start) & mask)
+ }
+
+ #[inline]
+ #[must_use]
+ fn extract(self, start: u32, length: u32) -> Self
+ {
+ let mask = Self::mask(start, length);
+ (self & mask) >> start
+ }
+}
+
+macro_rules! impl_num_ext {
+ ($type:ty) => {
+ impl IntegerExt for $type {
+ const BITS: u32 = <$type>::BITS;
+ const MAX: Self = <$type>::MAX;
+ const MIN: Self = <$type>::MIN;
+ const ONE: Self = 1;
+ const ZERO: Self = 0;
+ }
+ };
+}
+
+impl_num_ext!(u8);
+impl_num_ext!(u16);
+impl_num_ext!(u32);
+impl_num_ext!(u64);
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_deposit() {
+ assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15);
+ assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15);
+ }
+
+ #[test]
+ fn test_extract() {
+ assert_eq!(15u32.extract(2, 4), 3);
+ }
+
+ #[test]
+ fn test_bit() {
+ assert_eq!(u8::bit(7), 128);
+ assert_eq!(u32::bit(16), 0x10000);
+ }
+
+ #[test]
+ fn test_mask() {
+ assert_eq!(u8::mask(7, 1), 128);
+ assert_eq!(u32::mask(8, 8), 0xff00);
+ }
+}
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
new file mode 100644
index 0000000..28349de
--- /dev/null
+++ b/rust/qemu-api/src/cell.rs
@@ -0,0 +1,822 @@
+// SPDX-License-Identifier: MIT
+//
+// This file is based on library/core/src/cell.rs from
+// Rust 1.82.0.
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and associated
+// documentation files (the "Software"), to deal in the
+// Software without restriction, including without
+// limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+//! BQL-protected mutable containers.
+//!
+//! Rust memory safety is based on this rule: Given an object `T`, it is only
+//! possible to have one of the following:
+//!
+//! - Having several immutable references (`&T`) to the object (also known as
+//! **aliasing**).
+//! - Having one mutable reference (`&mut T`) to the object (also known as
+//! **mutability**).
+//!
+//! This is enforced by the Rust compiler. However, there are situations where
+//! this rule is not flexible enough. Sometimes it is required to have multiple
+//! references to an object and yet mutate it. In particular, QEMU objects
+//! usually have their pointer shared with the "outside world very early in
+//! their lifetime", for example when they create their
+//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual
+//! parts of a device must be made mutable in a controlled manner through the
+//! use of cell types.
+//!
+//! [`BqlCell<T>`] and [`BqlRefCell<T>`] allow doing this via the Big QEMU Lock.
+//! While they are essentially the same single-threaded primitives that are
+//! available in `std::cell`, the BQL allows them to be used from a
+//! multi-threaded context and to share references across threads, while
+//! maintaining Rust's safety guarantees. For this reason, unlike
+//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the
+//! `Sync` trait.
+//!
+//! BQL checks are performed in debug builds but can be optimized away in
+//! release builds, providing runtime safety during development with no overhead
+//! in production.
+//!
+//! The two provide different ways of handling interior mutability.
+//! `BqlRefCell` is best suited for data that is primarily accessed by the
+//! device's own methods, where multiple reads and writes can be grouped within
+//! a single borrow and a mutable reference can be passed around. Instead,
+//! [`BqlCell`] is a better choice when sharing small pieces of data with
+//! external code (especially C code), because it provides simple get/set
+//! operations that can be used one at a time.
+//!
+//! Warning: While `BqlCell` and `BqlRefCell` are similar to their `std::cell`
+//! counterparts, they are not interchangeable. Using `std::cell` types in
+//! QEMU device implementations is usually incorrect and can lead to
+//! thread-safety issues.
+//!
+//! ## `BqlCell<T>`
+//!
+//! [`BqlCell<T>`] implements interior mutability by moving values in and out of
+//! the cell. That is, an `&mut T` to the inner value can never be obtained as
+//! long as the cell is shared. The value itself cannot be directly obtained
+//! without copying it, cloning it, or replacing it with something else. This
+//! type provides the following methods, all of which can be called only while
+//! the BQL is held:
+//!
+//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method
+//! retrieves the current interior value by duplicating it.
+//! - For types that implement [`Default`], the [`take`](BqlCell::take) method
+//! replaces the current interior value with [`Default::default()`] and
+//! returns the replaced value.
+//! - All types have:
+//! - [`replace`](BqlCell::replace): replaces the current interior value and
+//! returns the replaced value.
+//! - [`set`](BqlCell::set): this method replaces the interior value,
+//! dropping the replaced value.
+//!
+//! ## `BqlRefCell<T>`
+//!
+//! [`BqlRefCell<T>`] uses Rust's lifetimes to implement "dynamic borrowing", a
+//! process whereby one can claim temporary, exclusive, mutable access to the
+//! inner value:
+//!
+//! ```ignore
+//! fn clear_interrupts(&self, val: u32) {
+//! // A mutable borrow gives read-write access to the registers
+//! let mut regs = self.registers.borrow_mut();
+//! let old = regs.interrupt_status();
+//! regs.update_interrupt_status(old & !val);
+//! }
+//! ```
+//!
+//! Borrows for `BqlRefCell<T>`s are tracked at _runtime_, unlike Rust's native
+//! reference types which are entirely tracked statically, at compile time.
+//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
+//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The
+//! thread will panic if these rules are violated or if the BQL is not held.
+
+use std::{
+ cell::{Cell, UnsafeCell},
+ cmp::Ordering,
+ fmt,
+ marker::PhantomData,
+ mem,
+ ops::{Deref, DerefMut},
+ ptr::NonNull,
+};
+
+use crate::bindings;
+
+// TODO: When building doctests do not include the actual BQL, because cargo
+// does not know how to link them to libqemuutil. This can be fixed by
+// running rustdoc from "meson test" instead of relying on cargo.
+pub fn bql_locked() -> bool {
+ // SAFETY: the function does nothing but return a thread-local bool
+ !cfg!(MESON) || unsafe { bindings::bql_locked() }
+}
+
+fn bql_block_unlock(increase: bool) {
+ if cfg!(MESON) {
+ // SAFETY: this only adjusts a counter
+ unsafe {
+ bindings::bql_block_unlock(increase);
+ }
+ }
+}
+
+/// A mutable memory location that is protected by the Big QEMU Lock.
+///
+/// # Memory layout
+///
+/// `BqlCell<T>` has the same in-memory representation as its inner type `T`.
+#[repr(transparent)]
+pub struct BqlCell<T> {
+ value: UnsafeCell<T>,
+}
+
+// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex,
+// except it is stored out-of-line
+unsafe impl<T: Send> Send for BqlCell<T> {}
+unsafe impl<T: Send> Sync for BqlCell<T> {}
+
+impl<T: Copy> Clone for BqlCell<T> {
+ #[inline]
+ fn clone(&self) -> BqlCell<T> {
+ BqlCell::new(self.get())
+ }
+}
+
+impl<T: Default> Default for BqlCell<T> {
+ /// Creates a `BqlCell<T>`, with the `Default` value for T.
+ #[inline]
+ fn default() -> BqlCell<T> {
+ BqlCell::new(Default::default())
+ }
+}
+
+impl<T: PartialEq + Copy> PartialEq for BqlCell<T> {
+ #[inline]
+ fn eq(&self, other: &BqlCell<T>) -> bool {
+ self.get() == other.get()
+ }
+}
+
+impl<T: Eq + Copy> Eq for BqlCell<T> {}
+
+impl<T: PartialOrd + Copy> PartialOrd for BqlCell<T> {
+ #[inline]
+ fn partial_cmp(&self, other: &BqlCell<T>) -> Option<Ordering> {
+ self.get().partial_cmp(&other.get())
+ }
+}
+
+impl<T: Ord + Copy> Ord for BqlCell<T> {
+ #[inline]
+ fn cmp(&self, other: &BqlCell<T>) -> Ordering {
+ self.get().cmp(&other.get())
+ }
+}
+
+impl<T> From<T> for BqlCell<T> {
+ /// Creates a new `BqlCell<T>` containing the given value.
+ fn from(t: T) -> BqlCell<T> {
+ BqlCell::new(t)
+ }
+}
+
+impl<T: fmt::Debug + Copy> fmt::Debug for BqlCell<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.get().fmt(f)
+ }
+}
+
+impl<T: fmt::Display + Copy> fmt::Display for BqlCell<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.get().fmt(f)
+ }
+}
+
+impl<T> BqlCell<T> {
+ /// Creates a new `BqlCell` containing the given value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ /// ```
+ #[inline]
+ pub const fn new(value: T) -> BqlCell<T> {
+ BqlCell {
+ value: UnsafeCell::new(value),
+ }
+ }
+
+ /// Sets the contained value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ ///
+ /// c.set(10);
+ /// ```
+ #[inline]
+ pub fn set(&self, val: T) {
+ self.replace(val);
+ }
+
+ /// Replaces the contained value with `val`, and returns the old contained
+ /// value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let cell = BqlCell::new(5);
+ /// assert_eq!(cell.get(), 5);
+ /// assert_eq!(cell.replace(10), 5);
+ /// assert_eq!(cell.get(), 10);
+ /// ```
+ #[inline]
+ pub fn replace(&self, val: T) -> T {
+ assert!(bql_locked());
+ // SAFETY: This can cause data races if called from multiple threads,
+ // but it won't happen as long as C code accesses the value
+ // under BQL protection only.
+ mem::replace(unsafe { &mut *self.value.get() }, val)
+ }
+
+ /// Unwraps the value, consuming the cell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ /// let five = c.into_inner();
+ ///
+ /// assert_eq!(five, 5);
+ /// ```
+ pub fn into_inner(self) -> T {
+ assert!(bql_locked());
+ self.value.into_inner()
+ }
+}
+
+impl<T: Copy> BqlCell<T> {
+ /// Returns a copy of the contained value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ ///
+ /// let five = c.get();
+ /// ```
+ #[inline]
+ pub fn get(&self) -> T {
+ assert!(bql_locked());
+ // SAFETY: This can cause data races if called from multiple threads,
+ // but it won't happen as long as C code accesses the value
+ // under BQL protection only.
+ unsafe { *self.value.get() }
+ }
+}
+
+impl<T> BqlCell<T> {
+ /// Returns a raw pointer to the underlying data in this cell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ ///
+ /// let ptr = c.as_ptr();
+ /// ```
+ #[inline]
+ pub const fn as_ptr(&self) -> *mut T {
+ self.value.get()
+ }
+}
+
+impl<T: Default> BqlCell<T> {
+ /// Takes the value of the cell, leaving `Default::default()` in its place.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ /// let five = c.take();
+ ///
+ /// assert_eq!(five, 5);
+ /// assert_eq!(c.into_inner(), 0);
+ /// ```
+ pub fn take(&self) -> T {
+ self.replace(Default::default())
+ }
+}
+
+/// A mutable memory location with dynamically checked borrow rules,
+/// protected by the Big QEMU Lock.
+///
+/// See the [module-level documentation](self) for more.
+///
+/// # Memory layout
+///
+/// `BqlRefCell<T>` starts with the same in-memory representation as its
+/// inner type `T`.
+#[repr(C)]
+pub struct BqlRefCell<T> {
+ // It is important that this is the first field (which is not the case
+ // for std::cell::BqlRefCell), so that we can use offset_of! on it.
+ // UnsafeCell and repr(C) both prevent usage of niches.
+ value: UnsafeCell<T>,
+ borrow: Cell<BorrowFlag>,
+ // Stores the location of the earliest currently active borrow.
+ // This gets updated whenever we go from having zero borrows
+ // to having a single borrow. When a borrow occurs, this gets included
+ // in the panic message
+ #[cfg(feature = "debug_cell")]
+ borrowed_at: Cell<Option<&'static std::panic::Location<'static>>>,
+}
+
+// Positive values represent the number of `BqlRef` active. Negative values
+// represent the number of `BqlRefMut` active. Right now QEMU's implementation
+// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping
+// components of a `BqlRefCell` (e.g., different ranges of a slice).
+//
+// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely
+// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of
+// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or
+// underflow. However, this is not a guarantee, as a pathological program could
+// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all
+// code must explicitly check for overflow and underflow in order to avoid
+// unsafety, or at least behave correctly in the event that overflow or
+// underflow happens (e.g., see BorrowRef::new).
+type BorrowFlag = isize;
+const UNUSED: BorrowFlag = 0;
+
+#[inline(always)]
+const fn is_writing(x: BorrowFlag) -> bool {
+ x < UNUSED
+}
+
+#[inline(always)]
+const fn is_reading(x: BorrowFlag) -> bool {
+ x > UNUSED
+}
+
+impl<T> BqlRefCell<T> {
+ /// Creates a new `BqlRefCell` containing `value`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlRefCell;
+ ///
+ /// let c = BqlRefCell::new(5);
+ /// ```
+ #[inline]
+ pub const fn new(value: T) -> BqlRefCell<T> {
+ BqlRefCell {
+ value: UnsafeCell::new(value),
+ borrow: Cell::new(UNUSED),
+ #[cfg(feature = "debug_cell")]
+ borrowed_at: Cell::new(None),
+ }
+ }
+}
+
+// This ensures the panicking code is outlined from `borrow_mut` for
+// `BqlRefCell`.
+#[inline(never)]
+#[cold]
+#[cfg(feature = "debug_cell")]
+fn panic_already_borrowed(source: &Cell<Option<&'static std::panic::Location<'static>>>) -> ! {
+ // If a borrow occurred, then we must already have an outstanding borrow,
+ // so `borrowed_at` will be `Some`
+ panic!("already borrowed at {:?}", source.take().unwrap())
+}
+
+#[inline(never)]
+#[cold]
+#[cfg(not(feature = "debug_cell"))]
+fn panic_already_borrowed() -> ! {
+ panic!("already borrowed")
+}
+
+impl<T> BqlRefCell<T> {
+ #[inline]
+ #[allow(clippy::unused_self)]
+ fn panic_already_borrowed(&self) -> ! {
+ #[cfg(feature = "debug_cell")]
+ {
+ panic_already_borrowed(&self.borrowed_at)
+ }
+ #[cfg(not(feature = "debug_cell"))]
+ {
+ panic_already_borrowed()
+ }
+ }
+
+ /// Immutably borrows the wrapped value.
+ ///
+ /// The borrow lasts until the returned `BqlRef` exits scope. Multiple
+ /// immutable borrows can be taken out at the same time.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the value is currently mutably borrowed.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlRefCell;
+ ///
+ /// let c = BqlRefCell::new(5);
+ ///
+ /// let borrowed_five = c.borrow();
+ /// let borrowed_five2 = c.borrow();
+ /// ```
+ ///
+ /// An example of panic:
+ ///
+ /// ```should_panic
+ /// use qemu_api::cell::BqlRefCell;
+ ///
+ /// let c = BqlRefCell::new(5);
+ ///
+ /// let m = c.borrow_mut();
+ /// let b = c.borrow(); // this causes a panic
+ /// ```
+ #[inline]
+ #[track_caller]
+ pub fn borrow(&self) -> BqlRef<'_, T> {
+ if let Some(b) = BorrowRef::new(&self.borrow) {
+ // `borrowed_at` is always the *first* active borrow
+ if b.borrow.get() == 1 {
+ #[cfg(feature = "debug_cell")]
+ self.borrowed_at.set(Some(std::panic::Location::caller()));
+ }
+
+ bql_block_unlock(true);
+
+ // SAFETY: `BorrowRef` ensures that there is only immutable access
+ // to the value while borrowed.
+ let value = unsafe { NonNull::new_unchecked(self.value.get()) };
+ BqlRef { value, borrow: b }
+ } else {
+ self.panic_already_borrowed()
+ }
+ }
+
+ /// Mutably borrows the wrapped value.
+ ///
+ /// The borrow lasts until the returned `BqlRefMut` or all `BqlRefMut`s
+ /// derived from it exit scope. The value cannot be borrowed while this
+ /// borrow is active.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the value is currently borrowed.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlRefCell;
+ ///
+ /// let c = BqlRefCell::new("hello".to_owned());
+ ///
+ /// *c.borrow_mut() = "bonjour".to_owned();
+ ///
+ /// assert_eq!(&*c.borrow(), "bonjour");
+ /// ```
+ ///
+ /// An example of panic:
+ ///
+ /// ```should_panic
+ /// use qemu_api::cell::BqlRefCell;
+ ///
+ /// let c = BqlRefCell::new(5);
+ /// let m = c.borrow();
+ ///
+ /// let b = c.borrow_mut(); // this causes a panic
+ /// ```
+ #[inline]
+ #[track_caller]
+ pub fn borrow_mut(&self) -> BqlRefMut<'_, T> {
+ if let Some(b) = BorrowRefMut::new(&self.borrow) {
+ #[cfg(feature = "debug_cell")]
+ {
+ self.borrowed_at.set(Some(std::panic::Location::caller()));
+ }
+
+ // SAFETY: this only adjusts a counter
+ bql_block_unlock(true);
+
+ // SAFETY: `BorrowRefMut` guarantees unique access.
+ let value = unsafe { NonNull::new_unchecked(self.value.get()) };
+ BqlRefMut {
+ value,
+ _borrow: b,
+ marker: PhantomData,
+ }
+ } else {
+ self.panic_already_borrowed()
+ }
+ }
+
+ /// Returns a raw pointer to the underlying data in this cell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlRefCell;
+ ///
+ /// let c = BqlRefCell::new(5);
+ ///
+ /// let ptr = c.as_ptr();
+ /// ```
+ #[inline]
+ pub const fn as_ptr(&self) -> *mut T {
+ self.value.get()
+ }
+}
+
+// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is
+// stored out-of-line. Even though BqlRefCell includes Cells, they are
+// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU
+// Lock cannot be released while any borrows is active.
+unsafe impl<T> Send for BqlRefCell<T> where T: Send {}
+unsafe impl<T> Sync for BqlRefCell<T> {}
+
+impl<T: Clone> Clone for BqlRefCell<T> {
+ /// # Panics
+ ///
+ /// Panics if the value is currently mutably borrowed.
+ #[inline]
+ #[track_caller]
+ fn clone(&self) -> BqlRefCell<T> {
+ BqlRefCell::new(self.borrow().clone())
+ }
+
+ /// # Panics
+ ///
+ /// Panics if `source` is currently mutably borrowed.
+ #[inline]
+ #[track_caller]
+ fn clone_from(&mut self, source: &Self) {
+ self.value.get_mut().clone_from(&source.borrow())
+ }
+}
+
+impl<T: Default> Default for BqlRefCell<T> {
+ /// Creates a `BqlRefCell<T>`, with the `Default` value for T.
+ #[inline]
+ fn default() -> BqlRefCell<T> {
+ BqlRefCell::new(Default::default())
+ }
+}
+
+impl<T: PartialEq> PartialEq for BqlRefCell<T> {
+ /// # Panics
+ ///
+ /// Panics if the value in either `BqlRefCell` is currently mutably
+ /// borrowed.
+ #[inline]
+ fn eq(&self, other: &BqlRefCell<T>) -> bool {
+ *self.borrow() == *other.borrow()
+ }
+}
+
+impl<T: Eq> Eq for BqlRefCell<T> {}
+
+impl<T: PartialOrd> PartialOrd for BqlRefCell<T> {
+ /// # Panics
+ ///
+ /// Panics if the value in either `BqlRefCell` is currently mutably
+ /// borrowed.
+ #[inline]
+ fn partial_cmp(&self, other: &BqlRefCell<T>) -> Option<Ordering> {
+ self.borrow().partial_cmp(&*other.borrow())
+ }
+}
+
+impl<T: Ord> Ord for BqlRefCell<T> {
+ /// # Panics
+ ///
+ /// Panics if the value in either `BqlRefCell` is currently mutably
+ /// borrowed.
+ #[inline]
+ fn cmp(&self, other: &BqlRefCell<T>) -> Ordering {
+ self.borrow().cmp(&*other.borrow())
+ }
+}
+
+impl<T> From<T> for BqlRefCell<T> {
+ /// Creates a new `BqlRefCell<T>` containing the given value.
+ fn from(t: T) -> BqlRefCell<T> {
+ BqlRefCell::new(t)
+ }
+}
+
+struct BorrowRef<'b> {
+ borrow: &'b Cell<BorrowFlag>,
+}
+
+impl<'b> BorrowRef<'b> {
+ #[inline]
+ fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRef<'b>> {
+ let b = borrow.get().wrapping_add(1);
+ if !is_reading(b) {
+ // Incrementing borrow can result in a non-reading value (<= 0) in these cases:
+ // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read
+ // borrow due to Rust's reference aliasing rules
+ // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed
+ // into isize::MIN (the max amount of writing borrows) so we can't allow an
+ // additional read borrow because isize can't represent so many read borrows
+ // (this can only happen if you mem::forget more than a small constant amount
+ // of `BqlRef`s, which is not good practice)
+ None
+ } else {
+ // Incrementing borrow can result in a reading value (> 0) in these cases:
+ // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read
+ // borrow
+ // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is
+ // large enough to represent having one more read borrow
+ borrow.set(b);
+ Some(BorrowRef { borrow })
+ }
+ }
+}
+
+impl Drop for BorrowRef<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ let borrow = self.borrow.get();
+ debug_assert!(is_reading(borrow));
+ self.borrow.set(borrow - 1);
+ bql_block_unlock(false)
+ }
+}
+
+impl Clone for BorrowRef<'_> {
+ #[inline]
+ fn clone(&self) -> Self {
+ BorrowRef::new(self.borrow).unwrap()
+ }
+}
+
+/// Wraps a borrowed reference to a value in a `BqlRefCell` box.
+/// A wrapper type for an immutably borrowed value from a `BqlRefCell<T>`.
+///
+/// See the [module-level documentation](self) for more.
+pub struct BqlRef<'b, T: 'b> {
+ // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a
+ // `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops.
+ // `NonNull` is also covariant over `T`, just like we would have with `&T`.
+ value: NonNull<T>,
+ borrow: BorrowRef<'b>,
+}
+
+impl<T> Deref for BqlRef<'_, T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &T {
+ // SAFETY: the value is accessible as long as we hold our borrow.
+ unsafe { self.value.as_ref() }
+ }
+}
+
+impl<'b, T> BqlRef<'b, T> {
+ /// Copies a `BqlRef`.
+ ///
+ /// The `BqlRefCell` is already immutably borrowed, so this cannot fail.
+ ///
+ /// This is an associated function that needs to be used as
+ /// `BqlRef::clone(...)`. A `Clone` implementation or a method would
+ /// interfere with the widespread use of `r.borrow().clone()` to clone
+ /// the contents of a `BqlRefCell`.
+ #[must_use]
+ #[inline]
+ #[allow(clippy::should_implement_trait)]
+ pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> {
+ BqlRef {
+ value: orig.value,
+ borrow: orig.borrow.clone(),
+ }
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for BqlRef<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
+impl<T: fmt::Display> fmt::Display for BqlRef<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
+struct BorrowRefMut<'b> {
+ borrow: &'b Cell<BorrowFlag>,
+}
+
+impl<'b> BorrowRefMut<'b> {
+ #[inline]
+ fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRefMut<'b>> {
+ // There must currently be no existing references when borrow_mut() is
+ // called, so we explicitly only allow going from UNUSED to UNUSED - 1.
+ match borrow.get() {
+ UNUSED => {
+ borrow.set(UNUSED - 1);
+ Some(BorrowRefMut { borrow })
+ }
+ _ => None,
+ }
+ }
+}
+
+impl Drop for BorrowRefMut<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ let borrow = self.borrow.get();
+ debug_assert!(is_writing(borrow));
+ self.borrow.set(borrow + 1);
+ bql_block_unlock(false)
+ }
+}
+
+/// A wrapper type for a mutably borrowed value from a `BqlRefCell<T>`.
+///
+/// See the [module-level documentation](self) for more.
+pub struct BqlRefMut<'b, T: 'b> {
+ // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a
+ // `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops.
+ value: NonNull<T>,
+ _borrow: BorrowRefMut<'b>,
+ // `NonNull` is covariant over `T`, so we need to reintroduce invariance.
+ marker: PhantomData<&'b mut T>,
+}
+
+impl<T> Deref for BqlRefMut<'_, T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &T {
+ // SAFETY: the value is accessible as long as we hold our borrow.
+ unsafe { self.value.as_ref() }
+ }
+}
+
+impl<T> DerefMut for BqlRefMut<'_, T> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut T {
+ // SAFETY: the value is accessible as long as we hold our borrow.
+ unsafe { self.value.as_mut() }
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for BqlRefMut<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
+impl<T: fmt::Display> fmt::Display for BqlRefMut<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 2659793..df91a2e 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -8,20 +8,122 @@ use std::{ffi::CStr, os::raw::c_void};
use crate::bindings::{Object, ObjectClass, TypeInfo};
-/// Trait a type must implement to be registered with QEMU.
-pub trait ObjectImpl {
+unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
+ // 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 extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) {
+ // 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>() })
+}
+
+/// Trait exposed by all structs corresponding to QOM objects.
+///
+/// # Safety
+///
+/// For classes declared in C:
+///
+/// - `Class` and `TYPE` must match the data in the `TypeInfo`;
+///
+/// - the first field of the struct must be of the instance type corresponding
+/// to the superclass, as declared in the `TypeInfo`
+///
+/// - likewise, the first field of the `Class` struct must be of the class type
+/// corresponding to the superclass
+///
+/// For classes declared in Rust and implementing [`ObjectImpl`]:
+///
+/// - 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`
+///
+/// - likewise, the first field of the `Class` must be of the class struct
+/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`.
+pub unsafe trait ObjectType: Sized {
+ /// The QOM class object corresponding to this struct. Not used yet.
type Class;
- const TYPE_INFO: TypeInfo;
+
+ /// The name of the type, which can be passed to `object_new()` to
+ /// generate an instance of this type.
const TYPE_NAME: &'static CStr;
- const PARENT_TYPE_NAME: Option<&'static CStr>;
- const ABSTRACT: bool;
- const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)>;
- const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)>;
- const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)>;
}
-pub trait Class {
+/// Trait a type must implement to be registered with QEMU.
+pub trait ObjectImpl: ObjectType + ClassInitImpl {
+ /// The parent of the type. This should match the first field of
+ /// the struct that implements `ObjectImpl`:
+ 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
+ /// initializing its own members.
+ ///
+ /// FIXME: The argument is not really a valid reference. `&mut
+ /// MaybeUninit<Self>` would be a better description.
+ const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = None;
+
+ /// 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 TYPE_INFO: TypeInfo = TypeInfo {
+ name: Self::TYPE_NAME.as_ptr(),
+ parent: Self::ParentType::TYPE_NAME.as_ptr(),
+ instance_size: core::mem::size_of::<Self>(),
+ instance_align: core::mem::align_of::<Self>(),
+ instance_init: match Self::INSTANCE_INIT {
+ None => None,
+ Some(_) => Some(rust_instance_init::<Self>),
+ },
+ instance_post_init: match Self::INSTANCE_POST_INIT {
+ None => None,
+ Some(_) => Some(rust_instance_post_init::<Self>),
+ },
+ instance_finalize: Self::INSTANCE_FINALIZE,
+ abstract_: Self::ABSTRACT,
+ class_size: core::mem::size_of::<Self::Class>(),
+ class_init: <Self as ClassInitImpl>::CLASS_INIT,
+ class_base_init: <Self as ClassInitImpl>::CLASS_BASE_INIT,
+ class_data: core::ptr::null_mut(),
+ interfaces: core::ptr::null_mut(),
+ };
+}
+
+/// Trait used to fill in a class struct.
+///
+/// Each QOM class that has virtual methods describes them in a
+/// _class struct_. Class structs include a parent field corresponding
+/// to the vtable of the parent class, all the way up to [`ObjectClass`].
+/// Each QOM type has one such class struct.
+///
+/// The Rust implementation of methods will usually come from a trait
+/// like [`ObjectImpl`] or [`DeviceImpl`](crate::device_class::DeviceImpl).
+pub trait ClassInitImpl {
+ /// Function that is called after all parent class initialization
+ /// has occurred. On entry, the virtual method pointers are set to
+ /// the default values coming from the parent classes; the function
+ /// can change them to override virtual methods of a parent class.
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)>;
+
+ /// Called on descendent classes after all parent class initialization
+ /// has occurred, but before the class itself is initialized. This
+ /// is only useful if a class is not a leaf, and can be used to undo
+ /// the effects of copying the contents of the parent's class struct
+ /// to the descendants.
const CLASS_BASE_INIT: Option<
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
>;
@@ -64,28 +166,3 @@ macro_rules! module_init {
}
};
}
-
-#[macro_export]
-macro_rules! type_info {
- ($t:ty) => {
- $crate::bindings::TypeInfo {
- name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(),
- parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME {
- pname.as_ptr()
- } else {
- ::core::ptr::null_mut()
- },
- instance_size: ::core::mem::size_of::<$t>(),
- instance_align: ::core::mem::align_of::<$t>(),
- instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
- instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
- instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
- abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
- class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>(),
- class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT,
- class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT,
- class_data: ::core::ptr::null_mut(),
- interfaces: ::core::ptr::null_mut(),
- };
- }
-}
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
index 0ba798d..03d03fe 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/device_class.rs
@@ -2,32 +2,112 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::ffi::CStr;
+use std::{ffi::CStr, os::raw::c_void};
-use crate::bindings;
+use crate::{
+ bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
+ prelude::*,
+ zeroable::Zeroable,
+};
+
+/// Trait providing the contents of [`DeviceClass`].
+pub trait DeviceImpl {
+ /// _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;
+
+ /// 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.
+ fn properties() -> &'static [Property] {
+ &[Zeroable::ZERO; 1]
+ }
+
+ /// A `VMStateDescription` providing the migration format for the device
+ /// Not a `const` because referencing statics in constants is unstable
+ /// until Rust 1.83.0.
+ fn vmsd() -> Option<&'static VMStateDescription> {
+ None
+ }
+}
+
+/// # Safety
+///
+/// This function is only called through the QOM machinery and
+/// the `impl_device_class!` macro.
+/// 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_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 });
+}
+
+/// # 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 });
+}
+
+/// # Safety
+///
+/// We expect the FFI user of this function to pass a valid pointer that
+/// can be downcasted to type `DeviceClass`, because `T` implements
+/// `DeviceImpl`.
+pub unsafe extern "C" fn rust_device_class_init<T: DeviceImpl>(
+ klass: *mut ObjectClass,
+ _: *mut c_void,
+) {
+ let mut dc = ::core::ptr::NonNull::new(klass.cast::<DeviceClass>()).unwrap();
+ unsafe {
+ let dc = dc.as_mut();
+ if <T as DeviceImpl>::REALIZE.is_some() {
+ dc.realize = Some(rust_realize_fn::<T>);
+ }
+ if <T as DeviceImpl>::RESET.is_some() {
+ bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::<T>));
+ }
+ if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
+ dc.vmsd = vmsd;
+ }
+ bindings::device_class_set_props(dc, <T as DeviceImpl>::properties().as_ptr());
+ }
+}
#[macro_export]
-macro_rules! device_class_init {
- ($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => {
- pub unsafe extern "C" fn $func(
- klass: *mut $crate::bindings::ObjectClass,
- _: *mut ::std::os::raw::c_void,
- ) {
- let mut dc =
- ::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
- unsafe {
- dc.as_mut().realize = $realize_fn;
- dc.as_mut().vmsd = &$vmsd;
- $crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn);
- $crate::bindings::device_class_set_props(dc.as_mut(), $props.as_ptr());
- }
+macro_rules! impl_device_class {
+ ($type:ty) => {
+ impl $crate::definitions::ClassInitImpl for $type {
+ const CLASS_INIT: Option<
+ unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void),
+ > = Some($crate::device_class::rust_device_class_init::<$type>);
+ const CLASS_BASE_INIT: Option<
+ unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void),
+ > = None;
}
};
}
#[macro_export]
macro_rules! define_property {
- ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
+ ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => {
$crate::bindings::Property {
// use associated function syntax for type checking
name: ::std::ffi::CStr::as_ptr($name),
@@ -38,7 +118,7 @@ macro_rules! define_property {
..$crate::zeroable::Zeroable::ZERO
}
};
- ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr$(,)*) => {
+ ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => {
$crate::bindings::Property {
// use associated function syntax for type checking
name: ::std::ffi::CStr::as_ptr($name),
@@ -67,8 +147,8 @@ macro_rules! declare_properties {
};
}
-// workaround until we can use --generate-cstr in bindgen.
-pub const TYPE_DEVICE: &CStr =
- unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
-pub const TYPE_SYS_BUS_DEVICE: &CStr =
- unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
+unsafe impl ObjectType for bindings::DeviceState {
+ type Class = bindings::DeviceClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
+}
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
new file mode 100644
index 0000000..6258141
--- /dev/null
+++ b/rust/qemu-api/src/irq.rs
@@ -0,0 +1,91 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Bindings for interrupt sources
+
+use core::ptr;
+use std::{marker::PhantomData, os::raw::c_int};
+
+use crate::{
+ bindings::{qemu_set_irq, IRQState},
+ prelude::*,
+};
+
+/// Interrupt sources are used by devices to pass changes to a value (typically
+/// a boolean). The interrupt sink is usually an interrupt controller or
+/// GPIO controller.
+///
+/// As far as devices are concerned, interrupt sources are always active-high:
+/// for example, `InterruptSource<bool>`'s [`raise`](InterruptSource::raise)
+/// 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.
+///
+/// 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
+/// 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
+/// devices are generally shared objects, interrupt sources are an example of
+/// the interior mutability pattern.
+///
+/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell`
+/// allows access from whatever thread has it.
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct InterruptSource<T = bool>
+where
+ c_int: From<T>,
+{
+ cell: BqlCell<*mut IRQState>,
+ _marker: PhantomData<T>,
+}
+
+impl InterruptSource<bool> {
+ /// Send a low (`false`) value to the interrupt sink.
+ pub fn lower(&self) {
+ self.set(false);
+ }
+
+ /// Send a high-low pulse to the interrupt sink.
+ pub fn pulse(&self) {
+ self.set(true);
+ self.set(false);
+ }
+
+ /// Send a high (`true`) value to the interrupt sink.
+ pub fn raise(&self) {
+ self.set(true);
+ }
+}
+
+impl<T> InterruptSource<T>
+where
+ c_int: From<T>,
+{
+ /// Send `level` to the interrupt sink.
+ pub fn set(&self, level: T) {
+ let ptr = self.cell.get();
+ // SAFETY: the pointer is retrieved under the BQL and remains valid
+ // until the BQL is released, which is after qemu_set_irq() is entered.
+ unsafe {
+ qemu_set_irq(ptr, level.into());
+ }
+ }
+
+ pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState {
+ self.cell.as_ptr()
+ }
+}
+
+impl Default for InterruptSource {
+ fn default() -> Self {
+ InterruptSource {
+ cell: BqlCell::new(ptr::null_mut()),
+ _marker: PhantomData,
+ }
+ }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index aa8d16e..9e007e1 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -4,35 +4,22 @@
#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
-#[allow(
- dead_code,
- improper_ctypes_definitions,
- improper_ctypes,
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- unsafe_op_in_unsafe_fn,
- clippy::missing_const_for_fn,
- clippy::too_many_arguments,
- clippy::approx_constant,
- clippy::use_self,
- clippy::useless_transmute,
- clippy::missing_safety_doc,
-)]
#[rustfmt::skip]
pub mod bindings;
-unsafe impl Send for bindings::Property {}
-unsafe impl Sync for bindings::Property {}
-unsafe impl Sync for bindings::TypeInfo {}
-unsafe impl Sync for bindings::VMStateDescription {}
-unsafe impl Sync for bindings::VMStateField {}
-unsafe impl Sync for bindings::VMStateInfo {}
+// preserve one-item-per-"use" syntax, it is clearer
+// for prelude-like modules
+#[rustfmt::skip]
+pub mod prelude;
+pub mod bitops;
pub mod c_str;
+pub mod cell;
pub mod definitions;
pub mod device_class;
+pub mod irq;
pub mod offset_of;
+pub mod sysbus;
pub mod vmstate;
pub mod zeroable;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
new file mode 100644
index 0000000..1b8677b
--- /dev/null
+++ b/rust/qemu-api/src/prelude.rs
@@ -0,0 +1,10 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub use crate::bitops::IntegerExt;
+
+pub use crate::cell::BqlCell;
+pub use crate::cell::BqlRefCell;
+
+pub use crate::definitions::ObjectType;
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
new file mode 100644
index 0000000..5ee0685
--- /dev/null
+++ b/rust/qemu-api/src/sysbus.rs
@@ -0,0 +1,33 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::{ffi::CStr, ptr::addr_of};
+
+pub use bindings::{SysBusDevice, SysBusDeviceClass};
+
+use crate::{bindings, cell::bql_locked, irq::InterruptSource, prelude::*};
+
+unsafe impl ObjectType for SysBusDevice {
+ type Class = SysBusDeviceClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
+}
+
+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 _
+ }
+
+ /// 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) {
+ assert!(bql_locked());
+ unsafe {
+ bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
+ }
+ }
+}
diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs
index 13cdb2c..6125aee 100644
--- a/rust/qemu-api/src/zeroable.rs
+++ b/rust/qemu-api/src/zeroable.rs
@@ -7,9 +7,9 @@ use std::ptr;
/// behavior. This trait in principle could be implemented as just:
///
/// ```
-/// const ZERO: Self = unsafe {
-/// ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init()
-/// },
+/// pub unsafe trait Zeroable: Default {
+/// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
+/// }
/// ```
///
/// The need for a manual implementation is only because `zeroed()` cannot
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 43a4827..278efe9 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -2,14 +2,11 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{ffi::CStr, os::raw::c_void};
+use std::ffi::CStr;
use qemu_api::{
- bindings::*,
- c_str, declare_properties, define_property,
- definitions::{Class, ObjectImpl},
- device_class, device_class_init,
- zeroable::Zeroable,
+ bindings::*, c_str, declare_properties, define_property, definitions::ObjectImpl,
+ device_class::DeviceImpl, impl_device_class, prelude::*, zeroable::Zeroable,
};
#[test]
@@ -45,35 +42,29 @@ fn test_device_decl_macros() {
),
}
- device_class_init! {
- dummy_class_init,
- props => DUMMY_PROPERTIES,
- realize_fn => None,
- legacy_reset_fn => None,
- vmsd => VMSTATE,
+ unsafe impl ObjectType for DummyState {
+ type Class = DummyClass;
+ const TYPE_NAME: &'static CStr = c_str!("dummy");
}
impl ObjectImpl for DummyState {
- type Class = DummyClass;
- const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
- const TYPE_NAME: &'static CStr = c_str!("dummy");
- const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
+ type ParentType = DeviceState;
const ABSTRACT: bool = false;
- const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
- const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
- const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
}
- impl Class for DummyClass {
- const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
- Some(dummy_class_init);
- const CLASS_BASE_INIT: Option<
- unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
- > = None;
+ impl DeviceImpl for DummyState {
+ fn properties() -> &'static [Property] {
+ &DUMMY_PROPERTIES
+ }
+ fn vmsd() -> Option<&'static VMStateDescription> {
+ Some(&VMSTATE)
+ }
}
+ impl_device_class!(DummyState);
+
unsafe {
module_call_init(module_init_type::MODULE_INIT_QOM);
- object_unref(object_new(DummyState::TYPE_NAME.as_ptr()) as *mut _);
+ object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
}
}