* [PATCH 01/26] bql: check that the BQL is not dropped within marked sections
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-09 12:36 ` [PATCH 02/26] rust: cell: add BQL-enforcing Cell variant Paolo Bonzini
` (25 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
The Big QEMU Lock (BQL) is used to provide interior mutability to Rust
code. While BqlCell performs indivisible accesses, an equivalent of
RefCell will allow the borrower to hold to the interior content for a
long time. If the BQL is dropped, another thread could come and mutate
the data from C code (Rust code would panic on borrow_mut() instead).
In order to prevent this, add a new BQL primitive that can mark
BQL-atomic sections and aborts if the BQL is dropped within them.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/qemu/main-loop.h | 15 +++++++++++++++
stubs/iothread-lock.c | 15 +++++++++++++++
system/cpus.c | 15 +++++++++++++++
3 files changed, 45 insertions(+)
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index 5764db157c9..646306c272f 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -262,6 +262,21 @@ AioContext *iohandler_get_aio_context(void);
*/
bool bql_locked(void);
+/**
+ * bql_block: Allow/deny releasing the BQL
+ *
+ * The Big QEMU Lock (BQL) is used to provide interior mutability to
+ * Rust code, but this only works if other threads cannot run while
+ * the Rust code has an active borrow. This is because C code in
+ * other threads could come in and mutate data under the Rust code's
+ * feet.
+ *
+ * @increase: Whether to increase or decrease the blocking counter.
+ * Releasing the BQL while the counter is nonzero triggers
+ * an assertion failure.
+ */
+void bql_block_unlock(bool increase);
+
/**
* qemu_in_main_thread: return whether it's possible to safely access
* the global state of the block layer.
diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c
index d7890e5581c..54676598950 100644
--- a/stubs/iothread-lock.c
+++ b/stubs/iothread-lock.c
@@ -1,6 +1,8 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
+static uint32_t bql_unlock_blocked;
+
bool bql_locked(void)
{
return false;
@@ -12,4 +14,17 @@ void bql_lock_impl(const char *file, int line)
void bql_unlock(void)
{
+ assert(!bql_unlock_blocked);
+}
+
+void bql_block_unlock(bool increase)
+{
+ uint32_t new_value;
+
+ assert(bql_locked());
+
+ /* check for overflow! */
+ new_value = bql_unlock_blocked + increase - !increase;
+ assert((new_value > bql_unlock_blocked) == increase);
+ bql_unlock_blocked = new_value;
}
diff --git a/system/cpus.c b/system/cpus.c
index 1c818ff6828..ba633c7688b 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -514,6 +514,20 @@ bool qemu_in_vcpu_thread(void)
QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked)
+static uint32_t bql_unlock_blocked;
+
+void bql_block_unlock(bool increase)
+{
+ uint32_t new_value;
+
+ assert(bql_locked());
+
+ /* check for overflow! */
+ new_value = bql_unlock_blocked + increase - !increase;
+ assert((new_value > bql_unlock_blocked) == increase);
+ bql_unlock_blocked = new_value;
+}
+
bool bql_locked(void)
{
return get_bql_locked();
@@ -540,6 +554,7 @@ void bql_lock_impl(const char *file, int line)
void bql_unlock(void)
{
g_assert(bql_locked());
+ g_assert(!bql_unlock_blocked);
set_bql_locked(false);
qemu_mutex_unlock(&bql);
}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* [PATCH 02/26] rust: cell: add BQL-enforcing Cell variant
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
2024-12-09 12:36 ` [PATCH 01/26] bql: check that the BQL is not dropped within marked sections Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-09 12:36 ` [PATCH 03/26] rust: cell: add BQL-enforcing RefCell variant Paolo Bonzini
` (24 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
QEMU objects usually have their pointer shared with the "outside
world" very early in their lifetime, for example when they create their
MemoryRegions. Because at this point it is not valid anymore to
create a &mut reference to the device, individual parts of the
device struct must be made mutable in a controlled manner.
QEMU's Big Lock (BQL) effectively turns multi-threaded code into
single-threaded code while device code runs, as long as the BQL is not
released while the device is borrowed (because C code could sneak in and
mutate the device). We can then introduce custom interior mutability primitives
that are semantically similar to the standard library's (single-threaded)
Cell and RefCell, but account for QEMU's threading model. Accessing
the "BqlCell" or borrowing the "BqlRefCell" requires proving that the
BQL is held, and attempting to access without the BQL is a runtime panic,
similar to RefCell's already-borrowed panic.
With respect to naming I also considered omitting the "Bql" prefix or
moving it to the module, e.g. qemu_api::bql::{Cell, RefCell}. However,
this could easily lead to mistakes and confusion; for example rustc could
suggest the wrong import, leading to subtle bugs.
As a start introduce the an equivalent of Cell. Almost all of the code
was taken from Rust's standard library, while removing unstable features
and probably-unnecessary functionality that constitute a large of the
original code. A lot of what's left is documentation, as well as unit
tests in the form of doctests. These are not yet integrated in "make
check" but can be run with "cargo test --doc".
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/meson.build | 1 +
rust/qemu-api/src/cell.rs | 298 ++++++++++++++++++++++++++++++++++++++
rust/qemu-api/src/lib.rs | 1 +
3 files changed, 300 insertions(+)
create mode 100644 rust/qemu-api/src/cell.rs
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index d719c13f46d..edc21e1a3f8 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -13,6 +13,7 @@ _qemu_api_rs = static_library(
[
'src/lib.rs',
'src/bindings.rs',
+ 'src/cell.rs',
'src/c_str.rs',
'src/definitions.rs',
'src/device_class.rs',
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
new file mode 100644
index 00000000000..2e4ea8d590d
--- /dev/null
+++ b/rust/qemu-api/src/cell.rs
@@ -0,0 +1,298 @@
+// 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.
+//!
+//! This module provides a way to do so via the Big QEMU Lock. While
+//! [`BqlCell<T>`] is essentially the same single-threaded primitive that is
+//! available in `std::cell`, the BQL allows it to be used from a multi-threaded
+//! context and to share references across threads, while maintaining Rust's
+//! safety guarantees. For this reason, unlike its `std::cell` counterpart,
+//! `BqlCell` implements 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.
+//!
+//! Warning: While `BqlCell` is similar to its `std::cell` counterpart, the two
+//! 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.
+
+use std::{cell::UnsafeCell, cmp::Ordering, fmt, mem};
+
+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() }
+}
+
+/// 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())
+ }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 440aff3817d..b04d110b3f5 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -8,6 +8,7 @@
pub mod bindings;
pub mod c_str;
+pub mod cell;
pub mod definitions;
pub mod device_class;
pub mod offset_of;
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* [PATCH 03/26] rust: cell: add BQL-enforcing RefCell variant
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
2024-12-09 12:36 ` [PATCH 01/26] bql: check that the BQL is not dropped within marked sections Paolo Bonzini
2024-12-09 12:36 ` [PATCH 02/26] rust: cell: add BQL-enforcing Cell variant Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-09 12:36 ` [PATCH 04/26] rust: define prelude Paolo Bonzini
` (23 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Similar to the existing BqlCell, introduce a custom interior mutability
primitive that resembles RefCell but accounts for QEMU's threading model.
Borrowing the RefCell requires proving that the BQL is held, and
attempting to access without the BQL is a runtime panic.
Almost all of the code was taken from Rust's standard library, while
removing unstable features and probably-unnecessary functionality that
amounts to 60% of the original code. A lot of what's left is documentation,
as well as unit tests in the form of doctests. These are not yet integrated
in "make check" but can be run with "cargo test --doc".
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/Cargo.toml | 3 +-
rust/qemu-api/meson.build | 3 +
rust/qemu-api/src/cell.rs | 544 +++++++++++++++++++++++++++++++++++++-
3 files changed, 539 insertions(+), 11 deletions(-)
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 669f288d1cb..4aa22f31986 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -20,8 +20,9 @@ qemu_api_macros = { path = "../qemu-api-macros" }
version_check = "~0.9"
[features]
-default = []
+default = ["debug_cell"]
allocator = []
+debug_cell = []
[lints]
workspace = true
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index edc21e1a3f8..cacb112c5c3 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -6,6 +6,9 @@ _qemu_api_cfg = run_command(rustc_args,
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',
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index 2e4ea8d590d..28349de291a 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -46,20 +46,30 @@
//! parts of a device must be made mutable in a controlled manner through the
//! use of cell types.
//!
-//! This module provides a way to do so via the Big QEMU Lock. While
-//! [`BqlCell<T>`] is essentially the same single-threaded primitive that is
-//! available in `std::cell`, the BQL allows it to be used from a multi-threaded
-//! context and to share references across threads, while maintaining Rust's
-//! safety guarantees. For this reason, unlike its `std::cell` counterpart,
-//! `BqlCell` implements the `Sync` trait.
+//! [`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.
//!
-//! Warning: While `BqlCell` is similar to its `std::cell` counterpart, the two
-//! are not interchangeable. Using `std::cell` types in QEMU device
-//! implementations is usually incorrect and can lead to thread-safety issues.
+//! 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>`
//!
@@ -80,8 +90,37 @@
//! 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::UnsafeCell, cmp::Ordering, fmt, mem};
+use std::{
+ cell::{Cell, UnsafeCell},
+ cmp::Ordering,
+ fmt,
+ marker::PhantomData,
+ mem,
+ ops::{Deref, DerefMut},
+ ptr::NonNull,
+};
use crate::bindings;
@@ -93,6 +132,15 @@ pub fn bql_locked() -> 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
@@ -296,3 +344,479 @@ 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)
+ }
+}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* [PATCH 04/26] rust: define prelude
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (2 preceding siblings ...)
2024-12-09 12:36 ` [PATCH 03/26] rust: cell: add BQL-enforcing RefCell variant Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-10 7:19 ` Zhao Liu
2024-12-09 12:36 ` [PATCH 05/26] rust: add bindings for interrupt sources Paolo Bonzini
` (22 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Add a module that will contain frequently used traits and
occasionally structs. They can be included quickly with
"use qemu_api::prelude::*".
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/meson.build | 1 +
rust/qemu-api/src/lib.rs | 5 +++++
rust/qemu-api/src/prelude.rs | 6 ++++++
3 files changed, 12 insertions(+)
create mode 100644 rust/qemu-api/src/prelude.rs
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index cacb112c5c3..f8b4cd39a26 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -21,6 +21,7 @@ _qemu_api_rs = static_library(
'src/definitions.rs',
'src/device_class.rs',
'src/offset_of.rs',
+ 'src/prelude.rs',
'src/vmstate.rs',
'src/zeroable.rs',
],
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index b04d110b3f5..e5956cd5eb6 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -7,6 +7,11 @@
#[rustfmt::skip]
pub mod bindings;
+// preserve one-item-per-"use" syntax, it is clearer
+// for prelude-like modules
+#[rustfmt::skip]
+pub mod prelude;
+
pub mod c_str;
pub mod cell;
pub mod definitions;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
new file mode 100644
index 00000000000..dfaddbd062a
--- /dev/null
+++ b/rust/qemu-api/src/prelude.rs
@@ -0,0 +1,6 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub use crate::cell::BqlCell;
+pub use crate::cell::BqlRefCell;
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 04/26] rust: define prelude
2024-12-09 12:36 ` [PATCH 04/26] rust: define prelude Paolo Bonzini
@ 2024-12-10 7:19 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 7:19 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:36:55PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:36:55 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 04/26] rust: define prelude
> X-Mailer: git-send-email 2.47.1
>
> Add a module that will contain frequently used traits and
> occasionally structs. They can be included quickly with
> "use qemu_api::prelude::*".
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/qemu-api/meson.build | 1 +
> rust/qemu-api/src/lib.rs | 5 +++++
> rust/qemu-api/src/prelude.rs | 6 ++++++
> 3 files changed, 12 insertions(+)
> create mode 100644 rust/qemu-api/src/prelude.rs
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 05/26] rust: add bindings for interrupt sources
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (3 preceding siblings ...)
2024-12-09 12:36 ` [PATCH 04/26] rust: define prelude Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-10 7:25 ` Zhao Liu
2024-12-09 12:36 ` [PATCH 06/26] rust: add a bit operation module Paolo Bonzini
` (21 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
The InterruptSource bindings let us call qemu_set_irq() and sysbus_init_irq()
as safe code.
Interrupt sources, qemu_irq in C code, are pointers to IRQState objects.
They are QOM link properties and can be written to outside the control
of the device (i.e. from a shared reference); therefore they must be
interior-mutable in Rust. Since thread-safety is provided by the BQL,
what we want here is the newly-introduced BqlCell. A pointer to the
contents of the BqlCell (an IRQState**, or equivalently qemu_irq*)
is then passed to the C sysbus_init_irq function.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 22 ++++----
rust/qemu-api/meson.build | 2 +
rust/qemu-api/src/irq.rs | 91 ++++++++++++++++++++++++++++++++
rust/qemu-api/src/lib.rs | 2 +
rust/qemu-api/src/sysbus.rs | 27 ++++++++++
5 files changed, 134 insertions(+), 10 deletions(-)
create mode 100644 rust/qemu-api/src/irq.rs
create mode 100644 rust/qemu-api/src/sysbus.rs
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 317a9b3c5ad..c5c8c463d37 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -13,6 +13,7 @@
c_str,
definitions::ObjectImpl,
device_class::TYPE_SYS_BUS_DEVICE,
+ irq::InterruptSource,
};
use crate::{
@@ -94,7 +95,7 @@ pub struct PL011State {
/// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
/// ```
#[doc(alias = "irq")]
- pub interrupts: [qemu_irq; 6usize],
+ pub interrupts: [InterruptSource; IRQMASK.len()],
#[doc(alias = "clk")]
pub clock: NonNull<Clock>,
#[doc(alias = "migrate_clk")]
@@ -139,7 +140,8 @@ impl PL011State {
unsafe fn init(&mut self) {
const CLK_NAME: &CStr = c_str!("clk");
- let dev = addr_of_mut!(*self).cast::<DeviceState>();
+ let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::<SysBusDevice>()) };
+
// SAFETY:
//
// self and self.iomem are guaranteed to be valid at this point since callers
@@ -153,12 +155,15 @@ unsafe fn init(&mut self) {
Self::TYPE_INFO.name,
0x1000,
);
- let sbd = addr_of_mut!(*self).cast::<SysBusDevice>();
sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
- for irq in self.interrupts.iter_mut() {
- sysbus_init_irq(sbd, irq);
- }
}
+
+ for irq in self.interrupts.iter() {
+ sbd.init_irq(irq);
+ }
+
+ let dev = addr_of_mut!(*self).cast::<DeviceState>();
+
// SAFETY:
//
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
@@ -498,10 +503,7 @@ pub fn put_fifo(&mut self, value: c_uint) {
pub fn update(&self) {
let flags = self.int_level & self.int_enabled;
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
- // SAFETY: self.interrupts have been initialized in init().
- unsafe {
- qemu_set_irq(*irq, i32::from(flags & i != 0));
- }
+ irq.set(flags & i != 0);
}
}
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index f8b4cd39a26..b927eb58c8e 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -20,8 +20,10 @@ _qemu_api_rs = static_library(
'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/irq.rs b/rust/qemu-api/src/irq.rs
new file mode 100644
index 00000000000..6258141bdf0
--- /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 e5956cd5eb6..0efbef47441 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -16,7 +16,9 @@
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/sysbus.rs b/rust/qemu-api/src/sysbus.rs
new file mode 100644
index 00000000000..4e192c75898
--- /dev/null
+++ b/rust/qemu-api/src/sysbus.rs
@@ -0,0 +1,27 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::ptr::addr_of;
+
+pub use bindings::{SysBusDevice, SysBusDeviceClass};
+
+use crate::{bindings, cell::bql_locked, irq::InterruptSource};
+
+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());
+ }
+ }
+}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 05/26] rust: add bindings for interrupt sources
2024-12-09 12:36 ` [PATCH 05/26] rust: add bindings for interrupt sources Paolo Bonzini
@ 2024-12-10 7:25 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 7:25 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:36:56PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:36:56 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 05/26] rust: add bindings for interrupt sources
> X-Mailer: git-send-email 2.47.1
>
> The InterruptSource bindings let us call qemu_set_irq() and sysbus_init_irq()
> as safe code.
>
> Interrupt sources, qemu_irq in C code, are pointers to IRQState objects.
> They are QOM link properties and can be written to outside the control
> of the device (i.e. from a shared reference); therefore they must be
> interior-mutable in Rust. Since thread-safety is provided by the BQL,
> what we want here is the newly-introduced BqlCell. A pointer to the
> contents of the BqlCell (an IRQState**, or equivalently qemu_irq*)
> is then passed to the C sysbus_init_irq function.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 22 ++++----
> rust/qemu-api/meson.build | 2 +
> rust/qemu-api/src/irq.rs | 91 ++++++++++++++++++++++++++++++++
> rust/qemu-api/src/lib.rs | 2 +
> rust/qemu-api/src/sysbus.rs | 27 ++++++++++
> 5 files changed, 134 insertions(+), 10 deletions(-)
> create mode 100644 rust/qemu-api/src/irq.rs
> create mode 100644 rust/qemu-api/src/sysbus.rs
>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 06/26] rust: add a bit operation module
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (4 preceding siblings ...)
2024-12-09 12:36 ` [PATCH 05/26] rust: add bindings for interrupt sources Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-10 8:13 ` Zhao Liu
2024-12-09 12:36 ` [PATCH 07/26] rust: qom: add default definitions for ObjectImpl Paolo Bonzini
` (20 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
The bindgen supports `static inline` function binding since v0.64.0 as
an experimental feature (`--wrap-static-fns`), and stabilizes it after
v0.70.0.
But the oldest version of bindgen supported by QEMU is v0.60.1, so
there's no way to generate the binding for deposit64() which is `static
inline` (in include/qemu/bitops.h).
Instead, implement it by hand in Rust and make it available for all
unsigned types through an IntegerExt trait. Since it only involves bit
operations, the Rust version of the code is almost identical to the
original C version, but it applies to more types than just u64.
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/meson.build | 1 +
rust/qemu-api/src/bitops.rs | 119 +++++++++++++++++++++++++++++++++++
rust/qemu-api/src/lib.rs | 1 +
rust/qemu-api/src/prelude.rs | 2 +
4 files changed, 123 insertions(+)
create mode 100644 rust/qemu-api/src/bitops.rs
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index b927eb58c8e..adcee661150 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -16,6 +16,7 @@ _qemu_api_rs = static_library(
[
'src/lib.rs',
'src/bindings.rs',
+ 'src/bitops.rs',
'src/cell.rs',
'src/c_str.rs',
'src/definitions.rs',
diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
new file mode 100644
index 00000000000..5acd6642d1a
--- /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
+ {
+ 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? */
+ assert!(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/lib.rs b/rust/qemu-api/src/lib.rs
index 0efbef47441..9e007e16354 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -12,6 +12,7 @@
#[rustfmt::skip]
pub mod prelude;
+pub mod bitops;
pub mod c_str;
pub mod cell;
pub mod definitions;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index dfaddbd062a..a39e228babf 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -2,5 +2,7 @@
// 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;
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 06/26] rust: add a bit operation module
2024-12-09 12:36 ` [PATCH 06/26] rust: add a bit operation module Paolo Bonzini
@ 2024-12-10 8:13 ` Zhao Liu
2024-12-10 9:30 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 8:13 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:36:57PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:36:57 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 06/26] rust: add a bit operation module
> X-Mailer: git-send-email 2.47.1
>
> The bindgen supports `static inline` function binding since v0.64.0 as
> an experimental feature (`--wrap-static-fns`), and stabilizes it after
> v0.70.0.
>
> But the oldest version of bindgen supported by QEMU is v0.60.1, so
> there's no way to generate the binding for deposit64() which is `static
> inline` (in include/qemu/bitops.h).
>
> Instead, implement it by hand in Rust and make it available for all
> unsigned types through an IntegerExt trait. Since it only involves bit
> operations, the Rust version of the code is almost identical to the
> original C version, but it applies to more types than just u64.
>
> Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
> Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/qemu-api/meson.build | 1 +
> rust/qemu-api/src/bitops.rs | 119 +++++++++++++++++++++++++++++++++++
> rust/qemu-api/src/lib.rs | 1 +
> rust/qemu-api/src/prelude.rs | 2 +
> 4 files changed, 123 insertions(+)
> create mode 100644 rust/qemu-api/src/bitops.rs
>
> diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
> index b927eb58c8e..adcee661150 100644
> --- a/rust/qemu-api/meson.build
> +++ b/rust/qemu-api/meson.build
> @@ -16,6 +16,7 @@ _qemu_api_rs = static_library(
> [
> 'src/lib.rs',
> 'src/bindings.rs',
> + 'src/bitops.rs',
> 'src/cell.rs',
> 'src/c_str.rs',
> 'src/definitions.rs',
> diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
> new file mode 100644
> index 00000000000..5acd6642d1a
> --- /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>
You deserve to be the author!
> +// 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
Ah, I used to define shift bits as usize. I can change the bit shift
type back to u32 in HPET.
> + 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
> + {
> + assert!(start <= Self::BITS);
> +
> + Self::ONE << start
> + }
I think with this helper, I can add activating_bit() and deactivating_bit()
bindings, as they are also commonly used operations.
I will rename them to activate_bit and deactivate_bit, if no one has any
objections.
> + #[inline]
> + #[must_use]
> + fn mask(start: u32, length: u32) -> Self
> + {
> + /* FIXME: Implement a more elegant check with error handling support? */
I think current design is elegant enough, and this FIXME is not needed.
> + assert!(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);
assert? as you've already replaced debug_assert with assert in BqlCell.
> + let mask = Self::mask(start, length);
> + (self & !mask) | ((Self::from(fieldval) << start) & mask)
> + }
> +
Very useful for HPET! Thanks.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 06/26] rust: add a bit operation module
2024-12-10 8:13 ` Zhao Liu
@ 2024-12-10 9:30 ` Paolo Bonzini
0 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-10 9:30 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On 12/10/24 09:13, Zhao Liu wrote:
>> diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
>> new file mode 100644
>> index 00000000000..5acd6642d1a
>> --- /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>
>
> You deserve to be the author!
I'll add both of us. At this stage even identifying a need is an
important step.
>> +// 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
>
> Ah, I used to define shift bits as usize. I can change the bit shift
> type back to u32 in HPET.
I used u32 because BITS is u32 in the standard library, so probably
better to change it in HPET.
>> + 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
>> + {
>> + assert!(start <= Self::BITS);
>> +
>> + Self::ONE << start
>> + }
>
> I think with this helper, I can add activating_bit() and deactivating_bit()
> bindings, as they are also commonly used operations.
>
> I will rename them to activate_bit and deactivate_bit, if no one has any
> objections.
I think "-ing" is correct because it looks at a change between two
values. Or another possibility is putting the mask as "self":
fn bits_activated(from: Self, to: Self) -> bool {
(to & !from & self) != 0
}
fn bits_deactivated(from: Self, to: Self) -> bool {
(from & !to & self) != 0
}
Anyhow feel free to pick something you like and add it to IntegerExt.
>> + #[inline]
>> + #[must_use]
>> + fn deposit<U: IntegerExt>(self, start: u32, length: u32,
>> + fieldval: U) -> Self
>> + where Self: From<U>
>> + {
>> + debug_assert!(length <= U::BITS);
>
> assert? as you've already replaced debug_assert with assert in BqlCell.
No, not this time :) Rust has checks for overflow in debug mode but not
in release mode, so (independent of how Meson handles debug assertions)
debug_assert is the right one here.
Paolo
>> + let mask = Self::mask(start, length);
>> + (self & !mask) | ((Self::from(fieldval) << start) & mask)
>> + }
>> +
>
> Very useful for HPET! Thanks.
>
> Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
>
>
>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 07/26] rust: qom: add default definitions for ObjectImpl
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (5 preceding siblings ...)
2024-12-09 12:36 ` [PATCH 06/26] rust: add a bit operation module Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-10 8:21 ` Zhao Liu
2024-12-09 12:36 ` [PATCH 08/26] rust: qom: rename Class trait to ClassInitImpl Paolo Bonzini
` (19 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Remove a bunch of duplicate const definitions.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 6 ------
rust/qemu-api/src/definitions.rs | 8 ++++----
rust/qemu-api/tests/tests.rs | 4 ----
3 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index c5c8c463d37..3d173ae816d 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -109,10 +109,7 @@ impl ObjectImpl for PL011State {
const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
- const ABSTRACT: bool = false;
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
- 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;
}
#[repr(C)]
@@ -666,8 +663,5 @@ impl ObjectImpl for PL011Luminary {
const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
- const ABSTRACT: bool = false;
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
- 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;
}
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 26597934bbd..92b3c6f9118 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -14,10 +14,10 @@ pub trait ObjectImpl {
const TYPE_INFO: TypeInfo;
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)>;
+ 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;
}
pub trait Class {
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 925f5a3c77b..f793ff26e5d 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -58,10 +58,6 @@ impl ObjectImpl for DummyState {
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);
- 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 {
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 07/26] rust: qom: add default definitions for ObjectImpl
2024-12-09 12:36 ` [PATCH 07/26] rust: qom: add default definitions for ObjectImpl Paolo Bonzini
@ 2024-12-10 8:21 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 8:21 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:36:58PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:36:58 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 07/26] rust: qom: add default definitions for ObjectImpl
> X-Mailer: git-send-email 2.47.1
>
> Remove a bunch of duplicate const definitions.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 6 ------
> rust/qemu-api/src/definitions.rs | 8 ++++----
> rust/qemu-api/tests/tests.rs | 4 ----
> 3 files changed, 4 insertions(+), 14 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 08/26] rust: qom: rename Class trait to ClassInitImpl
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (6 preceding siblings ...)
2024-12-09 12:36 ` [PATCH 07/26] rust: qom: add default definitions for ObjectImpl Paolo Bonzini
@ 2024-12-09 12:36 ` Paolo Bonzini
2024-12-10 8:24 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 09/26] rust: qom: convert type_info! macro to an associated const Paolo Bonzini
` (18 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:36 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
While at it, document it.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 4 ++--
rust/qemu-api/src/definitions.rs | 25 ++++++++++++++++++++++---
rust/qemu-api/tests/tests.rs | 4 ++--
3 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 3d173ae816d..bd12067aaf0 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -117,7 +117,7 @@ pub struct PL011Class {
_inner: [u8; 0],
}
-impl qemu_api::definitions::Class for PL011Class {
+impl qemu_api::definitions::ClassInitImpl for PL011Class {
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
Some(crate::device_class::pl011_class_init);
const CLASS_BASE_INIT: Option<
@@ -650,7 +650,7 @@ pub struct PL011LuminaryClass {
}
}
-impl qemu_api::definitions::Class for PL011LuminaryClass {
+impl qemu_api::definitions::ClassInitImpl for PL011LuminaryClass {
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
None;
const CLASS_BASE_INIT: Option<
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 92b3c6f9118..3291f4242ce 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -20,8 +20,27 @@ pub trait ObjectImpl {
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
}
-pub trait Class {
+/// 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`].
+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),
>;
@@ -82,8 +101,8 @@ macro_rules! type_info {
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_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::ClassInitImpl>::CLASS_INIT,
+ class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::ClassInitImpl>::CLASS_BASE_INIT,
class_data: ::core::ptr::null_mut(),
interfaces: ::core::ptr::null_mut(),
};
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index f793ff26e5d..704c63c846f 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -7,7 +7,7 @@
use qemu_api::{
bindings::*,
c_str, declare_properties, define_property,
- definitions::{Class, ObjectImpl},
+ definitions::{ClassInitImpl, ObjectImpl},
device_class, device_class_init,
zeroable::Zeroable,
};
@@ -60,7 +60,7 @@ impl ObjectImpl for DummyState {
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
}
- impl Class for DummyClass {
+ impl ClassInitImpl 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<
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 08/26] rust: qom: rename Class trait to ClassInitImpl
2024-12-09 12:36 ` [PATCH 08/26] rust: qom: rename Class trait to ClassInitImpl Paolo Bonzini
@ 2024-12-10 8:24 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 8:24 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:36:59PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:36:59 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 08/26] rust: qom: rename Class trait to ClassInitImpl
> X-Mailer: git-send-email 2.47.1
>
> While at it, document it.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 4 ++--
> rust/qemu-api/src/definitions.rs | 25 ++++++++++++++++++++++---
> rust/qemu-api/tests/tests.rs | 4 ++--
> 3 files changed, 26 insertions(+), 7 deletions(-)
LGTM,
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 09/26] rust: qom: convert type_info! macro to an associated const
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (7 preceding siblings ...)
2024-12-09 12:36 ` [PATCH 08/26] rust: qom: rename Class trait to ClassInitImpl Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-10 8:30 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 10/26] rust: qom: move ClassInitImpl to the instance side Paolo Bonzini
` (17 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
type_info! is only used in the definition of ObjectImpl::TYPE_INFO, and
in fact in all of them. Pull type_info!'s definition into the ObjectImpl
trait, thus simplifying the external interface of qemu_api::definitions.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 6 ++--
rust/qemu-api/src/definitions.rs | 50 ++++++++++++++------------------
rust/qemu-api/tests/tests.rs | 1 -
3 files changed, 24 insertions(+), 33 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index bd12067aaf0..bcb146c24d6 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -106,7 +106,6 @@ pub struct PL011State {
impl ObjectImpl for PL011State {
type Class = PL011Class;
- const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
@@ -149,7 +148,7 @@ unsafe fn init(&mut self) {
addr_of_mut!(*self).cast::<Object>(),
&PL011_OPS,
addr_of_mut!(*self).cast::<c_void>(),
- Self::TYPE_INFO.name,
+ Self::TYPE_NAME.as_ptr(),
0x1000,
);
sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
@@ -598,7 +597,7 @@ pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> {
chr: *mut Chardev,
) -> *mut DeviceState {
unsafe {
- let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name);
+ let dev: *mut DeviceState = qdev_new(PL011State::TYPE_NAME.as_ptr());
let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr);
@@ -660,7 +659,6 @@ impl qemu_api::definitions::ClassInitImpl for PL011LuminaryClass {
impl ObjectImpl for PL011Luminary {
type Class = PL011LuminaryClass;
- const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 3291f4242ce..6ecfaf51b09 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -9,15 +9,34 @@
use crate::bindings::{Object, ObjectClass, TypeInfo};
/// Trait a type must implement to be registered with QEMU.
-pub trait ObjectImpl {
- type Class;
- const TYPE_INFO: TypeInfo;
+pub trait ObjectImpl: Sized {
+ type Class: ClassInitImpl;
const TYPE_NAME: &'static CStr;
const PARENT_TYPE_NAME: Option<&'static CStr>;
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;
+
+ const TYPE_INFO: TypeInfo = TypeInfo {
+ name: Self::TYPE_NAME.as_ptr(),
+ parent: if let Some(pname) = Self::PARENT_TYPE_NAME {
+ pname.as_ptr()
+ } else {
+ core::ptr::null_mut()
+ },
+ instance_size: core::mem::size_of::<Self>(),
+ instance_align: core::mem::align_of::<Self>(),
+ instance_init: Self::INSTANCE_INIT,
+ instance_post_init: Self::INSTANCE_POST_INIT,
+ instance_finalize: Self::INSTANCE_FINALIZE,
+ abstract_: Self::ABSTRACT,
+ class_size: core::mem::size_of::<Self::Class>(),
+ class_init: <Self::Class as ClassInitImpl>::CLASS_INIT,
+ class_base_init: <Self::Class as ClassInitImpl>::CLASS_BASE_INIT,
+ class_data: core::ptr::null_mut(),
+ interfaces: core::ptr::null_mut(),
+ };
}
/// Trait used to fill in a class struct.
@@ -83,28 +102,3 @@ extern "C" fn ctor_fn() {
}
};
}
-
-#[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::ClassInitImpl>::CLASS_INIT,
- class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::ClassInitImpl>::CLASS_BASE_INIT,
- class_data: ::core::ptr::null_mut(),
- interfaces: ::core::ptr::null_mut(),
- };
- }
-}
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 704c63c846f..7f9df348b00 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -55,7 +55,6 @@ pub struct DummyClass {
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);
}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 09/26] rust: qom: convert type_info! macro to an associated const
2024-12-09 12:37 ` [PATCH 09/26] rust: qom: convert type_info! macro to an associated const Paolo Bonzini
@ 2024-12-10 8:30 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 8:30 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:00PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:00 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 09/26] rust: qom: convert type_info! macro to an associated
> const
> X-Mailer: git-send-email 2.47.1
>
> type_info! is only used in the definition of ObjectImpl::TYPE_INFO, and
> in fact in all of them. Pull type_info!'s definition into the ObjectImpl
> trait, thus simplifying the external interface of qemu_api::definitions.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 6 ++--
> rust/qemu-api/src/definitions.rs | 50 ++++++++++++++------------------
> rust/qemu-api/tests/tests.rs | 1 -
> 3 files changed, 24 insertions(+), 33 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 10/26] rust: qom: move ClassInitImpl to the instance side
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (8 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 09/26] rust: qom: convert type_info! macro to an associated const Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-10 16:03 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 11/26] rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro Paolo Bonzini
` (16 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Put all traits on the instance struct, which makes it possible to reuse
class structs if no new virtual methods or class fields are added.
This is almost always the case for devices (because they are leaf
classes), which is the primary use case for Rust.
This is also simpler: soon we will find the implemented methods without
macros, and this removes the need to go from the class struct to the
instance struct to find the implementation of the *Impl traits.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 4 ++--
rust/qemu-api/src/definitions.rs | 8 ++++----
rust/qemu-api/tests/tests.rs | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index bcb146c24d6..2384d4bcb95 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -116,7 +116,7 @@ pub struct PL011Class {
_inner: [u8; 0],
}
-impl qemu_api::definitions::ClassInitImpl for PL011Class {
+impl qemu_api::definitions::ClassInitImpl for PL011State {
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
Some(crate::device_class::pl011_class_init);
const CLASS_BASE_INIT: Option<
@@ -649,7 +649,7 @@ pub struct PL011LuminaryClass {
}
}
-impl qemu_api::definitions::ClassInitImpl for PL011LuminaryClass {
+impl qemu_api::definitions::ClassInitImpl for PL011Luminary {
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
None;
const CLASS_BASE_INIT: Option<
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 6ecfaf51b09..487712611f6 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -9,8 +9,8 @@
use crate::bindings::{Object, ObjectClass, TypeInfo};
/// Trait a type must implement to be registered with QEMU.
-pub trait ObjectImpl: Sized {
- type Class: ClassInitImpl;
+pub trait ObjectImpl: ClassInitImpl + Sized {
+ type Class;
const TYPE_NAME: &'static CStr;
const PARENT_TYPE_NAME: Option<&'static CStr>;
const ABSTRACT: bool = false;
@@ -32,8 +32,8 @@ pub trait ObjectImpl: Sized {
instance_finalize: Self::INSTANCE_FINALIZE,
abstract_: Self::ABSTRACT,
class_size: core::mem::size_of::<Self::Class>(),
- class_init: <Self::Class as ClassInitImpl>::CLASS_INIT,
- class_base_init: <Self::Class as ClassInitImpl>::CLASS_BASE_INIT,
+ 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(),
};
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 7f9df348b00..fd0c979121c 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -59,7 +59,7 @@ impl ObjectImpl for DummyState {
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
}
- impl ClassInitImpl for DummyClass {
+ impl ClassInitImpl for DummyState {
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
Some(dummy_class_init);
const CLASS_BASE_INIT: Option<
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 10/26] rust: qom: move ClassInitImpl to the instance side
2024-12-09 12:37 ` [PATCH 10/26] rust: qom: move ClassInitImpl to the instance side Paolo Bonzini
@ 2024-12-10 16:03 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 16:03 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:01PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:01 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 10/26] rust: qom: move ClassInitImpl to the instance side
> X-Mailer: git-send-email 2.47.1
>
> Put all traits on the instance struct, which makes it possible to reuse
> class structs if no new virtual methods or class fields are added.
> This is almost always the case for devices (because they are leaf
> classes), which is the primary use case for Rust.
>
> This is also simpler: soon we will find the implemented methods without
> macros, and this removes the need to go from the class struct to the
> instance struct to find the implementation of the *Impl traits.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 4 ++--
> rust/qemu-api/src/definitions.rs | 8 ++++----
> rust/qemu-api/tests/tests.rs | 2 +-
> 3 files changed, 7 insertions(+), 7 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 11/26] rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (9 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 10/26] rust: qom: move ClassInitImpl to the instance side Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-10 16:06 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 12/26] rust: qdev: move bridge for realize and reset functions out of pl011 Paolo Bonzini
` (15 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Use a trait to access the former parameters to device_class_init!.
This allows hiding the details of the class_init implementation behind
a generic function and makes higher-level functionality available from
qemu_api.
The implementation of ClassInitImpl is then the same for all devices and
is easily macroized. Later on, we can remove the need to implement
ClassInitImpl by hand for all device types, and stop making
rust_device_class_init<>() public.
While at it, document the members of DeviceImpl.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 34 +++++-----
rust/hw/char/pl011/src/device_class.rs | 8 ---
rust/qemu-api/src/device_class.rs | 87 +++++++++++++++++++++-----
rust/qemu-api/tests/tests.rs | 30 ++++-----
4 files changed, 103 insertions(+), 56 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 2384d4bcb95..28b1924337d 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -12,11 +12,13 @@
bindings::{self, *},
c_str,
definitions::ObjectImpl,
- device_class::TYPE_SYS_BUS_DEVICE,
+ device_class::{DeviceImpl, TYPE_SYS_BUS_DEVICE},
+ impl_device_class,
irq::InterruptSource,
};
use crate::{
+ device_class,
memory_ops::PL011_OPS,
registers::{self, Interrupt},
RegisterOffset,
@@ -116,14 +118,20 @@ pub struct PL011Class {
_inner: [u8; 0],
}
-impl qemu_api::definitions::ClassInitImpl for PL011State {
- const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
- Some(crate::device_class::pl011_class_init);
- const CLASS_BASE_INIT: Option<
- unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
- > = None;
+impl DeviceImpl for PL011State {
+ fn properties() -> &'static [Property] {
+ &device_class::PL011_PROPERTIES
+ }
+ fn vmsd() -> Option<&'static VMStateDescription> {
+ Some(&device_class::VMSTATE_PL011)
+ }
+ const REALIZE: Option<unsafe extern "C" fn(*mut DeviceState, *mut *mut Error)> =
+ Some(device_class::pl011_realize);
+ const RESET: Option<unsafe extern "C" fn(*mut DeviceState)> = Some(device_class::pl011_reset);
}
+impl_device_class!(PL011State);
+
impl PL011State {
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
///
@@ -649,17 +657,13 @@ pub struct PL011LuminaryClass {
}
}
-impl qemu_api::definitions::ClassInitImpl for PL011Luminary {
- const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
- None;
- const CLASS_BASE_INIT: Option<
- unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
- > = None;
-}
-
impl ObjectImpl for PL011Luminary {
type Class = PL011LuminaryClass;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
}
+
+impl DeviceImpl for PL011Luminary {}
+
+impl_device_class!(PL011Luminary);
diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs
index a707fde1384..c61b6bb0258 100644
--- a/rust/hw/char/pl011/src/device_class.rs
+++ b/rust/hw/char/pl011/src/device_class.rs
@@ -93,14 +93,6 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
),
}
-qemu_api::device_class_init! {
- pl011_class_init,
- props => PL011_PROPERTIES,
- realize_fn => Some(pl011_realize),
- legacy_reset_fn => Some(pl011_reset),
- vmsd => VMSTATE_PL011,
-}
-
/// # Safety
///
/// We expect the FFI user of this function to pass a valid pointer, that has
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
index 922bbce1bb8..f683f94f2a5 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/device_class.rs
@@ -2,25 +2,80 @@
// 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},
+ 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<unsafe extern "C" fn(*mut DeviceState, *mut *mut Error)> = 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<unsafe extern "C" fn(dev: *mut DeviceState)> = 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
+///
+/// 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 let Some(realize_fn) = <T as DeviceImpl>::REALIZE {
+ dc.realize = Some(realize_fn);
+ }
+ if let Some(reset_fn) = <T as DeviceImpl>::RESET {
+ bindings::device_class_set_legacy_reset(dc, Some(reset_fn));
+ }
+ 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;
}
};
}
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index fd0c979121c..b8b12a40422 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -2,13 +2,14 @@
// 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::{ClassInitImpl, ObjectImpl},
- device_class, device_class_init,
+ definitions::ObjectImpl,
+ device_class::{self, DeviceImpl},
+ impl_device_class,
zeroable::Zeroable,
};
@@ -45,28 +46,23 @@ pub struct DummyClass {
),
}
- device_class_init! {
- dummy_class_init,
- props => DUMMY_PROPERTIES,
- realize_fn => None,
- legacy_reset_fn => None,
- vmsd => VMSTATE,
- }
-
impl ObjectImpl for DummyState {
type Class = DummyClass;
const TYPE_NAME: &'static CStr = c_str!("dummy");
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
}
- impl ClassInitImpl for DummyState {
- 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()).cast());
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 11/26] rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro
2024-12-09 12:37 ` [PATCH 11/26] rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro Paolo Bonzini
@ 2024-12-10 16:06 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 16:06 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:02PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:02 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 11/26] rust: qdev: move device_class_init! body to generic
> function, ClassInitImpl implementation to macro
> X-Mailer: git-send-email 2.47.1
>
> Use a trait to access the former parameters to device_class_init!.
> This allows hiding the details of the class_init implementation behind
> a generic function and makes higher-level functionality available from
> qemu_api.
>
> The implementation of ClassInitImpl is then the same for all devices and
> is easily macroized. Later on, we can remove the need to implement
> ClassInitImpl by hand for all device types, and stop making
> rust_device_class_init<>() public.
>
> While at it, document the members of DeviceImpl.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 34 +++++-----
> rust/hw/char/pl011/src/device_class.rs | 8 ---
> rust/qemu-api/src/device_class.rs | 87 +++++++++++++++++++++-----
> rust/qemu-api/tests/tests.rs | 30 ++++-----
> 4 files changed, 103 insertions(+), 56 deletions(-)
Rviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 12/26] rust: qdev: move bridge for realize and reset functions out of pl011
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (10 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 11/26] rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-10 16:07 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 13/26] rust: qom: automatically use Drop trait to implement instance_finalize Paolo Bonzini
` (14 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Allow the DeviceImpl trait to expose safe Rust functions.
rust_device_class_init<> adds thunks around the functions
in DeviceImpl.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 5 ++--
rust/hw/char/pl011/src/device_class.rs | 26 -------------------
rust/qemu-api/src/definitions.rs | 2 +-
rust/qemu-api/src/device_class.rs | 36 +++++++++++++++++++++-----
4 files changed, 33 insertions(+), 36 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 28b1924337d..56403c36609 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -125,9 +125,8 @@ fn properties() -> &'static [Property] {
fn vmsd() -> Option<&'static VMStateDescription> {
Some(&device_class::VMSTATE_PL011)
}
- const REALIZE: Option<unsafe extern "C" fn(*mut DeviceState, *mut *mut Error)> =
- Some(device_class::pl011_realize);
- const RESET: Option<unsafe extern "C" fn(*mut DeviceState)> = Some(device_class::pl011_reset);
+ const REALIZE: Option<fn(&mut Self)> = Some(Self::realize);
+ const RESET: Option<fn(&mut Self)> = Some(Self::reset);
}
impl_device_class!(PL011State);
diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs
index c61b6bb0258..975c3d42be7 100644
--- a/rust/hw/char/pl011/src/device_class.rs
+++ b/rust/hw/char/pl011/src/device_class.rs
@@ -92,29 +92,3 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
default = true
),
}
-
-/// # Safety
-///
-/// We expect the FFI user of this function to pass a valid pointer, that has
-/// the same size as [`PL011State`]. We also expect the device is
-/// readable/writeable from one thread at any time.
-pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) {
- unsafe {
- assert!(!dev.is_null());
- let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
- state.as_mut().realize();
- }
-}
-
-/// # Safety
-///
-/// We expect the FFI user of this function to pass a valid pointer, that has
-/// the same size as [`PL011State`]. We also expect the device is
-/// readable/writeable from one thread at any time.
-pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) {
- unsafe {
- assert!(!dev.is_null());
- let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
- state.as_mut().reset();
- }
-}
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 487712611f6..0467e6290e0 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -47,7 +47,7 @@ pub trait ObjectImpl: ClassInitImpl + Sized {
/// Each QOM type has one such class struct.
///
/// The Rust implementation of methods will usually come from a trait
-/// like [`ObjectImpl`].
+/// 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
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
index f683f94f2a5..f25904be4f6 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/device_class.rs
@@ -17,14 +17,14 @@ pub trait DeviceImpl {
///
/// If not `None`, the parent class's `realize` method is overridden
/// with the function pointed to by `REALIZE`.
- const REALIZE: Option<unsafe extern "C" fn(*mut DeviceState, *mut *mut Error)> = None;
+ 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<unsafe extern "C" fn(dev: *mut DeviceState)> = None;
+ 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
@@ -41,6 +41,30 @@ fn vmsd() -> Option<&'static VMStateDescription> {
}
}
+/// # 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
@@ -53,11 +77,11 @@ fn vmsd() -> Option<&'static VMStateDescription> {
let mut dc = ::core::ptr::NonNull::new(klass.cast::<DeviceClass>()).unwrap();
unsafe {
let dc = dc.as_mut();
- if let Some(realize_fn) = <T as DeviceImpl>::REALIZE {
- dc.realize = Some(realize_fn);
+ if <T as DeviceImpl>::REALIZE.is_some() {
+ dc.realize = Some(rust_realize_fn::<T>);
}
- if let Some(reset_fn) = <T as DeviceImpl>::RESET {
- bindings::device_class_set_legacy_reset(dc, Some(reset_fn));
+ 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;
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 12/26] rust: qdev: move bridge for realize and reset functions out of pl011
2024-12-09 12:37 ` [PATCH 12/26] rust: qdev: move bridge for realize and reset functions out of pl011 Paolo Bonzini
@ 2024-12-10 16:07 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 16:07 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:03PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:03 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 12/26] rust: qdev: move bridge for realize and reset
> functions out of pl011
> X-Mailer: git-send-email 2.47.1
>
> Allow the DeviceImpl trait to expose safe Rust functions.
> rust_device_class_init<> adds thunks around the functions
> in DeviceImpl.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 5 ++--
> rust/hw/char/pl011/src/device_class.rs | 26 -------------------
> rust/qemu-api/src/definitions.rs | 2 +-
> rust/qemu-api/src/device_class.rs | 36 +++++++++++++++++++++-----
> 4 files changed, 33 insertions(+), 36 deletions(-)
>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 13/26] rust: qom: automatically use Drop trait to implement instance_finalize
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (11 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 12/26] rust: qdev: move bridge for realize and reset functions out of pl011 Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-10 16:16 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011 Paolo Bonzini
` (13 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Replace the customizable INSTANCE_FINALIZE with a generic function
that drops the Rust object.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/src/definitions.rs | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 0467e6290e0..d64a581a5cc 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -8,6 +8,13 @@
use crate::bindings::{Object, ObjectClass, TypeInfo};
+unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
+ // SAFETY: obj is an instance of T, since drop_object<T>
+ // is called from QOM core as the instance_finalize function
+ // for class T
+ unsafe { std::ptr::drop_in_place(obj.cast::<T>()) }
+}
+
/// Trait a type must implement to be registered with QEMU.
pub trait ObjectImpl: ClassInitImpl + Sized {
type Class;
@@ -16,7 +23,6 @@ pub trait ObjectImpl: ClassInitImpl + Sized {
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;
const TYPE_INFO: TypeInfo = TypeInfo {
name: Self::TYPE_NAME.as_ptr(),
@@ -29,7 +35,7 @@ pub trait ObjectImpl: ClassInitImpl + Sized {
instance_align: core::mem::align_of::<Self>(),
instance_init: Self::INSTANCE_INIT,
instance_post_init: Self::INSTANCE_POST_INIT,
- instance_finalize: Self::INSTANCE_FINALIZE,
+ instance_finalize: Some(drop_object::<Self>),
abstract_: Self::ABSTRACT,
class_size: core::mem::size_of::<Self::Class>(),
class_init: <Self as ClassInitImpl>::CLASS_INIT,
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 13/26] rust: qom: automatically use Drop trait to implement instance_finalize
2024-12-09 12:37 ` [PATCH 13/26] rust: qom: automatically use Drop trait to implement instance_finalize Paolo Bonzini
@ 2024-12-10 16:16 ` Zhao Liu
2024-12-11 12:42 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 16:16 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:04PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:04 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 13/26] rust: qom: automatically use Drop trait to implement
> instance_finalize
> X-Mailer: git-send-email 2.47.1
>
> Replace the customizable INSTANCE_FINALIZE with a generic function
> that drops the Rust object.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/qemu-api/src/definitions.rs | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
Great idea. It nicely balances the differences between Rust and C QOM
conventions.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 13/26] rust: qom: automatically use Drop trait to implement instance_finalize
2024-12-10 16:16 ` Zhao Liu
@ 2024-12-11 12:42 ` Paolo Bonzini
2024-12-11 15:37 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-11 12:42 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Tue, Dec 10, 2024 at 4:58 PM Zhao Liu <zhao1.liu@intel.com> wrote:
> Great idea. It nicely balances the differences between Rust and C QOM
> conventions.
Except it does not work. :( Suppose you have
pub struct MySuperclass {
parent: DeviceState,
field: Box<MyData>,
...
}
impl Drop for MySuperclass {
...
}
pub struct MySubclass {
parent: MySuperclass,
...
}
When instance_finalize is called for MySubclass, it will walk the
struct's list of fields and call the drop method for MySuperclass.
Then, object_deinit recurses to the superclass and calls the same drop
method again. This will cause double-freeing of the Box<MyData>, or
more in general double-dropping.
What's happening here is that QOM wants to control the drop order of
MySuperclass and MySubclass's fields. To do so, the parent field must
be marked ManuallyDrop<>, which is quite ugly. Perhaps we can add a
wrapper type ParentField<> that is specific to QOM. This hides the
implementation detail of *what* is special about the ParentField, and
it will also be easy to check for in the #[derive(Object)] macro.
Maybe in the future it will even make sense to have special functions
implemented on ParentField, I don't know...
Paolo
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 13/26] rust: qom: automatically use Drop trait to implement instance_finalize
2024-12-11 12:42 ` Paolo Bonzini
@ 2024-12-11 15:37 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-11 15:37 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Wed, Dec 11, 2024 at 01:42:32PM +0100, Paolo Bonzini wrote:
> Date: Wed, 11 Dec 2024 13:42:32 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 13/26] rust: qom: automatically use Drop trait to
> implement instance_finalize
>
> On Tue, Dec 10, 2024 at 4:58 PM Zhao Liu <zhao1.liu@intel.com> wrote:
> > Great idea. It nicely balances the differences between Rust and C QOM
> > conventions.
>
> Except it does not work. :( Suppose you have
>
> pub struct MySuperclass {
> parent: DeviceState,
> field: Box<MyData>,
> ...
> }
>
> impl Drop for MySuperclass {
> ...
> }
>
> pub struct MySubclass {
> parent: MySuperclass,
> ...
> }
>
> When instance_finalize is called for MySubclass, it will walk the
> struct's list of fields and call the drop method for MySuperclass.
> Then, object_deinit recurses to the superclass and calls the same drop
> method again. This will cause double-freeing of the Box<MyData>, or
> more in general double-dropping.
Good catch! Yes, there is indeed such an issue... The above example
could become a test case :-), which I supposed could trigger a double-
dropping error when compiling.
> What's happening here is that QOM wants to control the drop order of
> MySuperclass and MySubclass's fields. To do so, the parent field must
> be marked ManuallyDrop<>, which is quite ugly. Perhaps we can add a
> wrapper type ParentField<> that is specific to QOM. This hides the
> implementation detail of *what* is special about the ParentField, and
> it will also be easy to check for in the #[derive(Object)] macro.
> Maybe in the future it will even make sense to have special functions
> implemented on ParentField, I don't know...
I also looked into the implementation of ManuallyDrop, and I agree with
a new ParentField (or simply ObjectParent). This wrapper is simple
enough but also useful for QOM. I will pay more attention to the
recursed relationships in QOM in review as well...
Thanks,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (12 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 13/26] rust: qom: automatically use Drop trait to implement instance_finalize Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-10 15:50 ` Zhao Liu
2024-12-10 16:02 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 15/26] rust: qom: split ObjectType from ObjectImpl trait Paolo Bonzini
` (12 subsequent siblings)
26 siblings, 2 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Allow the ObjectImpl trait to expose Rust functions that avoid raw
pointers (though INSTANCE_INIT for example is still unsafe).
ObjectImpl::TYPE_INFO adds thunks around the functions in
ObjectImpl.
While at it, document `TypeInfo`.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 40 +++++++--------------
rust/qemu-api/src/definitions.rs | 61 +++++++++++++++++++++++++++++---
2 files changed, 69 insertions(+), 32 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 56403c36609..b9f8fb134b5 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -110,7 +110,7 @@ impl ObjectImpl for PL011State {
type Class = PL011Class;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
- const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
+ const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
#[repr(C)]
@@ -615,19 +615,6 @@ pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> {
}
}
-/// # Safety
-///
-/// We expect the FFI user of this function to pass a valid pointer, that has
-/// the same size as [`PL011State`]. We also expect the device is
-/// readable/writeable from one thread at any time.
-pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
- unsafe {
- debug_assert!(!obj.is_null());
- let mut state = NonNull::new_unchecked(obj.cast::<PL011State>());
- state.as_mut().init();
- }
-}
-
#[repr(C)]
#[derive(Debug, qemu_api_macros::Object)]
/// PL011 Luminary device model.
@@ -640,19 +627,16 @@ pub struct PL011LuminaryClass {
_inner: [u8; 0],
}
-/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
-///
-/// # Safety
-///
-/// We expect the FFI user of this function to pass a valid pointer, that has
-/// the same size as [`PL011Luminary`]. We also expect the device is
-/// readable/writeable from one thread at any time.
-pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) {
- unsafe {
- debug_assert!(!obj.is_null());
- let mut state = NonNull::new_unchecked(obj.cast::<PL011Luminary>());
- let state = state.as_mut();
- state.parent_obj.device_id = DeviceId::Luminary;
+impl PL011Luminary {
+ /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
+ ///
+ /// # Safety
+ ///
+ /// We expect the FFI user of this function to pass a valid pointer, that
+ /// has the same size as [`PL011Luminary`]. We also expect the device is
+ /// readable/writeable from one thread at any time.
+ unsafe fn init(&mut self) {
+ self.parent_obj.device_id = DeviceId::Luminary;
}
}
@@ -660,7 +644,7 @@ impl ObjectImpl for PL011Luminary {
type Class = PL011LuminaryClass;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
- const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
+ const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
impl DeviceImpl for PL011Luminary {}
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index d64a581a5cc..078ba30d61a 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -8,6 +8,24 @@
use crate::bindings::{Object, ObjectClass, TypeInfo};
+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>() })
+}
+
unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
// SAFETY: obj is an instance of T, since drop_object<T>
// is called from QOM core as the instance_finalize function
@@ -16,13 +34,42 @@
}
/// Trait a type must implement to be registered with QEMU.
+///
+/// # Safety
+///
+/// - the struct must be `#[repr(C)]`
+///
+/// - `Class` and `TYPE` must match the data in the `TypeInfo` (this is
+/// automatic if the class is defined via `ObjectImpl`).
+///
+/// - the first field of the struct must be of the instance struct corresponding
+/// to the superclass declared as `PARENT_TYPE_NAME`
pub trait ObjectImpl: ClassInitImpl + Sized {
+ /// The QOM class object corresponding to this struct. Not used yet.
type Class;
+
+ /// The name of the type, which can be passed to `object_new()` to
+ /// generate an instance of this type.
const TYPE_NAME: &'static CStr;
+
+ /// The parent of the type. This should match the first field of
+ /// the struct that implements `ObjectImpl`:
const PARENT_TYPE_NAME: Option<&'static CStr>;
+
+ /// Whether the object can be instantiated
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;
+
+ /// 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(),
@@ -33,8 +80,14 @@ pub trait ObjectImpl: ClassInitImpl + Sized {
},
instance_size: core::mem::size_of::<Self>(),
instance_align: core::mem::align_of::<Self>(),
- instance_init: Self::INSTANCE_INIT,
- instance_post_init: Self::INSTANCE_POST_INIT,
+ 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: Some(drop_object::<Self>),
abstract_: Self::ABSTRACT,
class_size: core::mem::size_of::<Self::Class>(),
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-09 12:37 ` [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011 Paolo Bonzini
@ 2024-12-10 15:50 ` Zhao Liu
2024-12-10 17:38 ` Paolo Bonzini
2024-12-10 16:02 ` Zhao Liu
1 sibling, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 15:50 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:05PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:05 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of
> pl011
> X-Mailer: git-send-email 2.47.1
>
> Allow the ObjectImpl trait to expose Rust functions that avoid raw
> pointers (though INSTANCE_INIT for example is still unsafe).
> ObjectImpl::TYPE_INFO adds thunks around the functions in
> ObjectImpl.
>
> While at it, document `TypeInfo`.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 40 +++++++--------------
> rust/qemu-api/src/definitions.rs | 61 +++++++++++++++++++++++++++++---
> 2 files changed, 69 insertions(+), 32 deletions(-)
>
> diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
> index 56403c36609..b9f8fb134b5 100644
> --- a/rust/hw/char/pl011/src/device.rs
> +++ b/rust/hw/char/pl011/src/device.rs
> @@ -110,7 +110,7 @@ impl ObjectImpl for PL011State {
> type Class = PL011Class;
> const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
> const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
> - const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
> + const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
No need to keep `unsafe` here?
> +///
> +/// - the struct must be `#[repr(C)]`
> +///
> +/// - `Class` and `TYPE` must match the data in the `TypeInfo` (this is
> +/// automatic if the class is defined via `ObjectImpl`).
> +///
> +/// - the first field of the struct must be of the instance struct corresponding
> +/// to the superclass declared as `PARENT_TYPE_NAME`
> pub trait ObjectImpl: ClassInitImpl + Sized {
> + /// The QOM class object corresponding to this struct. Not used yet.
> type Class;
> +
> + /// The name of the type, which can be passed to `object_new()` to
> + /// generate an instance of this type.
> const TYPE_NAME: &'static CStr;
> +
> + /// The parent of the type. This should match the first field of
> + /// the struct that implements `ObjectImpl`:
> const PARENT_TYPE_NAME: Option<&'static CStr>;
> +
> + /// Whether the object can be instantiated
> 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;
> +
> + /// 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;
And here.
> + /// 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;
>
Otherwise,
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-10 15:50 ` Zhao Liu
@ 2024-12-10 17:38 ` Paolo Bonzini
2024-12-11 7:59 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-10 17:38 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On 12/10/24 16:50, Zhao Liu wrote:
> On Mon, Dec 09, 2024 at 01:37:05PM +0100, Paolo Bonzini wrote:
>> Date: Mon, 9 Dec 2024 13:37:05 +0100
>> From: Paolo Bonzini <pbonzini@redhat.com>
>> Subject: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of
>> pl011
>> X-Mailer: git-send-email 2.47.1
>>
>> Allow the ObjectImpl trait to expose Rust functions that avoid raw
>> pointers (though INSTANCE_INIT for example is still unsafe).
>> ObjectImpl::TYPE_INFO adds thunks around the functions in
>> ObjectImpl.
>>
>> While at it, document `TypeInfo`.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> ---
>> rust/hw/char/pl011/src/device.rs | 40 +++++++--------------
>> rust/qemu-api/src/definitions.rs | 61 +++++++++++++++++++++++++++++---
>> 2 files changed, 69 insertions(+), 32 deletions(-)
>>
>> diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
>> index 56403c36609..b9f8fb134b5 100644
>> --- a/rust/hw/char/pl011/src/device.rs
>> +++ b/rust/hw/char/pl011/src/device.rs
>> @@ -110,7 +110,7 @@ impl ObjectImpl for PL011State {
>> type Class = PL011Class;
>> const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
>> const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
>> - const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
>> + const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
>
> No need to keep `unsafe` here?
Right now instance_init is called with only the parent initialized, and
the remaining memory zeroed; its purpose is to prepare things for
instance_post_init which can then be safe (it's also kind of wrong for
instance_post_init to receive a &mut Self, because instance_init will
create other pointers to the object, for example in a MemoryRegion's
"parent" field).
The right thing to do would be to have an argument of type &mut
MaybeUninit<Self>. Then the caller would do something like
let maybe_uninit = obj as *mut MaybeUninit<Self>;
unsafe {
Self::INSTANCE_INIT(&mut *maybe_uninit);
maybe_uninit.assume_init_mut();
}
Note however that INSTANCE_INIT would still be unsafe, because its
safety promise is that it prepares things for the caller's
assume_init_mut().
The way that this will become safe is to use the pinned_init crate from
Linux: instance_init returns the initialization as an "impl
PinInit<Self>", and then instance_post_init can run with a &self. Until
then, however, instance_init has to remain unsafe.
Paolo
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-10 17:38 ` Paolo Bonzini
@ 2024-12-11 7:59 ` Zhao Liu
2024-12-11 9:11 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-11 7:59 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> > > - const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
> > > + const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
> >
> > No need to keep `unsafe` here?
>
> Right now instance_init is called with only the parent initialized, and the
> remaining memory zeroed; its purpose is to prepare things for
> instance_post_init which can then be safe (it's also kind of wrong for
> instance_post_init to receive a &mut Self, because instance_init will create
> other pointers to the object, for example in a MemoryRegion's "parent"
> field).
Thank you for explanation. It makes a lot of sense.
> The right thing to do would be to have an argument of type &mut
> MaybeUninit<Self>. Then the caller would do something like
>
> let maybe_uninit = obj as *mut MaybeUninit<Self>;
> unsafe {
> Self::INSTANCE_INIT(&mut *maybe_uninit);
> maybe_uninit.assume_init_mut();
> }
>
> Note however that INSTANCE_INIT would still be unsafe, because its safety
> promise is that it prepares things for the caller's assume_init_mut().
Yes, I feel that this approach more clearly explains the purpose of QOM
init.
And since we are talking about the safety of INSTANCE_INIT, I think we
should add some safety guidelines here, like:
* Proper Initialization of pointers and references
* Explicit initialization of Non-Zero Fields
* In placed memory region is correctly initialized.
(Or do you have any additional or clearer guidelines?)
This could be the reference when adding SAFETY comment for the device's
own `unsafe` init(). :-)
And this is also a good explanation to distinguish between initialization
in init() and realize(). For example, HPET attempts to initialize the
timer array in realize().
> The way that this will become safe is to use the pinned_init crate from
> Linux: instance_init returns the initialization as an "impl PinInit<Self>",
Then do we need to place OBJECT in some suitable memory location (Box or
something) for Rust implementation?
> and then instance_post_init can run with a &self. Until then, however,
> instance_init has to remain unsafe.
Thanks for sharing! It's a very reasonable direction.
Regards,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-11 7:59 ` Zhao Liu
@ 2024-12-11 9:11 ` Paolo Bonzini
2024-12-11 16:56 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-11 9:11 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
[-- Attachment #1: Type: text/plain, Size: 2780 bytes --]
Il mer 11 dic 2024, 08:41 Zhao Liu <zhao1.liu@intel.com> ha scritto:
> And since we are talking about the safety of INSTANCE_INIT, I think we
> should add some safety guidelines here, like:
> * Proper Initialization of pointers and references
> * Explicit initialization of Non-Zero Fields
> * In placed memory region is correctly initialized.
>
Yes that's pretty much it.
(Or do you have any additional or clearer guidelines?)
>
> This could be the reference when adding SAFETY comment for the device's
> own `unsafe` init(). :-)
>
> And this is also a good explanation to distinguish between initialization
> in init() and realize(). For example, HPET attempts to initialize the
> timer array in realize().
>
Generally:
- embedded objects will have to be initialized in instance_init unless they
are Options
- sysbus_init_irq and sysbus_init_mmio can be called in both instance_init
and instance_post_init for now, but they will have to be in post_init once
the signature of init changes to return impl PinInit
- if you don't need properties you can choose between post_init and
realize, if you need properties you need to initialize in realize (and
then, unlike C, you might need to explicitly allow the pre-realize state;
for example using Option<&...> instead of just a reference; or Vec<>
instead of an array).
>
> > The way that this will become safe is to use the pinned_init crate from
> > Linux: instance_init returns the initialization as an "impl
> PinInit<Self>",
>
> Then do we need to place OBJECT in some suitable memory location (Box or
> something) for Rust implementation?
>
Allocation is still done by the C code, so I am not sure I understand the
question. Rust code allocates QOM objects with object_new() so they are
malloc-ed. I discussed it with Manos some time ago and in principle you
could use a Box (QOM supports custom freeing functions) but it's a bit
complex because the freeing function would have to free the memory without
dropping the contents of the Box (the drop is done by QOM via
instance_finalize).
If you want to allocate the HPETTimers at realize time, I think you can
place them in a Vec. I think you won't need NonNull for this, but I am not
100% sure. Alternatively if you want to always prepare all MAX_TIMERS of
them and then only use a subset, you can use an array.
Either way, probably it makes sense for you to have an "fn
timers_iter(&self) -> impl Iter<Item = &BqlRefCell<HPETTimer>>" in
HPETState, or something like that. Then you can easily use for loops to
walk all timers between 0 and self.num_timers-1.
Paolo
> and then instance_post_init can run with a &self. Until then, however,
> > instance_init has to remain unsafe.
>
> Thanks for sharing! It's a very reasonable direction.
>
> Regards,
> Zhao
>
>
[-- Attachment #2: Type: text/html, Size: 4292 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-11 9:11 ` Paolo Bonzini
@ 2024-12-11 16:56 ` Zhao Liu
2024-12-12 9:24 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-11 16:56 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> Generally:
>
> - embedded objects will have to be initialized in instance_init unless they
> are Options
I see, at least for HPETTimer array, I need to prepare all of them in
instance_init()...
> - if you don't need properties you can choose between post_init and
> realize, if you need properties you need to initialize in realize (and
> then, unlike C, you might need to explicitly allow the pre-realize state;
> for example using Option<&...> instead of just a reference; or Vec<>
> instead of an array).
...in realize(), also need to handle the "timers" property to enable
the timers that property requires.
> - sysbus_init_irq and sysbus_init_mmio can be called in both instance_init
> and instance_post_init for now, but they will have to be in post_init once
> the signature of init changes to return impl PinInit
make sense, thanks!
> >
> > > The way that this will become safe is to use the pinned_init crate from
> > > Linux: instance_init returns the initialization as an "impl
> > PinInit<Self>",
> >
> > Then do we need to place OBJECT in some suitable memory location (Box or
> > something) for Rust implementation?
> >
>
> Allocation is still done by the C code, so I am not sure I understand the
> question. Rust code allocates QOM objects with object_new() so they are
> malloc-ed.
Sorry, I'm not familiar enough with this piece...I have the question
because PinInit doc said "you will need a suitable memory location that
can hold a T. This can be KBox<T>, Arc<T>, UniqueArc<T> or even the
stack (see stack_pin_init!)."
I see that the core point is ensuring that structures cannot be moved.
Given that object_new() on the C side ensures that the allocated object
will not be moved, Rust side does not need to worry about pinning, correct?
> I discussed it with Manos some time ago and in principle you
> could use a Box (QOM supports custom freeing functions) but it's a bit
> complex because the freeing function would have to free the memory without
> dropping the contents of the Box (the drop is done by QOM via
> instance_finalize).
>
> If you want to allocate the HPETTimers at realize time, I think you can
> place them in a Vec. I think you won't need NonNull for this, but I am not
> 100% sure. Alternatively if you want to always prepare all MAX_TIMERS of
> them and then only use a subset, you can use an array.
Vec seems to lack proper vmstate support. I understand that we need to
modify VMSTATE_STRUCT_VARRAY_POINTER_* to introduce a variant for Vec.
Since the array support is already available, I chose to use an array
instead (although vmstate is disabled for now).
> Either way, probably it makes sense for you to have an "fn
> timers_iter(&self) -> impl Iter<Item = &BqlRefCell<HPETTimer>>" in
> HPETState, or something like that. Then you can easily use for loops to
> walk all timers between 0 and self.num_timers-1.
Good idea, yes, will do.
Thanks,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-11 16:56 ` Zhao Liu
@ 2024-12-12 9:24 ` Paolo Bonzini
2024-12-13 8:53 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-12 9:24 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Wed, Dec 11, 2024 at 5:38 PM Zhao Liu <zhao1.liu@intel.com> wrote:
>
> > Generally:
> >
> > - embedded objects will have to be initialized in instance_init unless they
> > are Options
>
> I see, at least for HPETTimer array, I need to prepare all of them in
> instance_init()...
>
> ...in realize(), also need to handle the "timers" property to enable
> the timers that property requires.
I see -- though, thinking more about it, since you have
fn init_timer(&mut self) {
let raw_ptr: *mut HPETState = self;
for i in 0..HPET_MAX_TIMERS {
let mut timer = self.get_timer(i).borrow_mut();
timer.init(i, raw_ptr).init_timer_with_state();
}
}
It seems to me that you can do everything in instance_init. Later on a
function like the above
will become something like
impl HPETTimer {
fn init_timer(hpet: NonNull<HPETState>, n: usize) -> impl PinInit<Self> {
pin_init!(&this in HPETTimer {
index: n,
qemu_timer <- Timer::init_ns(...),
state: hpet,
config: 0,
cmp: 0,
fsb: 0,
cmp64: 0,
period: 0,
wrap_flag: false,
last: 0,
}
}
}
But even now you can write something that takes a &mut self as the
first argument. It's undefined behavior but it's okay as long as we
have a path forward.
> > > > The way that this will become safe is to use the pinned_init crate from
> > > > Linux: instance_init returns the initialization as an "impl
> > > PinInit<Self>",
> > >
> > > Then do we need to place OBJECT in some suitable memory location (Box or
> > > something) for Rust implementation?
> > >
> >
> > Allocation is still done by the C code, so I am not sure I understand the
> > question. Rust code allocates QOM objects with object_new() so they are
> > malloc-ed.
>
> Sorry, I'm not familiar enough with this piece...I have the question
> because PinInit doc said "you will need a suitable memory location that
> can hold a T. This can be KBox<T>, Arc<T>, UniqueArc<T> or even the
> stack (see stack_pin_init!)."
Ah, I see. You can use __pinned_init directly on the memory that is
passed to rust_instance_init. See for example the implementation of
InPlaceWrite for Box
(https://docs.rs/pinned-init/latest/src/pinned_init/lib.rs.html#1307).
> I see that the core point is ensuring that structures cannot be moved.
> Given that object_new() on the C side ensures that the allocated object
> will not be moved, Rust side does not need to worry about pinning, correct?
Sort of... You still need to worry about it for two reasons:
1) if you have &mut Self you can move values out of the object using
e.g. mem::replace or mem::swap. Those would move the value in memory
and cause trouble (think of moving a QEMUTimer while it is pointed to
by the QEMUTimerList). This is solved by 1) using &Self all the time +
interior mutability 2) using pinned_init's "PinnedDrop" functionality,
because &Self can be used in QEMU-specific APIs but (obviously) not in
the built-in Drop trait.
2) right now marking something as pinned is an indirect way to tell
the compiler and miri that there are external references to it. For a
longer discussion you can read
https://crates.io/crates/pinned-aliasable or
https://gist.github.com/Darksonn/1567538f56af1a8038ecc3c664a42462.
Linux does this with a wrapper type similar to the one in pinned-aliasable:
/// Stores an opaque value.
///
/// This is meant to be used with FFI objects that are never
interpreted by Rust code.
#[repr(transparent)]
pub struct Opaque<T> {
value: UnsafeCell<MaybeUninit<T>>,
_pin: PhantomPinned,
}
It's on my todo list to introduce it in qemu_api::cell and (for
example) change qom::Object from
pub use bindings::Object
to
pub type Object = Opaque<bindings::Object>;
Or something like that.
> Vec seems to lack proper vmstate support. I understand that we need to
> modify VMSTATE_STRUCT_VARRAY_POINTER_* to introduce a variant for Vec.
>
> Since the array support is already available, I chose to use an array
> instead (although vmstate is disabled for now).
Yes, you're right.
Palo
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-12 9:24 ` Paolo Bonzini
@ 2024-12-13 8:53 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-13 8:53 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> I see -- though, thinking more about it, since you have
>
> fn init_timer(&mut self) {
> let raw_ptr: *mut HPETState = self;
>
> for i in 0..HPET_MAX_TIMERS {
> let mut timer = self.get_timer(i).borrow_mut();
> timer.init(i, raw_ptr).init_timer_with_state();
> }
> }
>
> It seems to me that you can do everything in instance_init. Later on a
> function like the above
> will become something like
>
> impl HPETTimer {
> fn init_timer(hpet: NonNull<HPETState>, n: usize) -> impl PinInit<Self> {
Thank you! I should pass NonNull type other than `*mut HPETState` for now :-)
> pin_init!(&this in HPETTimer {
> index: n,
> qemu_timer <- Timer::init_ns(...),
> state: hpet,
> config: 0,
> cmp: 0,
> fsb: 0,
> cmp64: 0,
> period: 0,
> wrap_flag: false,
> last: 0,
> }
> }
> }
That's the right and ideal way, and I like it.
> But even now you can write something that takes a &mut self as the
> first argument. It's undefined behavior but it's okay as long as we
> have a path forward.
Yes, I agree. In the next version, I can follow your suggestion and put
these embedded items into instance_init(), to be better prepared for the
next step.
> > > > > The way that this will become safe is to use the pinned_init crate from
> > > > > Linux: instance_init returns the initialization as an "impl
> > > > PinInit<Self>",
> > > >
> > > > Then do we need to place OBJECT in some suitable memory location (Box or
> > > > something) for Rust implementation?
> > > >
> > >
> > > Allocation is still done by the C code, so I am not sure I understand the
> > > question. Rust code allocates QOM objects with object_new() so they are
> > > malloc-ed.
> >
> > Sorry, I'm not familiar enough with this piece...I have the question
> > because PinInit doc said "you will need a suitable memory location that
> > can hold a T. This can be KBox<T>, Arc<T>, UniqueArc<T> or even the
> > stack (see stack_pin_init!)."
>
> Ah, I see. You can use __pinned_init directly on the memory that is
> passed to rust_instance_init. See for example the implementation of
> InPlaceWrite for Box
> (https://docs.rs/pinned-init/latest/src/pinned_init/lib.rs.html#1307).
Thank you! I understand your intention. A similar implementation would
also be quite natural in rust_instance_init.
> > I see that the core point is ensuring that structures cannot be moved.
> > Given that object_new() on the C side ensures that the allocated object
> > will not be moved, Rust side does not need to worry about pinning, correct?
>
> Sort of... You still need to worry about it for two reasons:
>
> 1) if you have &mut Self you can move values out of the object using
> e.g. mem::replace or mem::swap. Those would move the value in memory
> and cause trouble (think of moving a QEMUTimer while it is pointed to
> by the QEMUTimerList). This is solved by 1) using &Self all the time +
> interior mutability
With your help and through our discussions, I have gained a clearer
understanding of this intention.
> 2) using pinned_init's "PinnedDrop" functionality,
> because &Self can be used in QEMU-specific APIs but (obviously) not in
> the built-in Drop trait.
>
> 2) right now marking something as pinned is an indirect way to tell
> the compiler and miri that there are external references to it. For a
> longer discussion you can read
> https://crates.io/crates/pinned-aliasable or
> https://gist.github.com/Darksonn/1567538f56af1a8038ecc3c664a42462.
Thanks for sharing!
> Linux does this with a wrapper type similar to the one in pinned-aliasable:
>
> /// Stores an opaque value.
> ///
> /// This is meant to be used with FFI objects that are never
> interpreted by Rust code.
> #[repr(transparent)]
> pub struct Opaque<T> {
> value: UnsafeCell<MaybeUninit<T>>,
> _pin: PhantomPinned,
> }
>
> It's on my todo list to introduce it in qemu_api::cell and (for
> example) change qom::Object from
>
> pub use bindings::Object
>
> to
>
> pub type Object = Opaque<bindings::Object>;
>
> Or something like that.
Yes, I agree with this idea. It's what we need.
Regards,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011
2024-12-09 12:37 ` [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011 Paolo Bonzini
2024-12-10 15:50 ` Zhao Liu
@ 2024-12-10 16:02 ` Zhao Liu
1 sibling, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 16:02 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> +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>()) }
> +}
Here's the difference, why doesn't init() narrow the unsafe scope like
post_init() does?
> +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>() })
> +}
> +
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 15/26] rust: qom: split ObjectType from ObjectImpl trait
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (13 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 14/26] rust: qom: move bridge for TypeInfo functions out of pl011 Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-11 8:41 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 16/26] rust: qom: change the parent type to an associated type Paolo Bonzini
` (11 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Define a separate trait for fields that also applies to classes that are
defined by C code. This makes it possible to add metadata to core classes,
which has multiple uses:
- it makes it possible to access the parent struct's TYPE_* for types
that are defined in Rust code, and to avoid repeating it in every subclass
- implementors of ObjectType will be allowed to implement the IsA<> trait and
therefore to perform typesafe casts from one class to another.
- in the future, an ObjectType could be created with Foo::new() in a type-safe
manner, without having to pass a TYPE_* constant.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 17 ++++++++++++-----
rust/qemu-api/src/definitions.rs | 9 ++++++---
rust/qemu-api/src/device_class.rs | 11 ++++++-----
rust/qemu-api/src/prelude.rs | 2 ++
rust/qemu-api/src/sysbus.rs | 10 ++++++++--
rust/qemu-api/tests/tests.rs | 12 +++++++++---
6 files changed, 43 insertions(+), 18 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index b9f8fb134b5..0ab825b1ca4 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -12,9 +12,10 @@
bindings::{self, *},
c_str,
definitions::ObjectImpl,
- device_class::{DeviceImpl, TYPE_SYS_BUS_DEVICE},
+ device_class::DeviceImpl,
impl_device_class,
irq::InterruptSource,
+ prelude::*,
};
use crate::{
@@ -106,10 +107,13 @@ pub struct PL011State {
device_id: DeviceId,
}
-impl ObjectImpl for PL011State {
+unsafe impl ObjectType for PL011State {
type Class = PL011Class;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
- const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
+}
+
+impl ObjectImpl for PL011State {
+ const PARENT_TYPE_NAME: Option<&'static CStr> = Some(<SysBusDevice as ObjectType>::TYPE_NAME);
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
@@ -640,10 +644,13 @@ unsafe fn init(&mut self) {
}
}
-impl ObjectImpl for PL011Luminary {
+unsafe impl ObjectType for PL011Luminary {
type Class = PL011LuminaryClass;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
- const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
+}
+
+impl ObjectImpl for PL011Luminary {
+ const PARENT_TYPE_NAME: Option<&'static CStr> = Some(<PL011State as ObjectType>::TYPE_NAME);
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 078ba30d61a..1c412dbc876 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -33,7 +33,7 @@
unsafe { std::ptr::drop_in_place(obj.cast::<T>()) }
}
-/// Trait a type must implement to be registered with QEMU.
+/// Trait exposed by all structs corresponding to QOM objects.
///
/// # Safety
///
@@ -43,15 +43,18 @@
/// automatic if the class is defined via `ObjectImpl`).
///
/// - the first field of the struct must be of the instance struct corresponding
-/// to the superclass declared as `PARENT_TYPE_NAME`
-pub trait ObjectImpl: ClassInitImpl + Sized {
+/// to the superclass declared in the `TypeInfo`
+pub unsafe trait ObjectType: Sized {
/// The QOM class object corresponding to this struct. Not used yet.
type Class;
/// The name of the type, which can be passed to `object_new()` to
/// generate an instance of this type.
const TYPE_NAME: &'static CStr;
+}
+/// 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`:
const PARENT_TYPE_NAME: Option<&'static CStr>;
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
index f25904be4f6..03d03feee83 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/device_class.rs
@@ -6,6 +6,7 @@
use crate::{
bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
+ prelude::*,
zeroable::Zeroable,
};
@@ -146,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/prelude.rs b/rust/qemu-api/src/prelude.rs
index a39e228babf..1b8677b2d9a 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -6,3 +6,5 @@
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
index 4e192c75898..5ee068541cf 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -2,11 +2,17 @@
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::ptr::addr_of;
+use std::{ffi::CStr, ptr::addr_of};
pub use bindings::{SysBusDevice, SysBusDeviceClass};
-use crate::{bindings, cell::bql_locked, irq::InterruptSource};
+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.
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index b8b12a40422..1be03eb685c 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -8,8 +8,9 @@
bindings::*,
c_str, declare_properties, define_property,
definitions::ObjectImpl,
- device_class::{self, DeviceImpl},
+ device_class::DeviceImpl,
impl_device_class,
+ prelude::*,
zeroable::Zeroable,
};
@@ -46,10 +47,15 @@ pub struct DummyClass {
),
}
- impl ObjectImpl for DummyState {
+ unsafe impl ObjectType for DummyState {
type Class = DummyClass;
const TYPE_NAME: &'static CStr = c_str!("dummy");
- const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
+ }
+
+ impl ObjectImpl for DummyState {
+ const PARENT_TYPE_NAME: Option<&'static CStr> =
+ Some(<DeviceState as ObjectType>::TYPE_NAME);
+ const ABSTRACT: bool = false;
}
impl DeviceImpl for DummyState {
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 15/26] rust: qom: split ObjectType from ObjectImpl trait
2024-12-09 12:37 ` [PATCH 15/26] rust: qom: split ObjectType from ObjectImpl trait Paolo Bonzini
@ 2024-12-11 8:41 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-11 8:41 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:06PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:06 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 15/26] rust: qom: split ObjectType from ObjectImpl trait
> X-Mailer: git-send-email 2.47.1
>
> Define a separate trait for fields that also applies to classes that are
> defined by C code. This makes it possible to add metadata to core classes,
> which has multiple uses:
>
> - it makes it possible to access the parent struct's TYPE_* for types
> that are defined in Rust code, and to avoid repeating it in every subclass
>
> - implementors of ObjectType will be allowed to implement the IsA<> trait and
> therefore to perform typesafe casts from one class to another.
>
> - in the future, an ObjectType could be created with Foo::new() in a type-safe
> manner, without having to pass a TYPE_* constant.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 17 ++++++++++++-----
> rust/qemu-api/src/definitions.rs | 9 ++++++---
> rust/qemu-api/src/device_class.rs | 11 ++++++-----
> rust/qemu-api/src/prelude.rs | 2 ++
> rust/qemu-api/src/sysbus.rs | 10 ++++++++--
> rust/qemu-api/tests/tests.rs | 12 +++++++++---
> 6 files changed, 43 insertions(+), 18 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 16/26] rust: qom: change the parent type to an associated type
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (14 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 15/26] rust: qom: split ObjectType from ObjectImpl trait Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-11 8:47 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 17/26] rust: qom: put class_init together from multiple ClassInitImpl<> Paolo Bonzini
` (10 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Avoid duplicated code to retrieve the QOM type strings from the
Rust type.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 6 ++++--
rust/qemu-api/src/definitions.rs | 8 ++------
rust/qemu-api/tests/tests.rs | 3 +--
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 0ab825b1ca4..3e29442a625 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -113,7 +113,8 @@ unsafe impl ObjectType for PL011State {
}
impl ObjectImpl for PL011State {
- const PARENT_TYPE_NAME: Option<&'static CStr> = Some(<SysBusDevice as ObjectType>::TYPE_NAME);
+ type ParentType = SysBusDevice;
+
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
@@ -650,7 +651,8 @@ unsafe impl ObjectType for PL011Luminary {
}
impl ObjectImpl for PL011Luminary {
- const PARENT_TYPE_NAME: Option<&'static CStr> = Some(<PL011State as ObjectType>::TYPE_NAME);
+ type ParentType = PL011State;
+
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 1c412dbc876..1975ad91867 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -57,7 +57,7 @@ pub unsafe trait ObjectType: Sized {
pub trait ObjectImpl: ObjectType + ClassInitImpl {
/// The parent of the type. This should match the first field of
/// the struct that implements `ObjectImpl`:
- const PARENT_TYPE_NAME: Option<&'static CStr>;
+ type ParentType: ObjectType;
/// Whether the object can be instantiated
const ABSTRACT: bool = false;
@@ -76,11 +76,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl {
const TYPE_INFO: TypeInfo = TypeInfo {
name: Self::TYPE_NAME.as_ptr(),
- parent: if let Some(pname) = Self::PARENT_TYPE_NAME {
- pname.as_ptr()
- } else {
- core::ptr::null_mut()
- },
+ 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 {
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 1be03eb685c..2a72b0f9276 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -53,8 +53,7 @@ unsafe impl ObjectType for DummyState {
}
impl ObjectImpl for DummyState {
- const PARENT_TYPE_NAME: Option<&'static CStr> =
- Some(<DeviceState as ObjectType>::TYPE_NAME);
+ type ParentType = DeviceState;
const ABSTRACT: bool = false;
}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 16/26] rust: qom: change the parent type to an associated type
2024-12-09 12:37 ` [PATCH 16/26] rust: qom: change the parent type to an associated type Paolo Bonzini
@ 2024-12-11 8:47 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-11 8:47 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:07PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:07 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 16/26] rust: qom: change the parent type to an associated
> type
> X-Mailer: git-send-email 2.47.1
>
> Avoid duplicated code to retrieve the QOM type strings from the
> Rust type.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 6 ++++--
> rust/qemu-api/src/definitions.rs | 8 ++------
> rust/qemu-api/tests/tests.rs | 3 +--
> 3 files changed, 7 insertions(+), 10 deletions(-)
>
...
> @@ -76,11 +76,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl {
>
> const TYPE_INFO: TypeInfo = TypeInfo {
> name: Self::TYPE_NAME.as_ptr(),
> - parent: if let Some(pname) = Self::PARENT_TYPE_NAME {
> - pname.as_ptr()
> - } else {
> - core::ptr::null_mut()
> - },
> + parent: Self::ParentType::TYPE_NAME.as_ptr(),
Object is implemented at C side, so at leaset the ParentType should be
TYPE_OBJECT.
> instance_size: core::mem::size_of::<Self>(),
> instance_align: core::mem::align_of::<Self>(),
> instance_init: match Self::INSTANCE_INIT {
Great!
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 17/26] rust: qom: put class_init together from multiple ClassInitImpl<>
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (15 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 16/26] rust: qom: change the parent type to an associated type Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-12 9:15 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 18/26] rust: qom: add possibility of overriding unparent Paolo Bonzini
` (9 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Parameterize the implementation of ClassInitImpl so that it is
possible to call up the chain of implementations, one superclass at
a time starting at ClassInitImpl<Self::Class>.
In order to avoid having to implement (for example)
ClassInitImpl<PL011Class>, also remove the dummy PL011Class and
PL011LuminaryClass structs and specify the same ObjectType::Class as
the superclass. In the future this default behavior can be handled by
a procedural macro, by looking at the first field in the struct.
Note that the new trait is safe: the calls are started by
rust_class_init<>(), which is not public and can convert the class
pointer to a Rust reference.
Since CLASS_BASE_INIT applies to the type that is being defined,
and only to it, move it to ObjectImpl.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 19 +------
rust/qemu-api/src/definitions.rs | 85 +++++++++++++++++++++++--------
rust/qemu-api/src/device_class.rs | 50 +++++++-----------
rust/qemu-api/src/sysbus.rs | 18 ++++++-
rust/qemu-api/tests/tests.rs | 9 +---
5 files changed, 101 insertions(+), 80 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 3e29442a625..d9e9f35f456 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -13,7 +13,6 @@
c_str,
definitions::ObjectImpl,
device_class::DeviceImpl,
- impl_device_class,
irq::InterruptSource,
prelude::*,
};
@@ -108,7 +107,7 @@ pub struct PL011State {
}
unsafe impl ObjectType for PL011State {
- type Class = PL011Class;
+ type Class = <SysBusDevice as ObjectType>::Class;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
}
@@ -118,11 +117,6 @@ impl ObjectImpl for PL011State {
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
-#[repr(C)]
-pub struct PL011Class {
- _inner: [u8; 0],
-}
-
impl DeviceImpl for PL011State {
fn properties() -> &'static [Property] {
&device_class::PL011_PROPERTIES
@@ -134,8 +128,6 @@ fn vmsd() -> Option<&'static VMStateDescription> {
const RESET: Option<fn(&mut Self)> = Some(Self::reset);
}
-impl_device_class!(PL011State);
-
impl PL011State {
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
///
@@ -627,11 +619,6 @@ pub struct PL011Luminary {
parent_obj: PL011State,
}
-#[repr(C)]
-pub struct PL011LuminaryClass {
- _inner: [u8; 0],
-}
-
impl PL011Luminary {
/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
///
@@ -646,7 +633,7 @@ unsafe fn init(&mut self) {
}
unsafe impl ObjectType for PL011Luminary {
- type Class = PL011LuminaryClass;
+ type Class = <PL011State as ObjectType>::Class;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
}
@@ -657,5 +644,3 @@ impl ObjectImpl for PL011Luminary {
}
impl DeviceImpl for PL011Luminary {}
-
-impl_device_class!(PL011Luminary);
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 1975ad91867..2429b9f53f0 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -33,6 +33,16 @@
unsafe { std::ptr::drop_in_place(obj.cast::<T>()) }
}
+unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>(
+ klass: *mut ObjectClass,
+ _data: *mut c_void,
+) {
+ // SAFETY: klass is a T::Class, since rust_class_init<T>
+ // is called from QOM core as the class_init function
+ // for class T
+ T::class_init(unsafe { &mut *klass.cast::<T::Class>() })
+}
+
/// Trait exposed by all structs corresponding to QOM objects.
///
/// # Safety
@@ -45,7 +55,8 @@
/// - the first field of the struct must be of the instance struct corresponding
/// to the superclass declared in the `TypeInfo`
pub unsafe trait ObjectType: Sized {
- /// The QOM class object corresponding to this struct. Not used yet.
+ /// The QOM class object corresponding to this struct. This is used
+ /// to automatically generate a `class_init` method.
type Class;
/// The name of the type, which can be passed to `object_new()` to
@@ -54,7 +65,7 @@ pub unsafe trait ObjectType: Sized {
}
/// Trait a type must implement to be registered with QEMU.
-pub trait ObjectImpl: ObjectType + ClassInitImpl {
+pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// The parent of the type. This should match the first field of
/// the struct that implements `ObjectImpl`:
type ParentType: ObjectType;
@@ -74,6 +85,15 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl {
/// `INSTANCE_INIT` functions have been called.
const INSTANCE_POST_INIT: Option<fn(&mut Self)> = None;
+ /// 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),
+ > = None;
+
const TYPE_INFO: TypeInfo = TypeInfo {
name: Self::TYPE_NAME.as_ptr(),
parent: Self::ParentType::TYPE_NAME.as_ptr(),
@@ -90,37 +110,60 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl {
instance_finalize: Some(drop_object::<Self>),
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_init: Some(rust_class_init::<Self>),
+ class_base_init: Self::CLASS_BASE_INIT,
class_data: core::ptr::null_mut(),
interfaces: core::ptr::null_mut(),
};
}
-/// Trait used to fill in a class struct.
+/// Internal trait used to automatically 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.
+/// Each QOM type has one such class struct; this trait takes care of
+/// initializing the `T` part of the class struct, for the type that
+/// implements the trait.
///
-/// 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
+/// Each struct will implement this trait with `T` equal to each
+/// superclass. For example, a device should implement at least
+/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>`.
+///
+/// Fortunately, this is almost never necessary. Instead, the Rust
+/// implementation of methods will usually come from a trait like
+/// [`ObjectImpl`] or [`DeviceImpl`](crate::device_class::DeviceImpl).
+/// `ClassInitImpl` then can be provided by blanket implementations
+/// that operate on all implementors of the `*Impl`* trait. For example:
+///
+/// ```ignore
+/// impl<T> ClassInitImpl<DeviceClass> for T
+/// where
+/// T: DeviceImpl,
+/// ```
+///
+/// The only case in which a manual implementation of the trait is needed
+/// is for interfaces (note that there is no Rust example yet for using
+/// interfaces). In this case, unlike the C case, the Rust class _has_
+/// to define its own class struct `FooClass` to go together with
+/// `ClassInitImpl<FooClass>`.
+pub trait ClassInitImpl<T> {
+ /// Initialize `klass` to point to the virtual method implementations
+ /// for `Self`. 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),
- >;
+ ///
+ /// The virtual method implementations usually come from another
+ /// trait, for example [`DeviceImpl`](crate::device_class::DeviceImpl)
+ /// when `T` is [`DeviceClass`](crate::bindings::DeviceClass).
+ ///
+ /// On entry, `klass`'s parent class is initialized, while the other fields
+ /// are all zero; it is therefore assumed that all fields in `T` can be
+ /// zeroed, otherwise it would not be possible to provide the class as a
+ /// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable)
+ /// to T; this is more easily done once Zeroable does not require a manual
+ /// implementation (Rust 1.75.0).
+ fn class_init(klass: &mut T);
}
#[macro_export]
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
index 03d03feee83..ee9ae7eeb74 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/device_class.rs
@@ -2,10 +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 crate::{
- bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
+ bindings::{self, DeviceClass, DeviceState, Error, Property, VMStateDescription},
+ definitions::ClassInitImpl,
prelude::*,
zeroable::Zeroable,
};
@@ -45,7 +46,7 @@ fn vmsd() -> Option<&'static VMStateDescription> {
/// # Safety
///
/// This function is only called through the QOM machinery and
-/// the `impl_device_class!` macro.
+/// used by the `ClassInitImpl<DeviceClass>` trait.
/// 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.
@@ -66,43 +67,26 @@ fn vmsd() -> Option<&'static VMStateDescription> {
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();
+impl<T> ClassInitImpl<DeviceClass> for T
+where
+ T: 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() {
- bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::<T>));
+ unsafe {
+ 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! 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;
+ unsafe {
+ bindings::device_class_set_props(dc, <T as DeviceImpl>::properties().as_ptr());
}
- };
+ }
}
#[macro_export]
@@ -147,8 +131,8 @@ macro_rules! declare_properties {
};
}
-unsafe impl ObjectType for bindings::DeviceState {
- type Class = bindings::DeviceClass;
+unsafe impl ObjectType for DeviceState {
+ type Class = DeviceClass;
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
}
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index 5ee068541cf..5d15b317405 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -6,7 +6,13 @@
pub use bindings::{SysBusDevice, SysBusDeviceClass};
-use crate::{bindings, cell::bql_locked, irq::InterruptSource, prelude::*};
+use crate::{
+ bindings::{self, DeviceClass},
+ cell::bql_locked,
+ definitions::ClassInitImpl,
+ irq::InterruptSource,
+ prelude::*,
+};
unsafe impl ObjectType for SysBusDevice {
type Class = SysBusDeviceClass;
@@ -14,6 +20,16 @@ unsafe impl ObjectType for SysBusDevice {
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
}
+// TODO: add SysBusDeviceImpl
+impl<T> ClassInitImpl<SysBusDeviceClass> for T
+where
+ T: ClassInitImpl<DeviceClass>,
+{
+ fn class_init(sdc: &mut SysBusDeviceClass) {
+ <T as ClassInitImpl<DeviceClass>>::class_init(&mut sdc.parent_class);
+ }
+}
+
impl SysBusDevice {
/// Return `self` cast to a mutable pointer, for use in calls to C code.
const fn as_mut_ptr(&self) -> *mut SysBusDevice {
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 2a72b0f9276..4ce1bd19247 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -31,11 +31,6 @@ pub struct DummyState {
pub migrate_clock: bool,
}
- #[repr(C)]
- pub struct DummyClass {
- pub _parent: DeviceClass,
- }
-
declare_properties! {
DUMMY_PROPERTIES,
define_property!(
@@ -48,7 +43,7 @@ pub struct DummyClass {
}
unsafe impl ObjectType for DummyState {
- type Class = DummyClass;
+ type Class = <DeviceState as ObjectType>::Class;
const TYPE_NAME: &'static CStr = c_str!("dummy");
}
@@ -66,8 +61,6 @@ fn vmsd() -> Option<&'static VMStateDescription> {
}
}
- impl_device_class!(DummyState);
-
unsafe {
module_call_init(module_init_type::MODULE_INIT_QOM);
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 17/26] rust: qom: put class_init together from multiple ClassInitImpl<>
2024-12-09 12:37 ` [PATCH 17/26] rust: qom: put class_init together from multiple ClassInitImpl<> Paolo Bonzini
@ 2024-12-12 9:15 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-12 9:15 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> Since CLASS_BASE_INIT applies to the type that is being defined,
> and only to it, move it to ObjectImpl.
It makes sense since class_base_init() is used when subclass is being
initialized.
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 19 +------
> rust/qemu-api/src/definitions.rs | 85 +++++++++++++++++++++++--------
> rust/qemu-api/src/device_class.rs | 50 +++++++-----------
> rust/qemu-api/src/sysbus.rs | 18 ++++++-
> rust/qemu-api/tests/tests.rs | 9 +---
> 5 files changed, 101 insertions(+), 80 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 18/26] rust: qom: add possibility of overriding unparent
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (16 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 17/26] rust: qom: put class_init together from multiple ClassInitImpl<> Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-12 9:40 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more Paolo Bonzini
` (8 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Add a blanket definition of ClassInitImpl<ObjectClass> that thunks
ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not
None.
ClassInitImpl<DeviceClass> can now call its superclass's ClassInitImpl,
so that the C and Rust hierarchies match more closely.
This is mostly done as an example of implementing the metaclass
hierarchy under ClassInitImpl.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/src/definitions.rs | 44 ++++++++++++++++++++++++++++---
rust/qemu-api/src/device_class.rs | 6 +++--
2 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
index 2429b9f53f0..958ea34decc 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/definitions.rs
@@ -6,7 +6,7 @@
use std::{ffi::CStr, os::raw::c_void};
-use crate::bindings::{Object, ObjectClass, TypeInfo};
+use crate::bindings::{self, Object, ObjectClass, TypeInfo};
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
// SAFETY: obj is an instance of T, since rust_instance_init<T>
@@ -115,6 +115,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
class_data: core::ptr::null_mut(),
interfaces: core::ptr::null_mut(),
};
+
+ // methods on ObjectClass
+ const UNPARENT: Option<fn(&mut Self)> = None;
}
/// Internal trait used to automatically fill in a class struct.
@@ -128,7 +131,8 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
///
/// Each struct will implement this trait with `T` equal to each
/// superclass. For example, a device should implement at least
-/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>`.
+/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>` and
+/// `ClassInitImpl<`[`ObjectClass`](crate::bindings::ObjectClass)`>`.
///
/// Fortunately, this is almost never necessary. Instead, the Rust
/// implementation of methods will usually come from a trait like
@@ -139,9 +143,13 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// ```ignore
/// impl<T> ClassInitImpl<DeviceClass> for T
/// where
-/// T: DeviceImpl,
+/// T: ClassInitImpl<ObjectClass> + DeviceImpl,
/// ```
///
+/// The bound on `ClassInitImpl<ObjectClass>` is needed so that,
+/// after initializing the `DeviceClass` part of the class struct,
+/// the parent [`ObjectClass`] is initialized as well.
+///
/// The only case in which a manual implementation of the trait is needed
/// is for interfaces (note that there is no Rust example yet for using
/// interfaces). In this case, unlike the C case, the Rust class _has_
@@ -203,3 +211,33 @@ extern "C" fn ctor_fn() {
}
};
}
+
+/// # 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_unparent_fn<T: ObjectImpl>(dev: *mut Object) {
+ unsafe {
+ assert!(!dev.is_null());
+ let mut state = core::ptr::NonNull::new_unchecked(dev.cast::<T>());
+ T::UNPARENT.unwrap()(state.as_mut());
+ }
+}
+
+impl<T> ClassInitImpl<ObjectClass> for T
+where
+ T: ObjectImpl,
+{
+ fn class_init(oc: &mut ObjectClass) {
+ if <T as ObjectImpl>::UNPARENT.is_some() {
+ oc.unparent = Some(rust_unparent_fn::<T>);
+ }
+ }
+}
+
+unsafe impl ObjectType for Object {
+ type Class = ObjectClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) };
+}
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
index ee9ae7eeb74..285dfe582c7 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/device_class.rs
@@ -5,7 +5,7 @@
use std::ffi::CStr;
use crate::{
- bindings::{self, DeviceClass, DeviceState, Error, Property, VMStateDescription},
+ bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
definitions::ClassInitImpl,
prelude::*,
zeroable::Zeroable,
@@ -69,7 +69,7 @@ fn vmsd() -> Option<&'static VMStateDescription> {
impl<T> ClassInitImpl<DeviceClass> for T
where
- T: DeviceImpl,
+ T: ClassInitImpl<ObjectClass> + DeviceImpl,
{
fn class_init(dc: &mut DeviceClass) {
if <T as DeviceImpl>::REALIZE.is_some() {
@@ -86,6 +86,8 @@ fn class_init(dc: &mut DeviceClass) {
unsafe {
bindings::device_class_set_props(dc, <T as DeviceImpl>::properties().as_ptr());
}
+
+ <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class);
}
}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 18/26] rust: qom: add possibility of overriding unparent
2024-12-09 12:37 ` [PATCH 18/26] rust: qom: add possibility of overriding unparent Paolo Bonzini
@ 2024-12-12 9:40 ` Zhao Liu
2024-12-12 11:15 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-12 9:40 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:09PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:09 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 18/26] rust: qom: add possibility of overriding unparent
> X-Mailer: git-send-email 2.47.1
>
> Add a blanket definition of ClassInitImpl<ObjectClass> that thunks
> ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not
> None.
>
> ClassInitImpl<DeviceClass> can now call its superclass's ClassInitImpl,
> so that the C and Rust hierarchies match more closely.
>
> This is mostly done as an example of implementing the metaclass
> hierarchy under ClassInitImpl.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/qemu-api/src/definitions.rs | 44 ++++++++++++++++++++++++++++---
> rust/qemu-api/src/device_class.rs | 6 +++--
> 2 files changed, 45 insertions(+), 5 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
(with an additional comment below...)
> diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
> index 2429b9f53f0..958ea34decc 100644
> --- a/rust/qemu-api/src/definitions.rs
> +++ b/rust/qemu-api/src/definitions.rs
> @@ -6,7 +6,7 @@
>
> use std::{ffi::CStr, os::raw::c_void};
>
> -use crate::bindings::{Object, ObjectClass, TypeInfo};
> +use crate::bindings::{self, Object, ObjectClass, TypeInfo};
>
> unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
> // SAFETY: obj is an instance of T, since rust_instance_init<T>
> @@ -115,6 +115,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
> class_data: core::ptr::null_mut(),
> interfaces: core::ptr::null_mut(),
> };
> +
> + // methods on ObjectClass
> + const UNPARENT: Option<fn(&mut Self)> = None;
> }
Will we change `&mut Self` to an immutable reference in the future?
IIUC, the parent on the C side also has a pointer to current object.
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 18/26] rust: qom: add possibility of overriding unparent
2024-12-12 9:40 ` Zhao Liu
@ 2024-12-12 11:15 ` Paolo Bonzini
0 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-12 11:15 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On 12/12/24 10:40, Zhao Liu wrote:
> On Mon, Dec 09, 2024 at 01:37:09PM +0100, Paolo Bonzini wrote:
>> Date: Mon, 9 Dec 2024 13:37:09 +0100
>> From: Paolo Bonzini <pbonzini@redhat.com>
>> Subject: [PATCH 18/26] rust: qom: add possibility of overriding unparent
>> X-Mailer: git-send-email 2.47.1
>>
>> Add a blanket definition of ClassInitImpl<ObjectClass> that thunks
>> ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not
>> None.
>>
>> ClassInitImpl<DeviceClass> can now call its superclass's ClassInitImpl,
>> so that the C and Rust hierarchies match more closely.
>>
>> This is mostly done as an example of implementing the metaclass
>> hierarchy under ClassInitImpl.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> ---
>> rust/qemu-api/src/definitions.rs | 44 ++++++++++++++++++++++++++++---
>> rust/qemu-api/src/device_class.rs | 6 +++--
>> 2 files changed, 45 insertions(+), 5 deletions(-)
>
> Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
>
> (with an additional comment below...)
>
>> diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
>> index 2429b9f53f0..958ea34decc 100644
>> --- a/rust/qemu-api/src/definitions.rs
>> +++ b/rust/qemu-api/src/definitions.rs
>> @@ -6,7 +6,7 @@
>>
>> use std::{ffi::CStr, os::raw::c_void};
>>
>> -use crate::bindings::{Object, ObjectClass, TypeInfo};
>> +use crate::bindings::{self, Object, ObjectClass, TypeInfo};
>>
>> unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
>> // SAFETY: obj is an instance of T, since rust_instance_init<T>
>> @@ -115,6 +115,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
>> class_data: core::ptr::null_mut(),
>> interfaces: core::ptr::null_mut(),
>> };
>> +
>> + // methods on ObjectClass
>> + const UNPARENT: Option<fn(&mut Self)> = None;
>> }
>
> Will we change `&mut Self` to an immutable reference in the future?
Good point, let's do it now since anyway UNPARENT is unused.
Paolo
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (17 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 18/26] rust: qom: add possibility of overriding unparent Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-12 9:52 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 20/26] rust: re-export C types from qemu-api submodules Paolo Bonzini
` (7 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
A full match would mean calling them qom::object and hw::core::qdev. For now,
keep the names shorter but still a bit easier to find.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 4 +-
rust/qemu-api-macros/src/lib.rs | 2 +-
rust/qemu-api/meson.build | 5 +-
rust/qemu-api/src/lib.rs | 5 +-
rust/qemu-api/src/module.rs | 43 +++++++++++
rust/qemu-api/src/prelude.rs | 2 +-
.../qemu-api/src/{device_class.rs => qdev.rs} | 4 +-
rust/qemu-api/src/{definitions.rs => qom.rs} | 74 +++++++++----------
rust/qemu-api/src/sysbus.rs | 2 +-
rust/qemu-api/tests/tests.rs | 5 +-
10 files changed, 92 insertions(+), 54 deletions(-)
create mode 100644 rust/qemu-api/src/module.rs
rename rust/qemu-api/src/{device_class.rs => qdev.rs} (97%)
rename rust/qemu-api/src/{definitions.rs => qom.rs} (83%)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index d9e9f35f456..3fed8b4ad25 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -11,10 +11,10 @@
use qemu_api::{
bindings::{self, *},
c_str,
- definitions::ObjectImpl,
- device_class::DeviceImpl,
irq::InterruptSource,
prelude::*,
+ qdev::DeviceImpl,
+ qom::ObjectImpl,
};
use crate::{
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index cf99ac04b8f..74a8bc7503e 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -40,7 +40,7 @@ pub fn derive_object(input: TokenStream) -> TokenStream {
let expanded = quote! {
::qemu_api::module_init! {
MODULE_INIT_QOM => unsafe {
- ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::definitions::ObjectImpl>::TYPE_INFO);
+ ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
}
}
};
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index adcee661150..7ff408ad68e 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -19,11 +19,12 @@ _qemu_api_rs = static_library(
'src/bitops.rs',
'src/cell.rs',
'src/c_str.rs',
- 'src/definitions.rs',
- 'src/device_class.rs',
'src/irq.rs',
+ 'src/module.rs',
'src/offset_of.rs',
'src/prelude.rs',
+ 'src/qdev.rs',
+ 'src/qom.rs',
'src/sysbus.rs',
'src/vmstate.rs',
'src/zeroable.rs',
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 9e007e16354..124bece0449 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -15,10 +15,11 @@
pub mod bitops;
pub mod c_str;
pub mod cell;
-pub mod definitions;
-pub mod device_class;
pub mod irq;
+pub mod module;
pub mod offset_of;
+pub mod qdev;
+pub mod qom;
pub mod sysbus;
pub mod vmstate;
pub mod zeroable;
diff --git a/rust/qemu-api/src/module.rs b/rust/qemu-api/src/module.rs
new file mode 100644
index 00000000000..fa5cea3598f
--- /dev/null
+++ b/rust/qemu-api/src/module.rs
@@ -0,0 +1,43 @@
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Macro to register blocks of code that run as QEMU starts up.
+
+#[macro_export]
+macro_rules! module_init {
+ ($type:ident => $body:block) => {
+ const _: () = {
+ #[used]
+ #[cfg_attr(
+ not(any(target_vendor = "apple", target_os = "windows")),
+ link_section = ".init_array"
+ )]
+ #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
+ #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
+ pub static LOAD_MODULE: extern "C" fn() = {
+ extern "C" fn init_fn() {
+ $body
+ }
+
+ extern "C" fn ctor_fn() {
+ unsafe {
+ $crate::bindings::register_module_init(
+ Some(init_fn),
+ $crate::bindings::module_init_type::$type,
+ );
+ }
+ }
+
+ ctor_fn
+ };
+ };
+ };
+
+ // shortcut because it's quite common that $body needs unsafe {}
+ ($type:ident => unsafe $body:block) => {
+ $crate::module_init! {
+ $type => { unsafe { $body } }
+ }
+ };
+}
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 1b8677b2d9a..5cc41f081f9 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -7,4 +7,4 @@
pub use crate::cell::BqlCell;
pub use crate::cell::BqlRefCell;
-pub use crate::definitions::ObjectType;
+pub use crate::qom::ObjectType;
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/qdev.rs
similarity index 97%
rename from rust/qemu-api/src/device_class.rs
rename to rust/qemu-api/src/qdev.rs
index 285dfe582c7..1228dabaaaf 100644
--- a/rust/qemu-api/src/device_class.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -2,12 +2,14 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
+//! Bindings to create devices and access device functionality from Rust.
+
use std::ffi::CStr;
use crate::{
bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
- definitions::ClassInitImpl,
prelude::*,
+ qom::ClassInitImpl,
zeroable::Zeroable,
};
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/qom.rs
similarity index 83%
rename from rust/qemu-api/src/definitions.rs
rename to rust/qemu-api/src/qom.rs
index 958ea34decc..9b316e07efa 100644
--- a/rust/qemu-api/src/definitions.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -2,7 +2,37 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-//! Definitions required by QEMU when registering a device.
+//! Bindings to access QOM functionality from Rust.
+//!
+//! This module provides automatic creation and registration of `TypeInfo`
+//! for classes that are written in Rust, and mapping between Rust traits
+//! and QOM vtables.
+//!
+//! # Structure of a class
+//!
+//! A concrete class only needs a struct holding instance state. The struct must
+//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl`
+//! traits provided by its superclasses.
+//!
+//! An abstract class will also provide a struct for instance data, with the
+//! same characteristics as for concrete classes, but it also needs additional
+//! components to support virtual methods:
+//!
+//! * a struct for instance data, with the same characteristics as for concrete
+//! classes.
+//!
+//! * a struct for class data, for example `DeviceClass`. This corresponds to
+//! the C "class struct" and holds the vtable that is used by instances of the
+//! class and its subclasses. It must start with its parent's class struct.
+//!
+//! * a trait for virtual method implementations, for example `DeviceImpl`.
+//! Child classes implement this trait to provide their own behavior for
+//! virtual methods. The trait's methods take `&self` to access instance data.
+//!
+//! * an implementation of [`ClassInitImpl`], for example
+//! `ClassInitImpl<DeviceClass>`. This fills the vtable in the class struct,
+//! typically with wrappers that call into the
+//! [`DeviceImpl`](crate::qdev::DeviceImpl) implementations.
use std::{ffi::CStr, os::raw::c_void};
@@ -136,7 +166,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
///
/// Fortunately, this is almost never necessary. Instead, the Rust
/// implementation of methods will usually come from a trait like
-/// [`ObjectImpl`] or [`DeviceImpl`](crate::device_class::DeviceImpl).
+/// [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl).
/// `ClassInitImpl` then can be provided by blanket implementations
/// that operate on all implementors of the `*Impl`* trait. For example:
///
@@ -162,7 +192,7 @@ pub trait ClassInitImpl<T> {
/// can change them to override virtual methods of a parent class.
///
/// The virtual method implementations usually come from another
- /// trait, for example [`DeviceImpl`](crate::device_class::DeviceImpl)
+ /// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl)
/// when `T` is [`DeviceClass`](crate::bindings::DeviceClass).
///
/// On entry, `klass`'s parent class is initialized, while the other fields
@@ -174,44 +204,6 @@ pub trait ClassInitImpl<T> {
fn class_init(klass: &mut T);
}
-#[macro_export]
-macro_rules! module_init {
- ($type:ident => $body:block) => {
- const _: () = {
- #[used]
- #[cfg_attr(
- not(any(target_vendor = "apple", target_os = "windows")),
- link_section = ".init_array"
- )]
- #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
- #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
- pub static LOAD_MODULE: extern "C" fn() = {
- extern "C" fn init_fn() {
- $body
- }
-
- extern "C" fn ctor_fn() {
- unsafe {
- $crate::bindings::register_module_init(
- Some(init_fn),
- $crate::bindings::module_init_type::$type,
- );
- }
- }
-
- ctor_fn
- };
- };
- };
-
- // shortcut because it's quite common that $body needs unsafe {}
- ($type:ident => unsafe $body:block) => {
- $crate::module_init! {
- $type => { unsafe { $body } }
- }
- };
-}
-
/// # Safety
///
/// We expect the FFI user of this function to pass a valid pointer that
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index 5d15b317405..fa69cadd7c1 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -9,9 +9,9 @@
use crate::{
bindings::{self, DeviceClass},
cell::bql_locked,
- definitions::ClassInitImpl,
irq::InterruptSource,
prelude::*,
+ qom::ClassInitImpl,
};
unsafe impl ObjectType for SysBusDevice {
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 4ce1bd19247..fc57eb81290 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -7,10 +7,9 @@
use qemu_api::{
bindings::*,
c_str, declare_properties, define_property,
- definitions::ObjectImpl,
- device_class::DeviceImpl,
- impl_device_class,
prelude::*,
+ qdev::DeviceImpl,
+ qom::ObjectImpl,
zeroable::Zeroable,
};
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more
2024-12-09 12:37 ` [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more Paolo Bonzini
@ 2024-12-12 9:52 ` Zhao Liu
2024-12-12 11:28 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-12 9:52 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:10PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:10 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit
> more
> X-Mailer: git-send-email 2.47.1
>
> A full match would mean calling them qom::object and hw::core::qdev. For now,
> keep the names shorter but still a bit easier to find.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 4 +-
> rust/qemu-api-macros/src/lib.rs | 2 +-
> rust/qemu-api/meson.build | 5 +-
> rust/qemu-api/src/lib.rs | 5 +-
> rust/qemu-api/src/module.rs | 43 +++++++++++
> rust/qemu-api/src/prelude.rs | 2 +-
> .../qemu-api/src/{device_class.rs => qdev.rs} | 4 +-
> rust/qemu-api/src/{definitions.rs => qom.rs} | 74 +++++++++----------
> rust/qemu-api/src/sysbus.rs | 2 +-
> rust/qemu-api/tests/tests.rs | 5 +-
> 10 files changed, 92 insertions(+), 54 deletions(-)
> create mode 100644 rust/qemu-api/src/module.rs
> rename rust/qemu-api/src/{device_class.rs => qdev.rs} (97%)
> rename rust/qemu-api/src/{definitions.rs => qom.rs} (83%)
>
> --- a/rust/qemu-api/src/definitions.rs
> +++ b/rust/qemu-api/src/qom.rs
> @@ -2,7 +2,37 @@
> // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> // SPDX-License-Identifier: GPL-2.0-or-later
>
> -//! Definitions required by QEMU when registering a device.
> +//! Bindings to access QOM functionality from Rust.
> +//!
> +//! This module provides automatic creation and registration of `TypeInfo`
> +//! for classes that are written in Rust, and mapping between Rust traits
> +//! and QOM vtables.
> +//!
> +//! # Structure of a class
> +//!
> +//! A concrete class only needs a struct holding instance state. The struct must
> +//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl`
> +//! traits provided by its superclasses.
In this commit, this comment is a bit ahead, but I think it's okay.
qom and qdev are both good names. In addition, we can rename the files
of PL011 as well. Perhaps device_class.rs could be merged into device.rs
(and eventually renamed to pl011.rs). I guess you might be planning to
keep it until the cleanup of vmstate and property is done.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more
2024-12-12 9:52 ` Zhao Liu
@ 2024-12-12 11:28 ` Paolo Bonzini
2024-12-13 9:19 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-12 11:28 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On 12/12/24 10:52, Zhao Liu wrote:
> On Mon, Dec 09, 2024 at 01:37:10PM +0100, Paolo Bonzini wrote:
>> Date: Mon, 9 Dec 2024 13:37:10 +0100
>> From: Paolo Bonzini <pbonzini@redhat.com>
>> Subject: [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit
>> more
>> X-Mailer: git-send-email 2.47.1
>>
>> A full match would mean calling them qom::object and hw::core::qdev. For now,
>> keep the names shorter but still a bit easier to find.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> ---
>> rust/hw/char/pl011/src/device.rs | 4 +-
>> rust/qemu-api-macros/src/lib.rs | 2 +-
>> rust/qemu-api/meson.build | 5 +-
>> rust/qemu-api/src/lib.rs | 5 +-
>> rust/qemu-api/src/module.rs | 43 +++++++++++
>> rust/qemu-api/src/prelude.rs | 2 +-
>> .../qemu-api/src/{device_class.rs => qdev.rs} | 4 +-
>> rust/qemu-api/src/{definitions.rs => qom.rs} | 74 +++++++++----------
>> rust/qemu-api/src/sysbus.rs | 2 +-
>> rust/qemu-api/tests/tests.rs | 5 +-
>> 10 files changed, 92 insertions(+), 54 deletions(-)
>> create mode 100644 rust/qemu-api/src/module.rs
>> rename rust/qemu-api/src/{device_class.rs => qdev.rs} (97%)
>> rename rust/qemu-api/src/{definitions.rs => qom.rs} (83%)
>>
>> --- a/rust/qemu-api/src/definitions.rs
>> +++ b/rust/qemu-api/src/qom.rs
>> @@ -2,7 +2,37 @@
>> // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
>> // SPDX-License-Identifier: GPL-2.0-or-later
>>
>> -//! Definitions required by QEMU when registering a device.
>> +//! Bindings to access QOM functionality from Rust.
>> +//!
>> +//! This module provides automatic creation and registration of `TypeInfo`
>> +//! for classes that are written in Rust, and mapping between Rust traits
>> +//! and QOM vtables.
>> +//!
>> +//! # Structure of a class
>> +//!
>> +//! A concrete class only needs a struct holding instance state. The struct must
>> +//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl`
>> +//! traits provided by its superclasses.
>
> In this commit, this comment is a bit ahead, but I think it's okay.
>
> qom and qdev are both good names. In addition, we can rename the files
> of PL011 as well. Perhaps device_class.rs could be merged into device.rs
> (and eventually renamed to pl011.rs). I guess you might be planning to
> keep it until the cleanup of vmstate and property is done.
Yeah, I don't have any specific plans but memory_ops will certainly go
away. device_class doesn't do much, but keeping it separate is a
reminder for things that are still there to be cleaned up.
As to VMState, there are two parts. One is the vmstate_description
macro, probably it has to be replaced with something else to incorporate
the trampolines for pre_save/post_load/... I haven't looked at it but
it should not be a lot of work.
The second is VMStateFields, for which my idea is to implement a trait
on types to retrieve a basic VMStateField (for example something like
vmstate_uint32 would become an implementation of the VMState trait on
u32). Then you'd write something like "vmstate_of!(Type,
field).with_version_id(2)" (i.e. vmstate_of retrieves the basic field
and fills in the offset, then you apply more changes on top). But that
may take a while, and I think it cannot be done without the
const_refs_to_static feature, which is only stable in 1.83.0.
Paolo
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more
2024-12-12 11:28 ` Paolo Bonzini
@ 2024-12-13 9:19 ` Zhao Liu
2024-12-13 11:24 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-13 9:19 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> > In this commit, this comment is a bit ahead, but I think it's okay.
> >
> > qom and qdev are both good names. In addition, we can rename the files
> > of PL011 as well. Perhaps device_class.rs could be merged into device.rs
> > (and eventually renamed to pl011.rs). I guess you might be planning to
> > keep it until the cleanup of vmstate and property is done.
>
> Yeah, I don't have any specific plans but memory_ops will certainly go away.
This depends on MemoryRegionOps binding. I will see how much HPET can do,
and I'm sorry for not being able to review the remaining patches yet in
this week, especially the last two. I will continue next week (my machine
will be powered down until next Monday :( ).
> device_class doesn't do much, but keeping it separate is a reminder for
> things that are still there to be cleaned up.
>
> As to VMState, there are two parts. One is the vmstate_description macro,
> probably it has to be replaced with something else to incorporate the
> trampolines for pre_save/post_load/... I haven't looked at it but it should
> not be a lot of work.
>
> The second is VMStateFields,
I found vmstate_array_of_pointer_to_struct missed a `info` field, and I
could submit a patch to fix this nit next week (along with other cleanup
you and other miantainers suggested for HPET).
I'd also like to try apply zeroable to VMStateField to avoid missing any
items...
> for which my idea is to implement a trait on
> types to retrieve a basic VMStateField (for example something like
> vmstate_uint32 would become an implementation of the VMState trait on u32).
This makes sense.
> Then you'd write something like "vmstate_of!(Type,
> field).with_version_id(2)" (i.e. vmstate_of retrieves the basic field and
> fills in the offset, then you apply more changes on top). But that may take
> a while, and I think it cannot be done without the const_refs_to_static
> feature, which is only stable in 1.83.0.
I also like this idea!
Thanks,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more
2024-12-13 9:19 ` Zhao Liu
@ 2024-12-13 11:24 ` Paolo Bonzini
0 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-13 11:24 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
[-- Attachment #1: Type: text/plain, Size: 1017 bytes --]
Il ven 13 dic 2024, 10:01 Zhao Liu <zhao1.liu@intel.com> ha scritto:
> I found vmstate_array_of_pointer_to_struct missed a `info` field, and I
> could submit a patch to fix this nit next week (along with other cleanup
> you and other miantainers suggested for HPET).
>
I wouldn't worry too much about VMState, it's not in its final form and
anything that isn't needed to pass qtests can wait.
Paolo
> for which my idea is to implement a trait on
> > types to retrieve a basic VMStateField (for example something like
> > vmstate_uint32 would become an implementation of the VMState trait on
> u32).
>
> This makes sense.
>
> > Then you'd write something like "vmstate_of!(Type,
> > field).with_version_id(2)" (i.e. vmstate_of retrieves the basic field and
> > fills in the offset, then you apply more changes on top). But that may
> take
> > a while, and I think it cannot be done without the const_refs_to_static
> > feature, which is only stable in 1.83.0.
>
> I also like this idea!
>
> Thanks,
> Zhao
>
>
>
[-- Attachment #2: Type: text/html, Size: 1801 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 20/26] rust: re-export C types from qemu-api submodules
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (18 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-12 9:55 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 21/26] rust: tests: allow writing more than one test Paolo Bonzini
` (6 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Long term we do not want device code to use "bindings" at all, so make it
possible to get the relevant types from the other modules of qemu-api.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/src/qdev.rs | 7 +++++--
rust/qemu-api/src/qom.rs | 10 ++++++----
rust/qemu-api/src/sysbus.rs | 3 ++-
rust/qemu-api/src/vmstate.rs | 9 +++++----
rust/qemu-api/tests/tests.rs | 3 ++-
5 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 1228dabaaaf..96a4b1515da 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -6,10 +6,13 @@
use std::ffi::CStr;
+pub use bindings::{DeviceClass, DeviceState, Property};
+
use crate::{
- bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
+ bindings::{self, Error},
prelude::*,
- qom::ClassInitImpl,
+ qom::{ClassInitImpl, ObjectClass},
+ vmstate::VMStateDescription,
zeroable::Zeroable,
};
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 9b316e07efa..6e3923d8ed0 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -36,7 +36,9 @@
use std::{ffi::CStr, os::raw::c_void};
-use crate::bindings::{self, Object, ObjectClass, TypeInfo};
+pub use bindings::{Object, ObjectClass};
+
+use crate::bindings::{self, TypeInfo};
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
// SAFETY: obj is an instance of T, since rust_instance_init<T>
@@ -161,8 +163,8 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
///
/// Each struct will implement this trait with `T` equal to each
/// superclass. For example, a device should implement at least
-/// `ClassInitImpl<`[`DeviceClass`](crate::bindings::DeviceClass)`>` and
-/// `ClassInitImpl<`[`ObjectClass`](crate::bindings::ObjectClass)`>`.
+/// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and
+/// `ClassInitImpl<`[`ObjectClass`]`>`.
///
/// Fortunately, this is almost never necessary. Instead, the Rust
/// implementation of methods will usually come from a trait like
@@ -193,7 +195,7 @@ pub trait ClassInitImpl<T> {
///
/// The virtual method implementations usually come from another
/// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl)
- /// when `T` is [`DeviceClass`](crate::bindings::DeviceClass).
+ /// when `T` is [`DeviceClass`](crate::qdev::DeviceClass).
///
/// On entry, `klass`'s parent class is initialized, while the other fields
/// are all zero; it is therefore assumed that all fields in `T` can be
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index fa69cadd7c1..a23562d7273 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -7,10 +7,11 @@
pub use bindings::{SysBusDevice, SysBusDeviceClass};
use crate::{
- bindings::{self, DeviceClass},
+ bindings,
cell::bql_locked,
irq::InterruptSource,
prelude::*,
+ qdev::DeviceClass,
qom::ClassInitImpl,
};
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index bedcf1e8f39..25c68b703ea 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -10,6 +10,8 @@
//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when
//! declaring a device model state struct.
+pub use crate::bindings::VMStateDescription;
+
#[doc(alias = "VMSTATE_UNUSED_BUFFER")]
#[macro_export]
macro_rules! vmstate_unused_buffer {
@@ -328,7 +330,7 @@ macro_rules! vmstate_fields {
}
/// A transparent wrapper type for the `subsections` field of
-/// [`VMStateDescription`](crate::bindings::VMStateDescription).
+/// [`VMStateDescription`].
///
/// This is necessary to be able to declare subsection descriptions as statics,
/// because the only way to implement `Sync` for a foreign type (and `*const`
@@ -342,9 +344,8 @@ macro_rules! vmstate_fields {
unsafe impl Sync for VMStateSubsectionsWrapper {}
-/// Helper macro to declare a list of subsections
-/// ([`VMStateDescription`](`crate::bindings::VMStateDescription`)) into a
-/// static and return a pointer to the array of pointers it created.
+/// Helper macro to declare a list of subsections ([`VMStateDescription`])
+/// into a static and return a pointer to the array of pointers it created.
#[macro_export]
macro_rules! vmstate_subsections {
($($subsection:expr),*$(,)*) => {{
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index fc57eb81290..68557fb85c7 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -8,8 +8,9 @@
bindings::*,
c_str, declare_properties, define_property,
prelude::*,
- qdev::DeviceImpl,
+ qdev::{DeviceImpl, DeviceState, Property},
qom::ObjectImpl,
+ vmstate::VMStateDescription,
zeroable::Zeroable,
};
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 20/26] rust: re-export C types from qemu-api submodules
2024-12-09 12:37 ` [PATCH 20/26] rust: re-export C types from qemu-api submodules Paolo Bonzini
@ 2024-12-12 9:55 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-12 9:55 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:11PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:11 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 20/26] rust: re-export C types from qemu-api submodules
> X-Mailer: git-send-email 2.47.1
>
> Long term we do not want device code to use "bindings" at all, so make it
> possible to get the relevant types from the other modules of qemu-api.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/qemu-api/src/qdev.rs | 7 +++++--
> rust/qemu-api/src/qom.rs | 10 ++++++----
> rust/qemu-api/src/sysbus.rs | 3 ++-
> rust/qemu-api/src/vmstate.rs | 9 +++++----
> rust/qemu-api/tests/tests.rs | 3 ++-
> 5 files changed, 20 insertions(+), 12 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 21/26] rust: tests: allow writing more than one test
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (19 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 20/26] rust: re-export C types from qemu-api submodules Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-12 10:04 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 22/26] rust: qom: add casting functionality Paolo Bonzini
` (5 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/tests/tests.rs | 109 ++++++++++++++++++++---------------
1 file changed, 61 insertions(+), 48 deletions(-)
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 68557fb85c7..18738a80008 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -2,7 +2,7 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::ffi::CStr;
+use std::{cell::Cell, ffi::CStr, sync::Mutex};
use qemu_api::{
bindings::*,
@@ -14,55 +14,68 @@
zeroable::Zeroable,
};
+// Test that macros can compile.
+pub static VMSTATE: VMStateDescription = VMStateDescription {
+ name: c_str!("name").as_ptr(),
+ unmigratable: true,
+ ..Zeroable::ZERO
+};
+
+#[derive(qemu_api_macros::offsets)]
+#[repr(C)]
+#[derive(qemu_api_macros::Object)]
+pub struct DummyState {
+ parent: DeviceState,
+ migrate_clock: bool,
+}
+
+declare_properties! {
+ DUMMY_PROPERTIES,
+ define_property!(
+ c_str!("migrate-clk"),
+ DummyState,
+ migrate_clock,
+ unsafe { &qdev_prop_bool },
+ bool
+ ),
+}
+
+unsafe impl ObjectType for DummyState {
+ type Class = <DeviceState as ObjectType>::Class;
+ const TYPE_NAME: &'static CStr = c_str!("dummy");
+}
+
+impl ObjectImpl for DummyState {
+ type ParentType = DeviceState;
+ const ABSTRACT: bool = false;
+}
+
+impl DeviceImpl for DummyState {
+ fn properties() -> &'static [Property] {
+ &DUMMY_PROPERTIES
+ }
+ fn vmsd() -> Option<&'static VMStateDescription> {
+ Some(&VMSTATE)
+ }
+}
+
+fn init_qom() {
+ static ONCE: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
+
+ let g = ONCE.lock().unwrap();
+ if !g.get() {
+ unsafe {
+ module_call_init(module_init_type::MODULE_INIT_QOM);
+ }
+ g.set(true);
+ }
+}
+
#[test]
-fn test_device_decl_macros() {
- // Test that macros can compile.
- pub static VMSTATE: VMStateDescription = VMStateDescription {
- name: c_str!("name").as_ptr(),
- unmigratable: true,
- ..Zeroable::ZERO
- };
-
- #[derive(qemu_api_macros::offsets)]
- #[repr(C)]
- #[derive(qemu_api_macros::Object)]
- pub struct DummyState {
- pub _parent: DeviceState,
- pub migrate_clock: bool,
- }
-
- declare_properties! {
- DUMMY_PROPERTIES,
- define_property!(
- c_str!("migrate-clk"),
- DummyState,
- migrate_clock,
- unsafe { &qdev_prop_bool },
- bool
- ),
- }
-
- unsafe impl ObjectType for DummyState {
- type Class = <DeviceState as ObjectType>::Class;
- const TYPE_NAME: &'static CStr = c_str!("dummy");
- }
-
- impl ObjectImpl for DummyState {
- type ParentType = DeviceState;
- const ABSTRACT: bool = false;
- }
-
- impl DeviceImpl for DummyState {
- fn properties() -> &'static [Property] {
- &DUMMY_PROPERTIES
- }
- fn vmsd() -> Option<&'static VMStateDescription> {
- Some(&VMSTATE)
- }
- }
-
+/// Create and immediately drop an instance.
+fn test_object_new() {
+ init_qom();
unsafe {
- module_call_init(module_init_type::MODULE_INIT_QOM);
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
}
}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 21/26] rust: tests: allow writing more than one test
2024-12-09 12:37 ` [PATCH 21/26] rust: tests: allow writing more than one test Paolo Bonzini
@ 2024-12-12 10:04 ` Zhao Liu
2024-12-16 15:07 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-12 10:04 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> +fn init_qom() {
> + static ONCE: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
> +
> + let g = ONCE.lock().unwrap();
> + if !g.get() {
> + unsafe {
> + module_call_init(module_init_type::MODULE_INIT_QOM);
> + }
> + g.set(true);
> + }
> +}
Only one question: what is the purpose of using a Mutex here?
Others LGTM,
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 21/26] rust: tests: allow writing more than one test
2024-12-12 10:04 ` Zhao Liu
@ 2024-12-16 15:07 ` Paolo Bonzini
0 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-16 15:07 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Thu, Dec 12, 2024 at 10:46 AM Zhao Liu <zhao1.liu@intel.com> wrote:
>
> > +fn init_qom() {
> > + static ONCE: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
> > +
> > + let g = ONCE.lock().unwrap();
> > + if !g.get() {
> > + unsafe {
> > + module_call_init(module_init_type::MODULE_INIT_QOM);
> > + }
> > + g.set(true);
> > + }
> > +}
>
> Only one question: what is the purpose of using a Mutex here?
Different #[test] functions can be run in parallel. For now there is
nothing that needs the BQL in the tests, so I used an independent
Mutex (which could become a LazyLock later when the minimum supported
Rust version is bumped).
It could also use unsafe { bql_lock(); } and a BqlCell. I left that
for when there will be a way to lock the BQL from safe Rust code, but
I can also do that now already.
Paolo
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 22/26] rust: qom: add casting functionality
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (20 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 21/26] rust: tests: allow writing more than one test Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-16 12:53 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 23/26] rust: qom: add initial subset of methods on Object Paolo Bonzini
` (4 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Add traits that let client cast typecast safely between object types.
In particular, an upcast is compile-time guaranteed to succeed, and a
YOLO C-style downcast must be marked as unsafe.
The traits are based on an IsA<> trait that declares what
is a subclass of what, which is an idea taken from glib-rs
(https://docs.rs/glib/latest/glib/object/trait.IsA.html).
The four primitives are also taken from there
(https://docs.rs/glib/latest/glib/object/trait.Cast.html). However,
the implementation of casting itself is a bit different and uses the
Deref trait.
This removes some pointer arithmetic from the pl011 device; it is also a
prerequisite for the definition of methods, so that they can be invoked
on all subclass structs. This will use the IsA<> trait to detect the
structs that support the methods.
glib also has a "monadic" casting trait which could be implemented on
Option (as in https://docs.rs/glib/latest/glib/object/trait.CastNone.html)
and perhaps even Result. For now I'm leaving it out, as the patch is
already big enough and the benefit seems debatable.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/Cargo.toml | 1 +
rust/hw/char/pl011/src/device.rs | 12 +-
rust/qemu-api/src/prelude.rs | 7 +
rust/qemu-api/src/qdev.rs | 1 +
rust/qemu-api/src/qom.rs | 279 ++++++++++++++++++++++++++++++-
rust/qemu-api/src/sysbus.rs | 3 +-
rust/qemu-api/tests/tests.rs | 70 +++++++-
7 files changed, 362 insertions(+), 11 deletions(-)
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 6ec19b67297..5b6b6ca4382 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -38,6 +38,7 @@ should_implement_trait = "deny"
unused_self = "allow"
# default-allow lints
+as_ptr_cast_mut = "deny"
as_underscore = "deny"
assertions_on_result_states = "deny"
bool_to_int_with_if = "deny"
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 3fed8b4ad25..e85d13c5a2b 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -106,6 +106,8 @@ pub struct PL011State {
device_id: DeviceId,
}
+qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
+
unsafe impl ObjectType for PL011State {
type Class = <SysBusDevice as ObjectType>::Class;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
@@ -140,8 +142,6 @@ impl PL011State {
unsafe fn init(&mut self) {
const CLK_NAME: &CStr = c_str!("clk");
- let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::<SysBusDevice>()) };
-
// SAFETY:
//
// self and self.iomem are guaranteed to be valid at this point since callers
@@ -155,15 +155,16 @@ unsafe fn init(&mut self) {
Self::TYPE_NAME.as_ptr(),
0x1000,
);
+
+ let sbd: &mut SysBusDevice = self.upcast_mut();
sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
}
for irq in self.interrupts.iter() {
+ let sbd: &SysBusDevice = self.upcast();
sbd.init_irq(irq);
}
- let dev = addr_of_mut!(*self).cast::<DeviceState>();
-
// SAFETY:
//
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
@@ -172,6 +173,7 @@ unsafe fn init(&mut self) {
// calls this function to initialize the fields; therefore no code is
// able to access an invalid self.clock value.
unsafe {
+ let dev: &mut DeviceState = self.upcast_mut();
self.clock = NonNull::new(qdev_init_clock_in(
dev,
CLK_NAME.as_ptr(),
@@ -632,6 +634,8 @@ unsafe fn init(&mut self) {
}
}
+qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object);
+
unsafe impl ObjectType for PL011Luminary {
type Class = <PL011State as ObjectType>::Class;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 5cc41f081f9..a0a71fcd6bc 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -7,4 +7,11 @@
pub use crate::cell::BqlCell;
pub use crate::cell::BqlRefCell;
+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::ObjectType;
+
+pub use crate::qom_isa;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 96a4b1515da..35efd210087 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -143,3 +143,4 @@ unsafe impl ObjectType for DeviceState {
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
}
+qom_isa!(DeviceState: Object);
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 6e3923d8ed0..7115d85df6f 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -4,9 +4,16 @@
//! Bindings to access QOM functionality from Rust.
//!
-//! This module provides automatic creation and registration of `TypeInfo`
-//! for classes that are written in Rust, and mapping between Rust traits
-//! and QOM vtables.
+//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU
+//! devices. This module makes QOM's features available in Rust through two main
+//! mechanisms:
+//!
+//! * Automatic creation and registration of `TypeInfo` for classes that are
+//! written in Rust, as well as mapping between Rust traits and QOM vtables.
+//!
+//! * Type-safe casting between parent and child classes, through the [`IsA`]
+//! trait and methods such as [`upcast`](ObjectCast::upcast) and
+//! [`downcast`](ObjectCast::downcast).
//!
//! # Structure of a class
//!
@@ -34,11 +41,57 @@
//! typically with wrappers that call into the
//! [`DeviceImpl`](crate::qdev::DeviceImpl) implementations.
-use std::{ffi::CStr, os::raw::c_void};
+use std::{
+ ffi::CStr,
+ ops::{Deref, DerefMut},
+ os::raw::c_void,
+};
pub use bindings::{Object, ObjectClass};
-use crate::bindings::{self, TypeInfo};
+use crate::bindings::{self, object_dynamic_cast, TypeInfo};
+
+/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct
+/// or indirect parent of `Self`).
+///
+/// # Safety
+///
+/// The struct `Self` must be `#[repr(C)]` and must begin, directly or
+/// indirectly, with a field of type `P`. This ensures that invalid casts,
+/// which rely on `IsA<>` for static checking, are rejected at compile time.
+pub unsafe trait IsA<P: ObjectType>: ObjectType {}
+
+// SAFETY: it is always safe to cast to your own type
+unsafe impl<T: ObjectType> IsA<T> for T {}
+
+/// Macro to mark superclasses of QOM classes. This enables type-safe
+/// up- and downcasting.
+///
+/// # Safety
+///
+/// This macro is a thin wrapper around the [`IsA`] trait and performs
+/// no checking whatsoever of what is declared. It is the caller's
+/// responsibility to have $struct begin, directly or indirectly, with
+/// a field of type `$parent`.
+#[macro_export]
+macro_rules! qom_isa {
+ ($struct:ty : $($parent:ty),* ) => {
+ $(
+ // SAFETY: it is the caller responsibility to have $parent as the
+ // first field
+ unsafe impl $crate::qom::IsA<$parent> for $struct {}
+
+ impl AsRef<$parent> for $struct {
+ fn as_ref(&self) -> &$parent {
+ // SAFETY: follows the same rules as for IsA<U>, which is
+ // declared above.
+ let ptr: *const Self = self;
+ unsafe { &*ptr.cast::<$parent>() }
+ }
+ }
+ )*
+ };
+}
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
// SAFETY: obj is an instance of T, since rust_instance_init<T>
@@ -94,8 +147,224 @@ pub unsafe trait ObjectType: Sized {
/// The name of the type, which can be passed to `object_new()` to
/// generate an instance of this type.
const TYPE_NAME: &'static CStr;
+
+ /// Return the receiver as an Object. This is always safe, even
+ /// if this type represents an interface.
+ fn as_object(&self) -> &Object {
+ unsafe { &*self.as_object_ptr() }
+ }
+
+ /// Return the receiver as a const raw pointer to Object.
+ /// This is preferrable to `as_object_mut_ptr()` if a C
+ /// function only needs a `const Object *`.
+ fn as_object_ptr(&self) -> *const Object {
+ self.as_ptr().cast()
+ }
+
+ /// Return the receiver as a mutable raw pointer to Object.
+ ///
+ /// # Safety
+ ///
+ /// This cast is always safe, but because the result is mutable
+ /// and the incoming reference is not, this should only be used
+ /// for calls to C functions, and only if needed.
+ unsafe fn as_object_mut_ptr(&self) -> *mut Object {
+ self.as_object_ptr() as *mut _
+ }
}
+/// 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`]
+/// trait.
+pub trait ObjectDeref: Deref
+where
+ Self::Target: ObjectType,
+{
+ /// Convert to a const Rust pointer, to be used for example for FFI.
+ /// The target pointer type must be the type of `self` or a superclass
+ fn as_ptr<U: ObjectType>(&self) -> *const U
+ where
+ Self::Target: IsA<U>,
+ {
+ let ptr: *const Self::Target = self.deref();
+ ptr.cast::<U>()
+ }
+
+ /// Convert to a mutable Rust pointer, to be used for example for FFI.
+ /// The target pointer type must be the type of `self` or a superclass.
+ /// Used to implement interior mutability for objects.
+ ///
+ /// # 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
+ where
+ Self::Target: IsA<U>,
+ {
+ #[allow(clippy::as_ptr_cast_mut)]
+ {
+ self.as_ptr::<U>() as *mut _
+ }
+ }
+}
+
+/// Trait that adds extra functionality for `&T` where `T` is a QOM
+/// object type. Allows conversion to/from C objects in generic code.
+pub trait ObjectCast: ObjectDeref + Copy
+where
+ Self::Target: ObjectType,
+{
+ /// Safely convert from a derived type to one of its parent types.
+ ///
+ /// This is always safe; the [`IsA`] trait provides static verification
+ /// trait that `Self` dereferences to `U` or a child of `U`.
+ fn upcast<'a, U: ObjectType>(self) -> &'a U
+ where
+ Self::Target: IsA<U>,
+ Self: 'a,
+ {
+ // SAFETY: soundness is declared via IsA<U>, which is an unsafe trait
+ unsafe { self.unsafe_cast::<U>() }
+ }
+
+ /// Attempt to convert to a derived type.
+ ///
+ /// Returns `None` if the object is not actually of type `U`. This is
+ /// verified at runtime by checking the object's type information.
+ fn downcast<'a, U: IsA<Self::Target>>(self) -> Option<&'a U>
+ where
+ Self: 'a,
+ {
+ self.dynamic_cast::<U>()
+ }
+
+ /// Attempt to convert between any two types in the QOM hierarchy.
+ ///
+ /// Returns `None` if the object is not actually of type `U`. This is
+ /// verified at runtime by checking the object's type information.
+ fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U>
+ where
+ Self: 'a,
+ {
+ unsafe {
+ // SAFETY: upcasting to Object is always valid, and the
+ // return type is either NULL or the argument itself
+ let result: *const U =
+ object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast();
+
+ result.as_ref()
+ }
+ }
+
+ /// Convert to any QOM type without verification.
+ ///
+ /// # Safety
+ ///
+ /// What safety? You need to know yourself that the cast is correct; only
+ /// use when performance is paramount. It is still better than a raw
+ /// pointer `cast()`, which does not even check that you remain in the
+ /// realm of QOM `ObjectType`s.
+ ///
+ /// `unsafe_cast::<Object>()` is always safe.
+ unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U
+ where
+ Self: 'a,
+ {
+ unsafe { &*(self.as_ptr::<Self::Target>().cast::<U>()) }
+ }
+}
+
+impl<T: ObjectType> ObjectDeref for &T {}
+impl<T: ObjectType> ObjectCast for &T {}
+
+/// Trait for mutable type casting operations in the QOM hierarchy.
+///
+/// This trait provides the mutable counterparts to [`ObjectCast`]'s conversion
+/// functions. Unlike `ObjectCast`, this trait returns `Result` for fallible
+/// conversions to preserve the original smart pointer if the cast fails. This
+/// is necessary because mutable references cannot be copied, so a failed cast
+/// must return ownership of the original reference. For example:
+///
+/// ```ignore
+/// let mut dev = get_device();
+/// // If this fails, we need the original `dev` back to try something else
+/// match dev.dynamic_cast_mut::<FooDevice>() {
+/// Ok(foodev) => /* use foodev */,
+/// Err(dev) => /* still have ownership of dev */
+/// }
+/// ```
+pub trait ObjectCastMut: Sized + ObjectDeref + DerefMut
+where
+ Self::Target: ObjectType,
+{
+ /// Safely convert from a derived type to one of its parent types.
+ ///
+ /// This is always safe; the [`IsA`] trait provides static verification
+ /// that `Self` dereferences to `U` or a child of `U`.
+ fn upcast_mut<'a, U: ObjectType>(self) -> &'a mut U
+ where
+ Self::Target: IsA<U>,
+ Self: 'a,
+ {
+ // SAFETY: soundness is declared via IsA<U>, which is an unsafe trait
+ unsafe { self.unsafe_cast_mut::<U>() }
+ }
+
+ /// Attempt to convert to a derived type.
+ ///
+ /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the
+ /// object if the conversion failed. This is verified at runtime by
+ /// checking the object's type information.
+ fn downcast_mut<'a, U: IsA<Self::Target>>(self) -> Result<&'a mut U, Self>
+ where
+ Self: 'a,
+ {
+ self.dynamic_cast_mut::<U>()
+ }
+
+ /// Attempt to convert between any two types in the QOM hierarchy.
+ ///
+ /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the
+ /// object if the conversion failed. This is verified at runtime by
+ /// checking the object's type information.
+ fn dynamic_cast_mut<'a, U: ObjectType>(self) -> Result<&'a mut U, Self>
+ where
+ Self: 'a,
+ {
+ unsafe {
+ // SAFETY: upcasting to Object is always valid, and the
+ // return type is either NULL or the argument itself
+ let result: *mut U =
+ object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast();
+
+ result.as_mut().ok_or(self)
+ }
+ }
+
+ /// Convert to any QOM type without verification.
+ ///
+ /// # Safety
+ ///
+ /// What safety? You need to know yourself that the cast is correct; only
+ /// use when performance is paramount. It is still better than a raw
+ /// pointer `cast()`, which does not even check that you remain in the
+ /// realm of QOM `ObjectType`s.
+ ///
+ /// `unsafe_cast::<Object>()` is always safe.
+ unsafe fn unsafe_cast_mut<'a, U: ObjectType>(self) -> &'a mut U
+ where
+ Self: 'a,
+ {
+ unsafe { &mut *self.as_mut_ptr::<Self::Target>().cast::<U>() }
+ }
+}
+
+impl<T: ObjectType> ObjectDeref for &mut T {}
+impl<T: ObjectType> ObjectCastMut for &mut T {}
+
/// Trait a type must implement to be registered with QEMU.
pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// The parent of the type. This should match the first field of
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index a23562d7273..8193734bde4 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -11,7 +11,7 @@
cell::bql_locked,
irq::InterruptSource,
prelude::*,
- qdev::DeviceClass,
+ qdev::{DeviceClass, DeviceState},
qom::ClassInitImpl,
};
@@ -20,6 +20,7 @@ unsafe impl ObjectType for SysBusDevice {
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
}
+qom_isa!(SysBusDevice: DeviceState, Object);
// TODO: add SysBusDeviceImpl
impl<T> ClassInitImpl<SysBusDeviceClass> for T
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 18738a80008..549e9699c26 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -2,7 +2,13 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{cell::Cell, ffi::CStr, sync::Mutex};
+use std::{
+ cell::Cell,
+ ffi::CStr,
+ os::raw::c_void,
+ ptr::{addr_of, addr_of_mut},
+ sync::Mutex,
+};
use qemu_api::{
bindings::*,
@@ -29,6 +35,8 @@ pub struct DummyState {
migrate_clock: bool,
}
+qom_isa!(DummyState: Object, DeviceState);
+
declare_properties! {
DUMMY_PROPERTIES,
define_property!(
@@ -79,3 +87,63 @@ fn test_object_new() {
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
}
}
+
+// a note on all "cast" tests: usually, especially for downcasts the desired
+// class would be placed on the right, for example:
+//
+// let sbd_ref = p.dynamic_cast::<SysBusDevice>();
+//
+// Here I am doing the opposite to check that the resulting type is correct.
+
+#[test]
+#[allow(clippy::shadow_unrelated)]
+/// 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_ref: &DummyState = unsafe { &*p };
+ let obj_ref: &Object = p_ref.upcast();
+ assert_eq!(addr_of!(*obj_ref), p.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());
+
+ // 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>());
+ }
+}
+
+#[test]
+#[allow(clippy::shadow_unrelated)]
+/// Test casts on mutable references.
+fn test_cast_mut() {
+ init_qom();
+ let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
+
+ let p_ref: &mut DummyState = unsafe { &mut *p };
+ let obj_ref: &mut Object = p_ref.upcast_mut();
+ assert_eq!(addr_of_mut!(*obj_ref), p.cast());
+
+ let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut();
+ let obj_ref = sbd_ref.unwrap_err();
+
+ let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut();
+ let dev_ref = dev_ref.unwrap();
+ assert_eq!(addr_of_mut!(*dev_ref), p.cast());
+
+ // SAFETY: the cast is wrong, but the value is only used for comparison
+ unsafe {
+ let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut();
+ assert_eq!(addr_of_mut!(*sbd_ref), p.cast());
+
+ object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
+ }
+}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 22/26] rust: qom: add casting functionality
2024-12-09 12:37 ` [PATCH 22/26] rust: qom: add casting functionality Paolo Bonzini
@ 2024-12-16 12:53 ` Zhao Liu
2024-12-16 15:17 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-16 12:53 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> +/// Macro to mark superclasses of QOM classes. This enables type-safe
> +/// up- and downcasting.
> +///
> +/// # Safety
> +///
> +/// This macro is a thin wrapper around the [`IsA`] trait and performs
> +/// no checking whatsoever of what is declared. It is the caller's
> +/// responsibility to have $struct begin, directly or indirectly, with
> +/// a field of type `$parent`.
> +#[macro_export]
> +macro_rules! qom_isa {
> + ($struct:ty : $($parent:ty),* ) => {
This macro is quite good, but it requires specifying all the parents...
So I am thinking if it is possible to move ParentType to ObjectType, and
then try to traverse the ParentType in the macro, implementing IsA for
each ParentType... However, the first difficulty has already stopped me:
I cannot define ParentType for Object itself.
> + $(
> + // SAFETY: it is the caller responsibility to have $parent as the
> + // first field
> + unsafe impl $crate::qom::IsA<$parent> for $struct {}
> +
> + impl AsRef<$parent> for $struct {
> + fn as_ref(&self) -> &$parent {
> + // SAFETY: follows the same rules as for IsA<U>, which is
> + // declared above.
> + let ptr: *const Self = self;
> + unsafe { &*ptr.cast::<$parent>() }
> + }
> + }
> + )*
> + };
> +}
>
> unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
> // SAFETY: obj is an instance of T, since rust_instance_init<T>
> @@ -94,8 +147,224 @@ pub unsafe trait ObjectType: Sized {
> /// The name of the type, which can be passed to `object_new()` to
> /// generate an instance of this type.
> const TYPE_NAME: &'static CStr;
> +
> + /// Return the receiver as an Object. This is always safe, even
> + /// if this type represents an interface.
This comment is a bit confusing to me... EMM, why mention the interface?
I understand if something implements ObjectType, then it should be an
Object, so deref/cast here would be valid. And if it is an interface,
it would need to implement the corresponding trait.
...
This cast idea is nice! In the future, class might also need to implement
similar cast support (e.g., class_init in virtio).
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 22/26] rust: qom: add casting functionality
2024-12-16 12:53 ` Zhao Liu
@ 2024-12-16 15:17 ` Paolo Bonzini
0 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-16 15:17 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 16, 2024 at 1:35 PM Zhao Liu <zhao1.liu@intel.com> wrote:
>
> > +/// Macro to mark superclasses of QOM classes. This enables type-safe
> > +/// up- and downcasting.
> > +///
> > +/// # Safety
> > +///
> > +/// This macro is a thin wrapper around the [`IsA`] trait and performs
> > +/// no checking whatsoever of what is declared. It is the caller's
> > +/// responsibility to have $struct begin, directly or indirectly, with
> > +/// a field of type `$parent`.
> > +#[macro_export]
> > +macro_rules! qom_isa {
> > + ($struct:ty : $($parent:ty),* ) => {
>
> This macro is quite good, but it requires specifying all the parents...
> So I am thinking if it is possible to move ParentType to ObjectType, and
> then try to traverse the ParentType in the macro, implementing IsA for
> each ParentType... However, the first difficulty has already stopped me:
> I cannot define ParentType for Object itself.
I am not sure how that could be done, but I've seen people
implementing boolean logic purely with types, like
pub struct True;
pub struct False;
pub trait Boolean {}
impl Boolean for True;
impl Boolean for False;
pub trait Or<T: Boolean>: Boolean {
type Result: Boolean;
}
impl<T: Boolean> Or<T> for True {
type Result = True;
}
impl<T: Boolean> Or<T> for False {
type Result = T;
}
and I think that can be used to implement recursive IsA, but that is a
bit too magic for this first step. QEMU class hierarchies are
relatively shallow.
> > @@ -94,8 +147,224 @@ pub unsafe trait ObjectType: Sized {
> > /// The name of the type, which can be passed to `object_new()` to
> > /// generate an instance of this type.
> > const TYPE_NAME: &'static CStr;
> > +
> > + /// Return the receiver as an Object. This is always safe, even
> > + /// if this type represents an interface.
>
> This comment is a bit confusing to me... EMM, why mention the interface?
> I understand if something implements ObjectType, then it should be an
> Object, so deref/cast here would be valid. And if it is an interface,
> it would need to implement the corresponding trait.
What I meant is that interfaces do (will) not implement IsA<Object>,
but they are ObjectTypes. So if you have let's say an "&impl
UserCreatable" argument, you could use as_object() to obtain an
&Object.
> This cast idea is nice! In the future, class might also need to implement=
> similar cast support (e.g., class_init in virtio).
Ok, good!
Paolo
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 23/26] rust: qom: add initial subset of methods on Object
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (21 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 22/26] rust: qom: add casting functionality Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-16 15:18 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 24/26] rust: qom: move device_id to PL011 class side Paolo Bonzini
` (3 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
Add an example of implementing instance methods and converting the
result back to a Rust type. In this case the returned types are a
string (actually a Cow<str>; but that's transparent as long as it derefs
to &str) and a QOM class.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/src/prelude.rs | 1 +
rust/qemu-api/src/qom.rs | 56 ++++++++++++++++++++++++++++++++++--
rust/qemu-api/tests/tests.rs | 12 ++++++++
3 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index a0a71fcd6bc..6f32deeb2ed 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -12,6 +12,7 @@
pub use crate::qom::ObjectCast;
pub use crate::qom::ObjectCastMut;
pub use crate::qom::ObjectDeref;
+pub use crate::qom::ObjectMethods;
pub use crate::qom::ObjectType;
pub use crate::qom_isa;
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 7115d85df6f..d7475b8ed6a 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -5,8 +5,8 @@
//! Bindings to access QOM functionality from Rust.
//!
//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU
-//! devices. This module makes QOM's features available in Rust through two main
-//! mechanisms:
+//! devices. This module makes QOM's features available in Rust through three
+//! main mechanisms:
//!
//! * Automatic creation and registration of `TypeInfo` for classes that are
//! written in Rust, as well as mapping between Rust traits and QOM vtables.
@@ -15,6 +15,11 @@
//! trait and methods such as [`upcast`](ObjectCast::upcast) and
//! [`downcast`](ObjectCast::downcast).
//!
+//! * Automatic delegation of parent class methods to child classes. When a
+//! trait uses [`IsA`] as a bound, its contents become available to all child
+//! classes through blanket implementations. This works both for class methods
+//! and for instance methods accessed through references or smart pointers.
+//!
//! # Structure of a class
//!
//! A concrete class only needs a struct holding instance state. The struct must
@@ -40,6 +45,16 @@
//! `ClassInitImpl<DeviceClass>`. This fills the vtable in the class struct,
//! typically with wrappers that call into the
//! [`DeviceImpl`](crate::qdev::DeviceImpl) implementations.
+//!
+//! * a trait for instance methods, for example `DeviceMethods`. This trait is
+//! automatically implemented for any reference or smart pointer to a device
+//! instance. It calls into the vtable provides access across all subclasses
+//! to methods defined for the class.
+//!
+//! * optionally, a trait for class methods, for example `DeviceClassMethods`.
+//! This provides access to class-wide functionality that doesn't depend on
+//! instance data. Like instance methods, these are automatically inherited by
+//! child classes.
use std::{
ffi::CStr,
@@ -49,7 +64,7 @@
pub use bindings::{Object, ObjectClass};
-use crate::bindings::{self, object_dynamic_cast, TypeInfo};
+use crate::bindings::{self, object_dynamic_cast, object_get_class, object_get_typename, TypeInfo};
/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct
/// or indirect parent of `Self`).
@@ -504,3 +519,38 @@ unsafe impl ObjectType for Object {
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) };
}
+
+/// Trait for 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 ObjectMethods: ObjectDeref
+where
+ Self::Target: IsA<Object>,
+{
+ /// Return the name of the type of `self`
+ fn typename(&self) -> std::borrow::Cow<'_, str> {
+ let obj = self.upcast::<Object>();
+ // SAFETY: safety of this is the requirement for implementing IsA
+ // The result of the C API has static lifetime
+ unsafe {
+ let p = object_get_typename(obj.as_mut_ptr());
+ CStr::from_ptr(p).to_string_lossy()
+ }
+ }
+
+ fn get_class(&self) -> &'static <Self::Target as ObjectType>::Class {
+ let obj = self.upcast::<Object>();
+
+ // SAFETY: all objects can call object_get_class; the actual class
+ // type is guaranteed by the implementation of `ObjectType` and
+ // `ObjectImpl`.
+ let klass: &'static <Self::Target as ObjectType>::Class =
+ unsafe { &*object_get_class(obj.as_mut_ptr()).cast() };
+
+ klass
+ }
+}
+
+impl<R: ObjectDeref> ObjectMethods for R where R::Target: IsA<Object> {}
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 549e9699c26..4c766c7f808 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -88,6 +88,18 @@ fn test_object_new() {
}
}
+#[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>());
+ }
+}
+
// a note on all "cast" tests: usually, especially for downcasts the desired
// class would be placed on the right, for example:
//
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 23/26] rust: qom: add initial subset of methods on Object
2024-12-09 12:37 ` [PATCH 23/26] rust: qom: add initial subset of methods on Object Paolo Bonzini
@ 2024-12-16 15:18 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-16 15:18 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:14PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:14 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 23/26] rust: qom: add initial subset of methods on Object
> X-Mailer: git-send-email 2.47.1
>
> Add an example of implementing instance methods and converting the
> result back to a Rust type. In this case the returned types are a
> string (actually a Cow<str>; but that's transparent as long as it derefs
> to &str) and a QOM class.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/qemu-api/src/prelude.rs | 1 +
> rust/qemu-api/src/qom.rs | 56 ++++++++++++++++++++++++++++++++++--
> rust/qemu-api/tests/tests.rs | 12 ++++++++
> 3 files changed, 66 insertions(+), 3 deletions(-)
>
Good example!
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (22 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 23/26] rust: qom: add initial subset of methods on Object Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-17 3:58 ` Zhao Liu
2024-12-18 14:47 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 25/26] rust: qemu-api: add a module to wrap functions and zero-sized closures Paolo Bonzini
` (2 subsequent siblings)
26 siblings, 2 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
There is no need to monkeypatch DeviceId::Luminary into the already-initialized
PL011State. Instead, now that we can define a class hierarchy, we can define
PL011Class and make device_id a field in there.
There is also no need anymore to have "Arm" as zero, so change DeviceId into a
wrapper for the array; all it does is provide an Index<hwaddr> implementation
because arrays can only be indexed by usize.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 59 +++++++++++++++-----------------
1 file changed, 28 insertions(+), 31 deletions(-)
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index e85d13c5a2b..41220c99a83 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -5,7 +5,7 @@
use core::ptr::{addr_of_mut, NonNull};
use std::{
ffi::CStr,
- os::raw::{c_int, c_uchar, c_uint, c_void},
+ os::raw::{c_int, c_uint, c_void},
};
use qemu_api::{
@@ -14,7 +14,7 @@
irq::InterruptSource,
prelude::*,
qdev::DeviceImpl,
- qom::ObjectImpl,
+ qom::{ClassInitImpl, ObjectImpl},
};
use crate::{
@@ -35,27 +35,20 @@
/// QEMU sourced constant.
pub const PL011_FIFO_DEPTH: usize = 16_usize;
-#[derive(Clone, Copy, Debug)]
-enum DeviceId {
- #[allow(dead_code)]
- Arm = 0,
- Luminary,
-}
+#[derive(Clone, Copy)]
+struct DeviceId(&'static [u8; 8]);
impl std::ops::Index<hwaddr> for DeviceId {
- type Output = c_uchar;
+ type Output = u8;
fn index(&self, idx: hwaddr) -> &Self::Output {
- match self {
- Self::Arm => &Self::PL011_ID_ARM[idx as usize],
- Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize],
- }
+ &self.0[idx as usize]
}
}
impl DeviceId {
- const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
- const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1];
+ const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
+ const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
}
#[repr(C)]
@@ -102,17 +95,28 @@ pub struct PL011State {
pub clock: NonNull<Clock>,
#[doc(alias = "migrate_clk")]
pub migrate_clock: bool,
- /// The byte string that identifies the device.
- device_id: DeviceId,
}
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
+pub struct PL011Class {
+ parent_class: <SysBusDevice as ObjectType>::Class,
+ /// The byte string that identifies the device.
+ device_id: DeviceId,
+}
+
unsafe impl ObjectType for PL011State {
- type Class = <SysBusDevice as ObjectType>::Class;
+ type Class = PL011Class;
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
}
+impl ClassInitImpl<PL011Class> for PL011State {
+ fn class_init(klass: &mut PL011Class) {
+ klass.device_id = DeviceId::ARM;
+ <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
+ }
+}
+
impl ObjectImpl for PL011State {
type ParentType = SysBusDevice;
@@ -190,7 +194,8 @@ pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u
std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) {
Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
- u64::from(self.device_id[(offset - 0xfe0) >> 2])
+ let device_id = self.get_class().device_id;
+ u64::from(device_id[(offset - 0xfe0) >> 2])
}
Err(_) => {
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
@@ -621,16 +626,10 @@ pub struct PL011Luminary {
parent_obj: PL011State,
}
-impl PL011Luminary {
- /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
- ///
- /// # Safety
- ///
- /// We expect the FFI user of this function to pass a valid pointer, that
- /// has the same size as [`PL011Luminary`]. We also expect the device is
- /// readable/writeable from one thread at any time.
- unsafe fn init(&mut self) {
- self.parent_obj.device_id = DeviceId::Luminary;
+impl ClassInitImpl<PL011Class> for PL011Luminary {
+ fn class_init(klass: &mut PL011Class) {
+ klass.device_id = DeviceId::LUMINARY;
+ <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
}
}
@@ -643,8 +642,6 @@ unsafe impl ObjectType for PL011Luminary {
impl ObjectImpl for PL011Luminary {
type ParentType = PL011State;
-
- const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
}
impl DeviceImpl for PL011Luminary {}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-09 12:37 ` [PATCH 24/26] rust: qom: move device_id to PL011 class side Paolo Bonzini
@ 2024-12-17 3:58 ` Zhao Liu
2024-12-17 16:50 ` Paolo Bonzini
2024-12-18 14:47 ` Zhao Liu
1 sibling, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-17 3:58 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> +impl ClassInitImpl<PL011Class> for PL011State {
> + fn class_init(klass: &mut PL011Class) {
> + klass.device_id = DeviceId::ARM;
> + <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
This seems a bit of a conflict with the C version of QOM semantics. In C,
class_init is registered in TypeInfo, and then the QOM code will
automatically call the parent's class_init without needing to explicitly
call the parent's in the child's class_init.
However, SysBusDevice (and Device) is a bit different. Its TypeInfo is
registered on the C side, and the class_init method on the Rust side is not
actually a real QOM class_init (because it is not registered on the Rust
side).
Therefore, the call here seems valid from the code logic's perspective.
But, when there is deeper class inheritance, it seems impossible to
prevent class_init from being called both by the C side's QOM code and by
this kind of recursive case on the Rust side.
So, for devices like SysBusDevice that are registered on the C side,
should we not implement class_init and also not call it explicitly?
Or should we distinguish between two different usages of class_init? One
is registered in TypeInfo (only as a callback in rust_class_init) - perhaps
rename it as qom_class_init, and the other is used as a helper for Rust-side
calls (similar to the recursive usage here) - maybe rename it as
class_inter_init.
> + }
> +}
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-17 3:58 ` Zhao Liu
@ 2024-12-17 16:50 ` Paolo Bonzini
2024-12-18 6:57 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-17 16:50 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
[-- Attachment #1: Type: text/plain, Size: 2148 bytes --]
Il mar 17 dic 2024, 04:39 Zhao Liu <zhao1.liu@intel.com> ha scritto:
> > +impl ClassInitImpl<PL011Class> for PL011State {
> > + fn class_init(klass: &mut PL011Class) {
> > + klass.device_id = DeviceId::ARM;
> > + <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut
> klass.parent_class);
>
> This seems a bit of a conflict with the C version of QOM semantics. In C,
> class_init is registered in TypeInfo, and then the QOM code will
> automatically call the parent's class_init without needing to explicitly
> call the parent's in the child's class_init.
>
This is the same in Rust.
The difference is that in C you have a single class_init function that sets
all members of ObjectClass, DeviceClass, etc. In Rust each class has one
trait and there is a chain of ClassInitImpl implementations—one filling in
"oc" from ObjectImpl, one filling in "dc" from DeviceImpl and so on.
But in both cases you get a chain of calls from qom/object.c.
Therefore, the call here seems valid from the code logic's perspective.
> But, when there is deeper class inheritance, it seems impossible to
> prevent class_init from being called both by the C side's QOM code and by
> this kind of recursive case on the Rust side.
>
Note that here you have two parameters: what class is being filled (the
argument C of ClassInitImpl<C>) *and* what type is being initialized
(that's Self).
The "recursion" is only on the argument C, and matches the way C code
implements class_init.
Maybe the confusion is because I implemented class_init twice instead of
using a separate trait "PL011Impl"?
Paolo
So, for devices like SysBusDevice that are registered on the C side,
> should we not implement class_init and also not call it explicitly?
>
> Or should we distinguish between two different usages of class_init? One
> is registered in TypeInfo (only as a callback in rust_class_init) - perhaps
> rename it as qom_class_init, and the other is used as a helper for
> Rust-side
> calls (similar to the recursive usage here) - maybe rename it as
> class_inter_init.
>
> > + }
> > +}
>
>
[-- Attachment #2: Type: text/html, Size: 3397 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-17 16:50 ` Paolo Bonzini
@ 2024-12-18 6:57 ` Zhao Liu
2024-12-18 7:14 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-18 6:57 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Tue, Dec 17, 2024 at 05:50:09PM +0100, Paolo Bonzini wrote:
> Date: Tue, 17 Dec 2024 17:50:09 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
>
> Il mar 17 dic 2024, 04:39 Zhao Liu <zhao1.liu@intel.com> ha scritto:
>
> > > +impl ClassInitImpl<PL011Class> for PL011State {
> > > + fn class_init(klass: &mut PL011Class) {
> > > + klass.device_id = DeviceId::ARM;
> > > + <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut
> > klass.parent_class);
> >
> > This seems a bit of a conflict with the C version of QOM semantics. In C,
> > class_init is registered in TypeInfo, and then the QOM code will
> > automatically call the parent's class_init without needing to explicitly
> > call the parent's in the child's class_init.
> >
>
> This is the same in Rust.
>
> The difference is that in C you have a single class_init function that sets
> all members of ObjectClass, DeviceClass, etc. In Rust each class has one
> trait and there is a chain of ClassInitImpl implementations—one filling in
> "oc" from ObjectImpl, one filling in "dc" from DeviceImpl and so on.
>
> But in both cases you get a chain of calls from qom/object.c.
>
> Therefore, the call here seems valid from the code logic's perspective.
I supposed a case, where there is such a QOM (QEMU Object Model)
structure relationship:
* DummyState / DummyClass: defined in Rust side, and registered the
TypeInfo by `Object` macro.
- So its class_init will be called by C QOM code.
* DummyChildState / DummyChildClass: defined in Rust side as the
child-object of DummyState, and registered the TypeInfo by `Object`
macro. And suppose it can inherit the trait of DummyClass -
ClassInitImpl<DummyClass> (but I found a gap here, as detailed later;
I expect it should be able to inherit normally).
- So its class_init will be called by C QOM code. In C code call chain,
its parent's class_init should be called by C before its own
class_init.
- However, note that according to the Rust class initialization call
chain, it should also call the parent's class_init within its own
class_init.
- :( the parent's class_init gets called twice.
If you agree this case indeed exists, then I think we should distinguish
between different class_init methods for the Rust and C call chains.
Moving on to another topic, about the gap (or question :-)) where a
child class inherits the ClassInitImpl trait from the parent, please see
my test case example below: Doing something similar to SysBusDevice and
DeviceState using a generic T outside of the QOM library would violate
the orphan rule.
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 7edadf911cca..8cae222a37be 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -13,8 +13,8 @@
use qemu_api::{
bindings::*,
c_str, declare_properties, define_property,
- qdev::{DeviceImpl, DeviceState, Property},
- qom::{ObjectCast, ObjectCastMut, ObjectImpl, ObjectMethods, ObjectType},
+ qdev::{DeviceClass, DeviceImpl, DeviceState, Property},
+ qom::{ClassInitImpl, ObjectCast, ObjectCastMut, ObjectImpl, ObjectMethods, ObjectType},
qom_isa,
vmstate::VMStateDescription,
zeroable::Zeroable,
@@ -37,6 +37,10 @@ pub struct DummyState {
qom_isa!(DummyState: Object, DeviceState);
+pub struct DummyClass {
+ parent_class: <DeviceState as ObjectType>::Class,
+}
+
declare_properties! {
DUMMY_PROPERTIES,
define_property!(
@@ -49,7 +53,7 @@ pub struct DummyState {
}
unsafe impl ObjectType for DummyState {
- type Class = <DeviceState as ObjectType>::Class;
+ type Class = DummyClass;
const TYPE_NAME: &'static CStr = c_str!("dummy");
}
@@ -67,6 +71,51 @@ fn vmsd() -> Option<&'static VMStateDescription> {
}
}
+// `impl<T> ClassInitImpl<DummyClass> for T` doesn't work since it violates orphan rule.
+impl ClassInitImpl<DummyClass> for DummyState {
+ fn class_init(klass: &mut DummyClass) {
+ <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class);
+ }
+}
+
+#[derive(qemu_api_macros::offsets)]
+#[repr(C)]
+#[derive(qemu_api_macros::Object)]
+pub struct DummyChildState {
+ parent: DummyState,
+ migrate_clock: bool,
+}
+
+qom_isa!(DummyChildState: Object, DeviceState, DummyState);
+
+pub struct DummyChildClass {
+ parent_class: <DummyState as ObjectType>::Class,
+}
+
+unsafe impl ObjectType for DummyChildState {
+ type Class = DummyChildClass;
+ const TYPE_NAME: &'static CStr = c_str!("dummy_child");
+}
+
+impl ObjectImpl for DummyChildState {
+ type ParentType = DummyState;
+ const ABSTRACT: bool = false;
+}
+
+impl DeviceImpl for DummyChildState {}
+
+impl ClassInitImpl<DummyClass> for DummyChildState {
+ fn class_init(klass: &mut DummyClass) {
+ <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class);
+ }
+}
+
+impl ClassInitImpl<DummyChildClass> for DummyChildState {
+ fn class_init(klass: &mut DummyChildClass) {
+ <Self as ClassInitImpl<DummyClass>>::class_init(&mut klass.parent_class);
+ }
+}
+
fn init_qom() {
static ONCE: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
@@ -85,6 +134,7 @@ 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());
}
}
> > But, when there is deeper class inheritance, it seems impossible to
> > prevent class_init from being called both by the C side's QOM code and by
> > this kind of recursive case on the Rust side.
> >
>
> Note that here you have two parameters: what class is being filled (the
> argument C of ClassInitImpl<C>) *and* what type is being initialized
> (that's Self).
>
> The "recursion" is only on the argument C, and matches the way C code
> implements class_init.
For Rust side, PL011Class' class_init calls SysBusDeviceClass' class_init,
and SysBusDeviceClass will also call DeviceClass' class_init. So this is
also recursion, right?
> Maybe the confusion is because I implemented class_init twice instead of
> using a separate trait "PL011Impl"?
Ah, yes! But I think the Rust call chain should not use class_init anymore
but should use a different method. This way, the original class_init would
only serve the C QOM. A separate trait might break the inheritance
relationship similar to ClassInitImpl.
Regards,
Zhao
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-18 6:57 ` Zhao Liu
@ 2024-12-18 7:14 ` Paolo Bonzini
2024-12-18 10:26 ` Paolo Bonzini
2024-12-18 14:30 ` Zhao Liu
0 siblings, 2 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-18 7:14 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
[-- Attachment #1: Type: text/plain, Size: 3792 bytes --]
Il mer 18 dic 2024, 07:39 Zhao Liu <zhao1.liu@intel.com> ha scritto:
> I supposed a case, where there is such a QOM (QEMU Object Model)
> structure relationship:
>
> * DummyState / DummyClass: defined in Rust side, and registered the
> TypeInfo by `Object` macro.
>
> - So its class_init will be called by C QOM code.
>
> * DummyChildState / DummyChildClass: defined in Rust side as the
> child-object of DummyState, and registered the TypeInfo by `Object`
> macro. And suppose it can inherit the trait of DummyClass -
> ClassInitImpl<DummyClass> (but I found a gap here, as detailed later;
> I expect it should be able to inherit normally).
>
> - So its class_init will be called by C QOM code. In C code call chain,
> its parent's class_init should be called by C before its own
> class_init.
> - However, note that according to the Rust class initialization call
> chain, it should also call the parent's class_init within its own
> class_init.
> - :( the parent's class_init gets called twice.
>
No, I don't think so. You have the same thing already with
PL011State/PL011Luminary.
There, you have
* object_class_init
* device_class_init
* sysbus_device_class_init
* <PL011State as ClassInitImpl<PL011Class>>::class_init
* <PL011State as ClassInitImpl<SysBusDeviceClass>>::class_init
* <PL011State as ClassInitImpl<DeviceClass>>::class_init
* <PL011State as ClassInitImpl<ObjectClass>>::class_init
* <PL011Luminary as ClassInitImpl<PL011Class>>::class_init
* <PL011Luminary as ClassInitImpl<SysBusDeviceClass>>::class_init
* <PL011Luminary as ClassInitImpl<DeviceClass>>::class_init
* <PL011Luminary as ClassInitImpl<ObjectClass>>::class_init
But note that these calls are all different and indeed the last three are
empty (all vtable entries are None). This is like a C class_init
implementation that does not set any of sdc, dc or oc.
Moving on to another topic, about the gap (or question :-)) where a
> child class inherits the ClassInitImpl trait from the parent, please see
> my test case example below: Doing something similar to SysBusDevice and
> DeviceState using a generic T outside of the QOM library would violate
> the orphan rule.
>
Ugh, you're right. Maybe ClassInitImpl should just be merged into
ObjectImpl etc. as a default method implementation. I will check.
> > But, when there is deeper class inheritance, it seems impossible to
> > > prevent class_init from being called both by the C side's QOM code and
> by
> > > this kind of recursive case on the Rust side.
> > >
> >
> > Note that here you have two parameters: what class is being filled (the
> > argument C of ClassInitImpl<C>) *and* what type is being initialized
> > (that's Self).
> >
> > The "recursion" is only on the argument C, and matches the way C code
> > implements class_init.
>
> For Rust side, PL011Class' class_init calls SysBusDeviceClass' class_init,
> and SysBusDeviceClass will also call DeviceClass' class_init. So this is
> also recursion, right?
>
No, Self is not PL011Class. Self is PL011State (or PL011Luminary/ and it
always remains the same. What changes is *what part* of the class is
overwritten, but the order of calls from qom/object.c follows the same
logic in both C and Rust.
> Maybe the confusion is because I implemented class_init twice instead of
> > using a separate trait "PL011Impl"?
>
> Ah, yes! But I think the Rust call chain should not use class_init anymore
> but should use a different method. This way, the original class_init would
> only serve the C QOM. A separate trait might break the inheritance
> relationship similar to ClassInitImpl.
>
Do you still think that this is the case? I will look into how to avoid the
problem with the orphan rule, but otherwise I think things are fine.
Paolo
>
[-- Attachment #2: Type: text/html, Size: 5991 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-18 7:14 ` Paolo Bonzini
@ 2024-12-18 10:26 ` Paolo Bonzini
2024-12-18 14:46 ` Zhao Liu
2024-12-18 14:30 ` Zhao Liu
1 sibling, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-18 10:26 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
On 12/18/24 08:14, Paolo Bonzini wrote:
> Moving on to another topic, about the gap (or question :-)) where a
> child class inherits the ClassInitImpl trait from the parent, please see
> my test case example below: Doing something similar to SysBusDevice and
> DeviceState using a generic T outside of the QOM library would violate
> the orphan rule.
>
> Ugh, you're right. Maybe ClassInitImpl should just be merged into
> ObjectImpl etc. as a default method implementation. I will check.
Ok, I think we can make it mostly a documentation issue:
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 2b472915707..ee95eda0447 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -451,13 +451,14 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// Each struct will implement this trait with `T` equal to each
/// superclass. For example, a device should implement at least
/// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and
-/// `ClassInitImpl<`[`ObjectClass`]`>`.
+/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are
+/// made in one of two ways.
///
-/// Fortunately, this is almost never necessary. Instead, the Rust
-/// implementation of methods will usually come from a trait like
-/// [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl).
-/// `ClassInitImpl` then can be provided by blanket implementations
-/// that operate on all implementors of the `*Impl`* trait. For example:
+/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api`
+/// crate itself. The Rust implementation of methods will come from a
+/// trait like [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl),
+/// and `ClassInitImpl` is provided by blanket implementations that
+/// operate on all implementors of the `*Impl`* trait. For example:
///
/// ```ignore
/// impl<T> ClassInitImpl<DeviceClass> for T
@@ -469,11 +470,37 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
/// after initializing the `DeviceClass` part of the class struct,
/// the parent [`ObjectClass`] is initialized as well.
///
-/// The only case in which a manual implementation of the trait is needed
-/// is for interfaces (note that there is no Rust example yet for using
-/// interfaces). In this case, unlike the C case, the Rust class _has_
-/// to define its own class struct `FooClass` to go together with
-/// `ClassInitImpl<FooClass>`.
+/// In some other cases, manual implementation of the trait is needed.
+/// These are the following:
+///
+/// * for classes that implement interfaces, the Rust code _has_
+/// to define its own class struct `FooClass` and implement
+/// `ClassInitImpl<FooClass>`. `ClassInitImpl<FooClass>`'s
+/// `class_init` method will then forward to multiple other
+/// `class_init`s, for the interfaces as well as the superclass.
+/// (Note that there is no Rust example yet for using
+/// interfaces).
+///
+/// * for classes implemented outside the ``qemu-api`` crate, it's
+/// not possible to add blanket implementations like the above one,
+/// due to orphan rules. In that case, the easiest solution is to
+/// implement `ClassInitImpl<YourSuperclass>` for each subclass,
+/// and not have a `YourSuperclassImpl` trait at all:
+///
+/// ```ignore
+/// impl ClassInitImpl<YourSuperclass> for YourSubclass {
+/// fn class_init(klass: &mut YourSuperclass) {
+/// klass.some_method = Some(Self::some_method);
+/// <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
+/// }
+/// }
+/// ```
+///
+/// While this method incurs a small amount of code duplication,
+/// it is generally limited to the recursive call on the last line.
+/// This is because classes defined in Rust do not need the same
+/// glue code that is needed when the classes are defined in C code.
+/// You may consider using a macro if you have many subclasses.
pub trait ClassInitImpl<T> {
/// Initialize `klass` to point to the virtual method implementations
/// for `Self`. On entry, the virtual method pointers are set to
Optionally, something like this can be squashed in this patch, but I
do not think it's worth the savings of... 3 lines of code:
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 41220c99a83..cbd3abb96ec 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -46,11 +46,6 @@ fn index(&self, idx: hwaddr) -> &Self::Output {
}
}
-impl DeviceId {
- const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
- const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
-}
-
#[repr(C)]
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
/// PL011 Device Model in QEMU
@@ -112,7 +107,8 @@ unsafe impl ObjectType for PL011State {
impl ClassInitImpl<PL011Class> for PL011State {
fn class_init(klass: &mut PL011Class) {
- klass.device_id = DeviceId::ARM;
+ klass.device_id = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
+
<Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
}
}
@@ -628,7 +624,8 @@ pub struct PL011Luminary {
impl ClassInitImpl<PL011Class> for PL011Luminary {
fn class_init(klass: &mut PL011Class) {
- klass.device_id = DeviceId::LUMINARY;
+ klass.device_id = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
+
<Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
}
}
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-18 10:26 ` Paolo Bonzini
@ 2024-12-18 14:46 ` Zhao Liu
2024-12-18 16:01 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-18 14:46 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Wed, Dec 18, 2024 at 11:26:35AM +0100, Paolo Bonzini wrote:
> Date: Wed, 18 Dec 2024 11:26:35 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
>
> On 12/18/24 08:14, Paolo Bonzini wrote:
> > Moving on to another topic, about the gap (or question :-)) where a
> > child class inherits the ClassInitImpl trait from the parent, please see
> > my test case example below: Doing something similar to SysBusDevice and
> > DeviceState using a generic T outside of the QOM library would violate
> > the orphan rule.
> >
> > Ugh, you're right. Maybe ClassInitImpl should just be merged into
> > ObjectImpl etc. as a default method implementation. I will check.
>
> Ok, I think we can make it mostly a documentation issue:
>
> diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
> index 2b472915707..ee95eda0447 100644
> --- a/rust/qemu-api/src/qom.rs
> +++ b/rust/qemu-api/src/qom.rs
> @@ -451,13 +451,14 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
> /// Each struct will implement this trait with `T` equal to each
> /// superclass. For example, a device should implement at least
> /// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and
> -/// `ClassInitImpl<`[`ObjectClass`]`>`.
> +/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are
> +/// made in one of two ways.
> ///
> -/// Fortunately, this is almost never necessary. Instead, the Rust
> -/// implementation of methods will usually come from a trait like
> -/// [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl).
> -/// `ClassInitImpl` then can be provided by blanket implementations
> -/// that operate on all implementors of the `*Impl`* trait. For example:
> +/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api`
> +/// crate itself. The Rust implementation of methods will come from a
> +/// trait like [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl),
> +/// and `ClassInitImpl` is provided by blanket implementations that
> +/// operate on all implementors of the `*Impl`* trait. For example:
> ///
> /// ```ignore
> /// impl<T> ClassInitImpl<DeviceClass> for T
> @@ -469,11 +470,37 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
> /// after initializing the `DeviceClass` part of the class struct,
> /// the parent [`ObjectClass`] is initialized as well.
> ///
> -/// The only case in which a manual implementation of the trait is needed
> -/// is for interfaces (note that there is no Rust example yet for using
> -/// interfaces). In this case, unlike the C case, the Rust class _has_
> -/// to define its own class struct `FooClass` to go together with
> -/// `ClassInitImpl<FooClass>`.
> +/// In some other cases, manual implementation of the trait is needed.
> +/// These are the following:
> +///
> +/// * for classes that implement interfaces, the Rust code _has_
> +/// to define its own class struct `FooClass` and implement
> +/// `ClassInitImpl<FooClass>`. `ClassInitImpl<FooClass>`'s
> +/// `class_init` method will then forward to multiple other
> +/// `class_init`s, for the interfaces as well as the superclass.
> +/// (Note that there is no Rust example yet for using
> +/// interfaces).
> +///
> +/// * for classes implemented outside the ``qemu-api`` crate, it's
> +/// not possible to add blanket implementations like the above one,
> +/// due to orphan rules. In that case, the easiest solution is to
> +/// implement `ClassInitImpl<YourSuperclass>` for each subclass,
> +/// and not have a `YourSuperclassImpl` trait at all:
> +///
> +/// ```ignore
> +/// impl ClassInitImpl<YourSuperclass> for YourSubclass {
> +/// fn class_init(klass: &mut YourSuperclass) {
> +/// klass.some_method = Some(Self::some_method);
> +/// <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
> +/// }
> +/// }
> +/// ```
BTW, maybe we could also squash my previous example in test? :-)
> +///
> +/// While this method incurs a small amount of code duplication,
> +/// it is generally limited to the recursive call on the last line.
> +/// This is because classes defined in Rust do not need the same
> +/// glue code that is needed when the classes are defined in C code.
Now I understand this advantage.
> +/// You may consider using a macro if you have many subclasses.
Yes, a custom macro is enough!
> pub trait ClassInitImpl<T> {
> /// Initialize `klass` to point to the virtual method implementations
> /// for `Self`. On entry, the virtual method pointers are set to
All the above changes look good to me!
> Optionally, something like this can be squashed in this patch, but I
> do not think it's worth the savings of... 3 lines of code:
>
> diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
> index 41220c99a83..cbd3abb96ec 100644
> --- a/rust/hw/char/pl011/src/device.rs
> +++ b/rust/hw/char/pl011/src/device.rs
> @@ -46,11 +46,6 @@ fn index(&self, idx: hwaddr) -> &Self::Output {
> }
> }
> -impl DeviceId {
> - const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
> - const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
> -}
> -
> #[repr(C)]
> #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
> /// PL011 Device Model in QEMU
> @@ -112,7 +107,8 @@ unsafe impl ObjectType for PL011State {
> impl ClassInitImpl<PL011Class> for PL011State {
> fn class_init(klass: &mut PL011Class) {
> - klass.device_id = DeviceId::ARM;
> + klass.device_id = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
> +
> <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
> }
> }
> @@ -628,7 +624,8 @@ pub struct PL011Luminary {
> impl ClassInitImpl<PL011Class> for PL011Luminary {
> fn class_init(klass: &mut PL011Class) {
> - klass.device_id = DeviceId::LUMINARY;
> + klass.device_id = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
> +
> <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
> }
> }
Still fine for me. :-)
Regards,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-18 14:46 ` Zhao Liu
@ 2024-12-18 16:01 ` Paolo Bonzini
0 siblings, 0 replies; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-18 16:01 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
[-- Attachment #1: Type: text/plain, Size: 790 bytes --]
Il mer 18 dic 2024, 15:32 Zhao Liu <zhao1.liu@intel.com> ha scritto:
> On Wed, Dec 18, 2024 at 11:26:35AM +0100, Paolo Bonzini wrote:
> > Date: Wed, 18 Dec 2024 11:26:35 +0100
> > From: Paolo Bonzini <pbonzini@redhat.com>
> > Subject: Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
> >
> > On 12/18/24 08:14, Paolo Bonzini wrote:
> > > Moving on to another topic, about the gap (or question :-)) where a
> > > child class inherits the ClassInitImpl trait from the parent,
> please see
> > > my test case example below: Doing something similar to
> SysBusDevice and
> > > DeviceState using a generic T outside of the QOM library would
> violate
> > > the orphan rule.
>
> BTW, maybe we could also squash my previous example in test? :-)
>
Sure.
Paolo
[-- Attachment #2: Type: text/html, Size: 1372 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-18 7:14 ` Paolo Bonzini
2024-12-18 10:26 ` Paolo Bonzini
@ 2024-12-18 14:30 ` Zhao Liu
1 sibling, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-18 14:30 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> No, Self is not PL011Class. Self is PL011State (or PL011Luminary/ and it
> always remains the same. What changes is *what part* of the class is
> overwritten, but the order of calls from qom/object.c follows the same
> logic in both C and Rust.
Thanks! Now I feel I see!
For C side, type_initialize() will allocate a new class instance by
`ti->class = g_malloc0(ti->class_size)`, then actually C side's parent
class_init will initialize that new class instance.
For Rust side, the initialization call chain will initialize Self's
embedded parent class, one by one.
So that's fine!
> > Maybe the confusion is because I implemented class_init twice instead of
> > > using a separate trait "PL011Impl"?
> >
> > Ah, yes! But I think the Rust call chain should not use class_init anymore
> > but should use a different method. This way, the original class_init would
> > only serve the C QOM. A separate trait might break the inheritance
> > relationship similar to ClassInitImpl.
> >
>
> Do you still think that this is the case?
No, now this patch is fine for me!
Thanks,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
2024-12-09 12:37 ` [PATCH 24/26] rust: qom: move device_id to PL011 class side Paolo Bonzini
2024-12-17 3:58 ` Zhao Liu
@ 2024-12-18 14:47 ` Zhao Liu
1 sibling, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-18 14:47 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:15PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:15 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 24/26] rust: qom: move device_id to PL011 class side
> X-Mailer: git-send-email 2.47.1
>
> There is no need to monkeypatch DeviceId::Luminary into the already-initialized
> PL011State. Instead, now that we can define a class hierarchy, we can define
> PL011Class and make device_id a field in there.
>
> There is also no need anymore to have "Arm" as zero, so change DeviceId into a
> wrapper for the array; all it does is provide an Index<hwaddr> implementation
> because arrays can only be indexed by usize.
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 59 +++++++++++++++-----------------
> 1 file changed, 28 insertions(+), 31 deletions(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 25/26] rust: qemu-api: add a module to wrap functions and zero-sized closures
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (23 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 24/26] rust: qom: move device_id to PL011 class side Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-17 15:03 ` Zhao Liu
2024-12-09 12:37 ` [PATCH 26/26] rust: callbacks: allow passing optional callbacks as () Paolo Bonzini
2024-12-09 16:24 ` [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Philippe Mathieu-Daudé
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
One recurring issue when writing Rust bindings is how to convert a Rust
function ("fn" or "impl Fn") to a C function, and how to pass around
"self" to a C function that only takes a void*.
An easy solution would be to store on the heap a pair consisting of
a pointer to the Rust function and the pointer to "self", but it is
possible to do better. If an "Fn" has zero size (that is, if it is a
zero-capture closures or a function pointer---which in turn includes all
methods), it is possible to build a generic Rust function that calls it
even if you only have the type; you don't need either the pointer to the
function itself (because the address of the code is part of the type)
or any closure data (because it has size zero).
Introduce a wrapper that provides the functionality of calling the
function given only its type.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/meson.build | 1 +
rust/qemu-api/src/callbacks.rs | 141 +++++++++++++++++++++++++++++++++
rust/qemu-api/src/lib.rs | 1 +
3 files changed, 143 insertions(+)
create mode 100644 rust/qemu-api/src/callbacks.rs
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 7ff408ad68e..8c82c5e96c2 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -17,6 +17,7 @@ _qemu_api_rs = static_library(
'src/lib.rs',
'src/bindings.rs',
'src/bitops.rs',
+ 'src/callbacks.rs',
'src/cell.rs',
'src/c_str.rs',
'src/irq.rs',
diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs
new file mode 100644
index 00000000000..6401d807198
--- /dev/null
+++ b/rust/qemu-api/src/callbacks.rs
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: MIT
+
+//! Utility functions to deal with callbacks from C to Rust.
+
+use std::{mem, ptr::NonNull};
+
+/// Trait for functions (types implementing [`Fn`]) that can be used as
+/// callbacks. These include both zero-capture closures and function pointers.
+///
+/// In Rust, calling a function through the `Fn` trait normally requires a
+/// `self` parameter, even though for zero-sized functions (including function
+/// pointers) the type itself contains all necessary information to call the
+/// function. This trait provides a `call` function that doesn't require `self`,
+/// allowing zero-sized functions to be called using only their type.
+///
+/// This enables zero-sized functions to be passed entirely through generic
+/// parameters and resolved at compile-time. A typical use is a function
+/// receiving an unused parameter of generic type `F` and calling it via
+/// `F::call` or passing it to another function via `func::<F>`.
+///
+/// QEMU uses this trick to create wrappers to C callbacks. The wrappers
+/// are needed to convert an opaque `*mut c_void` into a Rust reference,
+/// but they only have a single opaque that they can use. The `FnCall`
+/// trait makes it possible to use that opaque for `self` or any other
+/// reference:
+///
+/// ```ignore
+/// // The compiler creates a new `rust_bh_cb` wrapper for each function
+/// // passed to `qemu_bh_schedule_oneshot` below.
+/// unsafe extern "C" fn rust_bh_cb<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>()) }, ))
+/// }
+///
+/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`.
+/// // Using a reference allows usage in const context.
+/// fn qemu_bh_schedule_oneshot<T, F: for<'a> FnCall<(&'a T,)>>(_f: &F, opaque: &T) {
+/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::<T, F>;
+/// unsafe {
+/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void)
+/// }
+/// }
+/// ```
+///
+/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore
+/// compiled to a separate function ("monomorphization"). If you wanted
+/// to pass `self` as the opaque value, the generic parameters would be
+/// `rust_bh_cb::<Self, F>`.
+///
+/// `Args` is a tuple type whose types are the arguments of the function,
+/// while `R` is the returned type.
+///
+/// # Examples
+///
+/// ```
+/// # use qemu_api::callbacks::FnCall;
+/// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
+/// F::call((s,))
+/// }
+///
+/// let s: String = call_it(&str::to_owned, "hello world");
+/// assert_eq!(s, "hello world");
+/// ```
+///
+/// Note that the compiler will produce a different version of `call_it` for
+/// each function that is passed to it. Therefore the argument is not really
+/// used, except to decide what is `F` and what `F::call` does.
+///
+/// Attempting to pass a non-zero-sized closure causes a compile-time failure:
+///
+/// ```compile_fail
+/// # use qemu_api::callbacks::FnCall;
+/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String {
+/// # F::call((s,))
+/// # }
+/// let x: &'static str = "goodbye world";
+/// call_it(&move |_| String::from(x), "hello workd");
+/// ```
+///
+/// # Safety
+///
+/// Because `Self` is a zero-sized type, all instances of the type are
+/// equivalent. However, in addition to this, `Self` must have no invariants
+/// that could be violated by creating a reference to it.
+///
+/// This is always true for zero-capture closures and function pointers, as long
+/// as the code is able to name the function in the first place.
+pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
+ /// Referring to this internal constant asserts that the `Self` type is
+ /// zero-sized. Can be replaced by an inline const expression in
+ /// Rust 1.79.0+.
+ const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
+
+ /// Call the function with the arguments in args.
+ fn call(a: Args) -> R;
+}
+
+macro_rules! impl_call {
+ ($($args:ident,)* ) => (
+ // SAFETY: because each function is treated as a separate type,
+ // accessing `FnCall` is only possible in code that would be
+ // allowed to call the function.
+ unsafe impl<F, $($args,)* R> FnCall<($($args,)*), R> for F
+ where
+ F: 'static + Sync + Sized + Fn($($args, )*) -> R,
+ {
+ #[inline(always)]
+ fn call(a: ($($args,)*)) -> R {
+ let _: () = Self::ASSERT_ZERO_SIZED;
+
+ // SAFETY: the safety of this method is the condition for implementing
+ // `FnCall`. As to the `NonNull` idiom to create a zero-sized type,
+ // see https://github.com/rust-lang/libs-team/issues/292.
+ let f: &'static F = unsafe { &*NonNull::<Self>::dangling().as_ptr() };
+ let ($($args,)*) = a;
+ f($($args,)*)
+ }
+ }
+ )
+}
+
+impl_call!(_1, _2, _3, _4, _5,);
+impl_call!(_1, _2, _3, _4,);
+impl_call!(_1, _2, _3,);
+impl_call!(_1, _2,);
+impl_call!(_1,);
+impl_call!();
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ // The `_f` parameter is unused but it helps the compiler infer `F`.
+ fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String {
+ F::call(("hello world",))
+ }
+
+ #[test]
+ fn test_call() {
+ assert_eq!(do_test_call(&str::to_owned), "hello world")
+ }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 124bece0449..4b43e02c0f9 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -14,6 +14,7 @@
pub mod bitops;
pub mod c_str;
+pub mod callbacks;
pub mod cell;
pub mod irq;
pub mod module;
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 25/26] rust: qemu-api: add a module to wrap functions and zero-sized closures
2024-12-09 12:37 ` [PATCH 25/26] rust: qemu-api: add a module to wrap functions and zero-sized closures Paolo Bonzini
@ 2024-12-17 15:03 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-17 15:03 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
> +pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
> + /// Referring to this internal constant asserts that the `Self` type is
> + /// zero-sized. Can be replaced by an inline const expression in
> + /// Rust 1.79.0+.
> + const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
> +
> + /// Call the function with the arguments in args.
> + fn call(a: Args) -> R;
> +}
> +
> +macro_rules! impl_call {
> + ($($args:ident,)* ) => (
> + // SAFETY: because each function is treated as a separate type,
> + // accessing `FnCall` is only possible in code that would be
> + // allowed to call the function.
> + unsafe impl<F, $($args,)* R> FnCall<($($args,)*), R> for F
> + where
> + F: 'static + Sync + Sized + Fn($($args, )*) -> R,
> + {
> + #[inline(always)]
> + fn call(a: ($($args,)*)) -> R {
> + let _: () = Self::ASSERT_ZERO_SIZED;
> +
> + // SAFETY: the safety of this method is the condition for implementing
> + // `FnCall`. As to the `NonNull` idiom to create a zero-sized type,
> + // see https://github.com/rust-lang/libs-team/issues/292.
> + let f: &'static F = unsafe { &*NonNull::<Self>::dangling().as_ptr() };
Awesome! The definition of FnCall and this trick are both very elegant!
I've learned a lot.
> + let ($($args,)*) = a;
> + f($($args,)*)
> + }
> + }
> + )
> +}
> +
The examples and test both show the power of this callback pattern,
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH 26/26] rust: callbacks: allow passing optional callbacks as ()
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (24 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 25/26] rust: qemu-api: add a module to wrap functions and zero-sized closures Paolo Bonzini
@ 2024-12-09 12:37 ` Paolo Bonzini
2024-12-17 16:13 ` Zhao Liu
2024-12-09 16:24 ` [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Philippe Mathieu-Daudé
26 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 12:37 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
In some cases, callbacks are optional. Using "Some(function)" and "None"
does not work well, because when someone writes "None" the compiler does
not know what to use for "F" in "Option<F>".
Therefore, adopt () to mean a "null" callback. It is possible to enforce
that a callback is valid by adding a "let _: () = F::ASSERT_IS_SOME" before
the invocation of F::call.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/src/callbacks.rs | 97 ++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs
index 6401d807198..83c681d6478 100644
--- a/rust/qemu-api/src/callbacks.rs
+++ b/rust/qemu-api/src/callbacks.rs
@@ -76,6 +76,31 @@
/// 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
@@ -90,10 +115,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 use 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,
@@ -103,6 +188,8 @@ unsafe impl<F, $($args,)* R> FnCall<($($args,)*), R> for F
where
F: 'static + Sync + Sized + Fn($($args, )*) -> R,
{
+ const IS_SOME: bool = true;
+
#[inline(always)]
fn call(a: ($($args,)*)) -> R {
let _: () = Self::ASSERT_ZERO_SIZED;
@@ -138,4 +225,14 @@ fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String {
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_eq!(F::is_some(), true);
+ }
+
+ #[test]
+ fn test_is_some() {
+ do_test_is_some(&str::to_owned);
+ }
}
--
2.47.1
^ permalink raw reply related [flat|nested] 81+ messages in thread* Re: [PATCH 26/26] rust: callbacks: allow passing optional callbacks as ()
2024-12-09 12:37 ` [PATCH 26/26] rust: callbacks: allow passing optional callbacks as () Paolo Bonzini
@ 2024-12-17 16:13 ` Zhao Liu
2024-12-17 16:40 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-17 16:13 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 01:37:17PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 13:37:17 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 26/26] rust: callbacks: allow passing optional callbacks as
> ()
> X-Mailer: git-send-email 2.47.1
>
> In some cases, callbacks are optional. Using "Some(function)" and "None"
> does not work well, because when someone writes "None" the compiler does
> not know what to use for "F" in "Option<F>".
I understand the direct use case is MemoryRegionOps, which has optional
callbacks. However, I'm not quite sure how exactly it should be applied
to C bindings and how it will play with Option<callback>.
Could u pls provide a simple example?
> Therefore, adopt () to mean a "null" callback. It is possible to enforce
> that a callback is valid by adding a "let _: () = F::ASSERT_IS_SOME" before
> the invocation of F::call.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/qemu-api/src/callbacks.rs | 97 ++++++++++++++++++++++++++++++++++
> 1 file changed, 97 insertions(+)
>
> diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs
> index 6401d807198..83c681d6478 100644
> --- a/rust/qemu-api/src/callbacks.rs
> +++ b/rust/qemu-api/src/callbacks.rs
> @@ -76,6 +76,31 @@
> /// call_it(&move |_| String::from(x), "hello workd");
typo: s/workd/word/ (in previous patch :-))
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 26/26] rust: callbacks: allow passing optional callbacks as ()
2024-12-17 16:13 ` Zhao Liu
@ 2024-12-17 16:40 ` Paolo Bonzini
2024-12-18 7:09 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-17 16:40 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
[-- Attachment #1: Type: text/plain, Size: 1776 bytes --]
Il mar 17 dic 2024, 16:55 Zhao Liu <zhao1.liu@intel.com> ha scritto:
> On Mon, Dec 09, 2024 at 01:37:17PM +0100, Paolo Bonzini wrote:
> > Date: Mon, 9 Dec 2024 13:37:17 +0100
> > From: Paolo Bonzini <pbonzini@redhat.com>
> > Subject: [PATCH 26/26] rust: callbacks: allow passing optional callbacks
> as
> > ()
> > X-Mailer: git-send-email 2.47.1
> >
> > In some cases, callbacks are optional. Using "Some(function)" and "None"
> > does not work well, because when someone writes "None" the compiler does
> > not know what to use for "F" in "Option<F>".
>
> I understand the direct use case is MemoryRegionOps, which has optional
> callbacks. However, I'm not quite sure how exactly it should be applied
> to C bindings and how it will play with Option<callback>.
>
You wouldn't use Option<callback> at all, using () instead of None; the
difference is that () does not have a parameter while None does (and the
compiler cannot infer it). But I am okay with leaving this patch behind
until there's a need.
Paolo
Could u pls provide a simple example?
>
> > Therefore, adopt () to mean a "null" callback. It is possible to enforce
> > that a callback is valid by adding a "let _: () = F::ASSERT_IS_SOME"
> before
> > the invocation of F::call.
> >
> > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> > ---
> > rust/qemu-api/src/callbacks.rs | 97 ++++++++++++++++++++++++++++++++++
> > 1 file changed, 97 insertions(+)
> >
> > diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/
> callbacks.rs
> > index 6401d807198..83c681d6478 100644
> > --- a/rust/qemu-api/src/callbacks.rs
> > +++ b/rust/qemu-api/src/callbacks.rs
> > @@ -76,6 +76,31 @@
> > /// call_it(&move |_| String::from(x), "hello workd");
>
> typo: s/workd/word/ (in previous patch :-))
>
>
[-- Attachment #2: Type: text/html, Size: 3264 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 26/26] rust: callbacks: allow passing optional callbacks as ()
2024-12-17 16:40 ` Paolo Bonzini
@ 2024-12-18 7:09 ` Zhao Liu
2024-12-18 7:32 ` Paolo Bonzini
0 siblings, 1 reply; 81+ messages in thread
From: Zhao Liu @ 2024-12-18 7:09 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Tue, Dec 17, 2024 at 05:40:14PM +0100, Paolo Bonzini wrote:
> Date: Tue, 17 Dec 2024 17:40:14 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 26/26] rust: callbacks: allow passing optional
> callbacks as ()
>
> Il mar 17 dic 2024, 16:55 Zhao Liu <zhao1.liu@intel.com> ha scritto:
>
> > On Mon, Dec 09, 2024 at 01:37:17PM +0100, Paolo Bonzini wrote:
> > > Date: Mon, 9 Dec 2024 13:37:17 +0100
> > > From: Paolo Bonzini <pbonzini@redhat.com>
> > > Subject: [PATCH 26/26] rust: callbacks: allow passing optional callbacks
> > as
> > > ()
> > > X-Mailer: git-send-email 2.47.1
> > >
> > > In some cases, callbacks are optional. Using "Some(function)" and "None"
> > > does not work well, because when someone writes "None" the compiler does
> > > not know what to use for "F" in "Option<F>".
> >
> > I understand the direct use case is MemoryRegionOps, which has optional
> > callbacks. However, I'm not quite sure how exactly it should be applied
> > to C bindings and how it will play with Option<callback>.
> >
>
> You wouldn't use Option<callback> at all, using () instead of None; the
> difference is that () does not have a parameter while None does (and the
> compiler cannot infer it). But I am okay with leaving this patch behind
> until there's a need.
Am I using the wrong terminology? Function pointers in a structure should
be called a vtable, rather than callbacks (for example, methods in TypeInfo,
read/write methods in MemoryRegionOps). Callbacks are typically function
pointers used as function parameters (for example, timer/gpio). So, is the
callback implementation here only used for the latter case?
Thanks,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 26/26] rust: callbacks: allow passing optional callbacks as ()
2024-12-18 7:09 ` Zhao Liu
@ 2024-12-18 7:32 ` Paolo Bonzini
2024-12-18 15:09 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-18 7:32 UTC (permalink / raw)
To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Junjie Mao
[-- Attachment #1: Type: text/plain, Size: 1253 bytes --]
Il mer 18 dic 2024, 07:50 Zhao Liu <zhao1.liu@intel.com> ha scritto:
> Am I using the wrong terminology? Function pointers in a structure should
> be called a vtable, rather than callbacks (for example, methods in
> TypeInfo,
> read/write methods in MemoryRegionOps). Callbacks are typically function
> pointers used as function parameters (for example, timer/gpio). So, is the
> callback implementation here only used for the latter case?
>
The callback implementation is not used for QOM indeed. In that case, using
FnCall would require something like
const UNPARENT: impl FnCall((&Self,));
which does not exist as far as I know?
MemoryRegionOps is a mix of callbacks and a vtable. From the Rust point of
view, with the API that uses the builder pattern, MemoryRegionOps (and
VMStateDescription too) would be closer to callbacks. Instead when you use
traits and fill in the class object, that's clearly a vtable.
But in this sense MemoryRegionOps do not need optional callbacks. You just
don't call the ops.read() method if you don't need to set a read callback
for example. So I am not sure if anything that is planned right now needs
the optional callbacks. It's good to have the patch for the future but it's
not necessary right now.
Paolo
[-- Attachment #2: Type: text/html, Size: 1893 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 26/26] rust: callbacks: allow passing optional callbacks as ()
2024-12-18 7:32 ` Paolo Bonzini
@ 2024-12-18 15:09 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-18 15:09 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust, Junjie Mao
On Wed, Dec 18, 2024 at 08:32:59AM +0100, Paolo Bonzini wrote:
> Date: Wed, 18 Dec 2024 08:32:59 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 26/26] rust: callbacks: allow passing optional
> callbacks as ()
>
> Il mer 18 dic 2024, 07:50 Zhao Liu <zhao1.liu@intel.com> ha scritto:
>
> > Am I using the wrong terminology? Function pointers in a structure should
> > be called a vtable, rather than callbacks (for example, methods in
> > TypeInfo,
> > read/write methods in MemoryRegionOps). Callbacks are typically function
> > pointers used as function parameters (for example, timer/gpio). So, is the
> > callback implementation here only used for the latter case?
> >
>
> The callback implementation is not used for QOM indeed. In that case, using
> FnCall would require something like
>
> const UNPARENT: impl FnCall((&Self,));
>
> which does not exist as far as I know?
Yes, it's incorrect. (I know you have some magic, so I have this question
to see your idea.)
> MemoryRegionOps is a mix of callbacks and a vtable. From the Rust point of
> view, with the API that uses the builder pattern, MemoryRegionOps (and
> VMStateDescription too) would be closer to callbacks. Instead when you use
> traits and fill in the class object, that's clearly a vtable.
>
> But in this sense MemoryRegionOps do not need optional callbacks. You just
> don't call the ops.read() method if you don't need to set a read callback
> for example. So I am not sure if anything that is planned right now needs
> the optional callbacks. It's good to have the patch for the future but it's
> not necessary right now.
Thanks for your explaination. I agree your plan. I also think it will be
useful in the future.
Regards,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 00/26] rust: bundle of prerequisites for HPET implementation
2024-12-09 12:36 [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Paolo Bonzini
` (25 preceding siblings ...)
2024-12-09 12:37 ` [PATCH 26/26] rust: callbacks: allow passing optional callbacks as () Paolo Bonzini
@ 2024-12-09 16:24 ` Philippe Mathieu-Daudé
2024-12-09 17:23 ` Paolo Bonzini
26 siblings, 1 reply; 81+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-12-09 16:24 UTC (permalink / raw)
To: Paolo Bonzini, qemu-devel; +Cc: qemu-rust, Zhao Liu, Junjie Mao
On 9/12/24 13:36, Paolo Bonzini wrote:
> Paolo Bonzini (26):
> bql: check that the BQL is not dropped within marked sections
> rust: cell: add BQL-enforcing Cell variant
> rust: cell: add BQL-enforcing RefCell variant
> rust: define prelude
> rust: add bindings for interrupt sources
> rust: add a bit operation module
> rust: qom: add default definitions for ObjectImpl
> rust: qom: rename Class trait to ClassInitImpl
> rust: qom: convert type_info! macro to an associated const
> rust: qom: move ClassInitImpl to the instance side
> rust: qdev: move device_class_init! body to generic function,
> ClassInitImpl implementation to macro
> rust: qdev: move bridge for realize and reset functions out of pl011
> rust: qom: automatically use Drop trait to implement instance_finalize
> rust: qom: move bridge for TypeInfo functions out of pl011
> rust: qom: split ObjectType from ObjectImpl trait
> rust: qom: change the parent type to an associated type
> rust: qom: put class_init together from multiple ClassInitImpl<>
> rust: qom: add possibility of overriding unparent
> rust: rename qemu-api modules to follow C code a bit more
> rust: re-export C types from qemu-api submodules
> rust: tests: allow writing more than one test
> rust: qom: add casting functionality
> rust: qom: add initial subset of methods on Object
> rust: qom: move device_id to PL011 class side
> rust: qemu-api: add a module to wrap functions and zero-sized closures
> rust: callbacks: allow passing optional callbacks as ()
Am I correct some patches are from Zhao Liu and got the
authorship inadvertently removed?
^ permalink raw reply [flat|nested] 81+ messages in thread* Re: [PATCH 00/26] rust: bundle of prerequisites for HPET implementation
2024-12-09 16:24 ` [PATCH 00/26] rust: bundle of prerequisites for HPET implementation Philippe Mathieu-Daudé
@ 2024-12-09 17:23 ` Paolo Bonzini
2024-12-10 2:38 ` Zhao Liu
0 siblings, 1 reply; 81+ messages in thread
From: Paolo Bonzini @ 2024-12-09 17:23 UTC (permalink / raw)
To: Philippe Mathieu-Daudé; +Cc: qemu-devel, qemu-rust, Zhao Liu, Junjie Mao
On Mon, Dec 9, 2024 at 5:24 PM Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> On 9/12/24 13:36, Paolo Bonzini wrote:
>
> > Paolo Bonzini (26):
> > bql: check that the BQL is not dropped within marked sections
> > rust: cell: add BQL-enforcing Cell variant
> > rust: cell: add BQL-enforcing RefCell variant
> > rust: define prelude
> > rust: add bindings for interrupt sources
> > rust: add a bit operation module
> > rust: qom: add default definitions for ObjectImpl
> > rust: qom: rename Class trait to ClassInitImpl
> > rust: qom: convert type_info! macro to an associated const
> > rust: qom: move ClassInitImpl to the instance side
> > rust: qdev: move device_class_init! body to generic function,
> > ClassInitImpl implementation to macro
> > rust: qdev: move bridge for realize and reset functions out of pl011
> > rust: qom: automatically use Drop trait to implement instance_finalize
> > rust: qom: move bridge for TypeInfo functions out of pl011
> > rust: qom: split ObjectType from ObjectImpl trait
> > rust: qom: change the parent type to an associated type
> > rust: qom: put class_init together from multiple ClassInitImpl<>
> > rust: qom: add possibility of overriding unparent
> > rust: rename qemu-api modules to follow C code a bit more
> > rust: re-export C types from qemu-api submodules
> > rust: tests: allow writing more than one test
> > rust: qom: add casting functionality
> > rust: qom: add initial subset of methods on Object
> > rust: qom: move device_id to PL011 class side
> > rust: qemu-api: add a module to wrap functions and zero-sized closures
> > rust: callbacks: allow passing optional callbacks as ()
>
> Am I correct some patches are from Zhao Liu and got the
> authorship inadvertently removed?
Just one, which is co-authored; it has
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
So I should have put either Co-authored-by him or left him as the
primary author.
Paolo
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH 00/26] rust: bundle of prerequisites for HPET implementation
2024-12-09 17:23 ` Paolo Bonzini
@ 2024-12-10 2:38 ` Zhao Liu
0 siblings, 0 replies; 81+ messages in thread
From: Zhao Liu @ 2024-12-10 2:38 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Philippe Mathieu-Daudé, qemu-devel, qemu-rust, Junjie Mao
On Mon, Dec 09, 2024 at 06:23:21PM +0100, Paolo Bonzini wrote:
> Date: Mon, 9 Dec 2024 18:23:21 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 00/26] rust: bundle of prerequisites for HPET
> implementation
>
> On Mon, Dec 9, 2024 at 5:24 PM Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
> >
> > On 9/12/24 13:36, Paolo Bonzini wrote:
> >
> > > Paolo Bonzini (26):
> > > bql: check that the BQL is not dropped within marked sections
> > > rust: cell: add BQL-enforcing Cell variant
> > > rust: cell: add BQL-enforcing RefCell variant
> > > rust: define prelude
> > > rust: add bindings for interrupt sources
> > > rust: add a bit operation module
> > > rust: qom: add default definitions for ObjectImpl
> > > rust: qom: rename Class trait to ClassInitImpl
> > > rust: qom: convert type_info! macro to an associated const
> > > rust: qom: move ClassInitImpl to the instance side
> > > rust: qdev: move device_class_init! body to generic function,
> > > ClassInitImpl implementation to macro
> > > rust: qdev: move bridge for realize and reset functions out of pl011
> > > rust: qom: automatically use Drop trait to implement instance_finalize
> > > rust: qom: move bridge for TypeInfo functions out of pl011
> > > rust: qom: split ObjectType from ObjectImpl trait
> > > rust: qom: change the parent type to an associated type
> > > rust: qom: put class_init together from multiple ClassInitImpl<>
> > > rust: qom: add possibility of overriding unparent
> > > rust: rename qemu-api modules to follow C code a bit more
> > > rust: re-export C types from qemu-api submodules
> > > rust: tests: allow writing more than one test
> > > rust: qom: add casting functionality
> > > rust: qom: add initial subset of methods on Object
> > > rust: qom: move device_id to PL011 class side
> > > rust: qemu-api: add a module to wrap functions and zero-sized closures
> > > rust: callbacks: allow passing optional callbacks as ()
> >
> > Am I correct some patches are from Zhao Liu and got the
> > authorship inadvertently removed?
>
> Just one, which is co-authored; it has
>
> Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
> Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>
> So I should have put either Co-authored-by him or left him as the
> primary author.
>
Thank you Paolo! You deserve to be credited as the author. It's
completely different from my previous solution, so it's all your
work! HPET will be a user.
Regards,
Zhao
^ permalink raw reply [flat|nested] 81+ messages in thread