From 5f9976486970b0fec50ff4c07da7af620cd7d0a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Dec 2024 07:42:44 -0600 Subject: rust/qemu-api: Use device_class_set_props_n This means we can update declare_properties to drop the zero terminator at the end of the array as well. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson Tested-by: Lei Yang Link: https://lore.kernel.org/r/20241218134251.4724-18-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/device_class.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs index 03d03fe..c98f0b2 100644 --- a/rust/qemu-api/src/device_class.rs +++ b/rust/qemu-api/src/device_class.rs @@ -7,7 +7,6 @@ use std::{ffi::CStr, os::raw::c_void}; use crate::{ bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, prelude::*, - zeroable::Zeroable, }; /// Trait providing the contents of [`DeviceClass`]. @@ -31,7 +30,7 @@ pub trait DeviceImpl { /// 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 @@ -87,7 +86,10 @@ pub unsafe extern "C" fn rust_device_class_init( if let Some(vmsd) = ::vmsd() { dc.vmsd = vmsd; } - bindings::device_class_set_props(dc, ::properties().as_ptr()); + let prop = ::properties(); + if !prop.is_empty() { + bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + } } } @@ -134,7 +136,7 @@ macro_rules! define_property { macro_rules! declare_properties { ($ident:ident, $($prop:expr),*$(,)*) => { pub static $ident: [$crate::bindings::Property; { - let mut len = 1; + let mut len = 0; $({ _ = stringify!($prop); len += 1; @@ -142,7 +144,6 @@ macro_rules! declare_properties { len }] = [ $($prop),*, - $crate::zeroable::Zeroable::ZERO, ]; }; } -- cgit v1.1 From 6dd818fbbbe3efc63889e7d811ac6b70e788c629 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Nov 2024 15:19:23 +0100 Subject: rust: qom: put class_init together from multiple ClassInitImpl<> 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. In order to avoid having to implement (for example) ClassInitImpl, 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. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/definitions.rs | 111 ++++++++++++++++++++++++++++++-------- rust/qemu-api/src/device_class.rs | 50 ++++++----------- rust/qemu-api/src/sysbus.rs | 18 ++++++- 3 files changed, 124 insertions(+), 55 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index df91a2e..13f8f6f 100644 --- a/rust/qemu-api/src/definitions.rs +++ b/rust/qemu-api/src/definitions.rs @@ -26,6 +26,16 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::() }) } +unsafe extern "C" fn rust_class_init>( + klass: *mut ObjectClass, + _data: *mut c_void, +) { + // SAFETY: klass is a T::Class, since rust_class_init + // is called from QOM core as the class_init function + // for class T + T::class_init(unsafe { &mut *klass.cast::() }) +} + /// Trait exposed by all structs corresponding to QOM objects. /// /// # Safety @@ -50,7 +60,8 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { /// - likewise, the first field of the `Class` must be of the class struct /// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`. pub unsafe trait ObjectType: Sized { - /// The QOM class object corresponding to this struct. Not used yet. + /// 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 @@ -59,7 +70,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 { /// The parent of the type. This should match the first field of /// the struct that implements `ObjectImpl`: type ParentType: ObjectType; @@ -80,6 +91,15 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// `INSTANCE_INIT` functions have been called. const INSTANCE_POST_INIT: Option = 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(), @@ -96,37 +116,86 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { instance_finalize: Self::INSTANCE_FINALIZE, abstract_: Self::ABSTRACT, class_size: core::mem::size_of::(), - class_init: ::CLASS_INIT, - class_base_init: ::CLASS_BASE_INIT, + class_init: Some(rust_class_init::), + 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. +/// +/// 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)`>`. +/// Such implementations are made in one of two ways. +/// +/// 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::device_class::DeviceImpl), and `ClassInitImpl` is +/// provided by blanket implementations that operate on all implementors of the +/// `*Impl`* trait. For example: +/// +/// ```ignore +/// impl ClassInitImpl for T +/// where +/// T: DeviceImpl, +/// ``` /// -/// The Rust implementation of methods will usually come from a trait -/// like [`ObjectImpl`] or [`DeviceImpl`](crate::device_class::DeviceImpl). -pub trait ClassInitImpl { - /// Function that is called after all parent class initialization - /// has occurred. On entry, the virtual method pointers are set to +/// The other case is when manual implementation of the trait is needed. +/// This covers the following cases: +/// +/// * if a class implements a QOM interface, the Rust code _has_ to define its +/// own class struct `FooClass` and implement `ClassInitImpl`. +/// `ClassInitImpl`'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` for each subclass and not have a +/// `YourSuperclassImpl` trait at all. +/// +/// ```ignore +/// impl ClassInitImpl for YourSubclass { +/// fn class_init(klass: &mut YourSuperclass) { +/// klass.some_method = Some(Self::some_method); +/// >::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 { + /// 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; - - /// 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 c98f0b2..dcec548 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 // 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::*, }; @@ -44,7 +45,7 @@ pub trait DeviceImpl { /// # Safety /// /// This function is only called through the QOM machinery and -/// the `impl_device_class!` macro. +/// used by the `ClassInitImpl` 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. @@ -65,49 +66,32 @@ unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { 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( - klass: *mut ObjectClass, - _: *mut c_void, -) { - let mut dc = ::core::ptr::NonNull::new(klass.cast::()).unwrap(); - unsafe { - let dc = dc.as_mut(); +impl ClassInitImpl for T +where + T: DeviceImpl, +{ + fn class_init(dc: &mut DeviceClass) { if ::REALIZE.is_some() { dc.realize = Some(rust_realize_fn::); } if ::RESET.is_some() { - bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); + unsafe { + bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); + } } if let Some(vmsd) = ::vmsd() { dc.vmsd = vmsd; } let prop = ::properties(); if !prop.is_empty() { - bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + unsafe { + bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + } } } } #[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; - } - }; -} - -#[macro_export] macro_rules! define_property { ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { $crate::bindings::Property { @@ -148,8 +132,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 5ee0685..5d15b31 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -6,7 +6,13 @@ use std::{ffi::CStr, ptr::addr_of}; 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 ClassInitImpl for T +where + T: ClassInitImpl, +{ + fn class_init(sdc: &mut SysBusDeviceClass) { + >::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 { -- cgit v1.1 From cb36da9bd84076470f36da56542e85a2436e3d95 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Oct 2024 15:00:26 +0100 Subject: rust: qom: add possibility of overriding unparent Add a blanket definition of ClassInitImpl that thunks ObjectImpl::UNPARENT and overrides it in ObjectClass if it is not None. ClassInitImpl 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. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/definitions.rs | 44 ++++++++++++++++++++++++++++++++++++--- rust/qemu-api/src/device_class.rs | 6 ++++-- 2 files changed, 45 insertions(+), 5 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs index 13f8f6f..a2481c1 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(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init @@ -121,6 +121,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { class_data: core::ptr::null_mut(), interfaces: core::ptr::null_mut(), }; + + // methods on ObjectClass + const UNPARENT: Option = None; } /// Internal trait used to automatically fill in a class struct. @@ -134,7 +137,8 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// /// 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)`>`. /// Such implementations are made in one of two ways. /// /// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` @@ -147,9 +151,13 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// ```ignore /// impl ClassInitImpl for T /// where -/// T: DeviceImpl, +/// T: ClassInitImpl + DeviceImpl, /// ``` /// +/// The bound on `ClassInitImpl` is needed so that, +/// after initializing the `DeviceClass` part of the class struct, +/// the parent [`ObjectClass`] is initialized as well. +/// /// The other case is when manual implementation of the trait is needed. /// This covers the following cases: /// @@ -235,3 +243,33 @@ macro_rules! module_init { } }; } + +/// # 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(dev: *mut Object) { + unsafe { + assert!(!dev.is_null()); + let state = core::ptr::NonNull::new_unchecked(dev.cast::()); + T::UNPARENT.unwrap()(state.as_ref()); + } +} + +impl ClassInitImpl for T +where + T: ObjectImpl, +{ + fn class_init(oc: &mut ObjectClass) { + if ::UNPARENT.is_some() { + oc.unparent = Some(rust_unparent_fn::); + } + } +} + +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 dcec548..a9965d1 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::*, }; @@ -68,7 +68,7 @@ unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { impl ClassInitImpl for T where - T: DeviceImpl, + T: ClassInitImpl + DeviceImpl, { fn class_init(dc: &mut DeviceClass) { if ::REALIZE.is_some() { @@ -88,6 +88,8 @@ where bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); } } + + >::class_init(&mut dc.parent_class); } } -- cgit v1.1 From 4aed0296b307b6e2e3b7d38ee6c5204cf2dfe1ca Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Oct 2024 14:15:27 +0100 Subject: rust: rename qemu-api modules to follow C code a bit more 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. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/definitions.rs | 275 -------------------------------------- rust/qemu-api/src/device_class.rs | 141 ------------------- rust/qemu-api/src/lib.rs | 5 +- rust/qemu-api/src/module.rs | 43 ++++++ rust/qemu-api/src/prelude.rs | 2 +- rust/qemu-api/src/qdev.rs | 143 ++++++++++++++++++++ rust/qemu-api/src/qom.rs | 263 ++++++++++++++++++++++++++++++++++++ rust/qemu-api/src/sysbus.rs | 2 +- 8 files changed, 454 insertions(+), 420 deletions(-) delete mode 100644 rust/qemu-api/src/definitions.rs delete mode 100644 rust/qemu-api/src/device_class.rs create mode 100644 rust/qemu-api/src/module.rs create mode 100644 rust/qemu-api/src/qdev.rs create mode 100644 rust/qemu-api/src/qom.rs (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs deleted file mode 100644 index a2481c1..0000000 --- a/rust/qemu-api/src/definitions.rs +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Definitions required by QEMU when registering a device. - -use std::{ffi::CStr, os::raw::c_void}; - -use crate::bindings::{self, Object, ObjectClass, TypeInfo}; - -unsafe extern "C" fn rust_instance_init(obj: *mut Object) { - // SAFETY: obj is an instance of T, since rust_instance_init - // is called from QOM core as the instance_init function - // for class T - unsafe { T::INSTANCE_INIT.unwrap()(&mut *obj.cast::()) } -} - -unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { - // SAFETY: obj is an instance of T, since rust_instance_post_init - // 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::() }) -} - -unsafe extern "C" fn rust_class_init>( - klass: *mut ObjectClass, - _data: *mut c_void, -) { - // SAFETY: klass is a T::Class, since rust_class_init - // is called from QOM core as the class_init function - // for class T - T::class_init(unsafe { &mut *klass.cast::() }) -} - -/// Trait exposed by all structs corresponding to QOM objects. -/// -/// # Safety -/// -/// For classes declared in C: -/// -/// - `Class` and `TYPE` must match the data in the `TypeInfo`; -/// -/// - the first field of the struct must be of the instance type corresponding -/// to the superclass, as declared in the `TypeInfo` -/// -/// - likewise, the first field of the `Class` struct must be of the class type -/// corresponding to the superclass -/// -/// For classes declared in Rust and implementing [`ObjectImpl`]: -/// -/// - the struct must be `#[repr(C)]`; -/// -/// - the first field of the struct must be of the instance struct corresponding -/// to the superclass, which is `ObjectImpl::ParentType` -/// -/// - likewise, the first field of the `Class` must be of the class struct -/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`. -pub unsafe trait ObjectType: Sized { - /// The QOM class object corresponding to this struct. 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 - /// 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`: - type ParentType: ObjectType; - - /// Whether the object can be instantiated - const ABSTRACT: bool = false; - const INSTANCE_FINALIZE: Option = 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` would be a better description. - const INSTANCE_INIT: Option = None; - - /// Function that is called to finish initialization of an object, once - /// `INSTANCE_INIT` functions have been called. - const INSTANCE_POST_INIT: Option = 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(), - instance_size: core::mem::size_of::(), - instance_align: core::mem::align_of::(), - instance_init: match Self::INSTANCE_INIT { - None => None, - Some(_) => Some(rust_instance_init::), - }, - instance_post_init: match Self::INSTANCE_POST_INIT { - None => None, - Some(_) => Some(rust_instance_post_init::), - }, - instance_finalize: Self::INSTANCE_FINALIZE, - abstract_: Self::ABSTRACT, - class_size: core::mem::size_of::(), - class_init: Some(rust_class_init::), - class_base_init: Self::CLASS_BASE_INIT, - class_data: core::ptr::null_mut(), - interfaces: core::ptr::null_mut(), - }; - - // methods on ObjectClass - const UNPARENT: Option = None; -} - -/// 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; this trait takes care of -/// initializing the `T` part of the class struct, for the type that -/// implements the trait. -/// -/// 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)`>`. -/// Such implementations are made in one of two ways. -/// -/// 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::device_class::DeviceImpl), and `ClassInitImpl` is -/// provided by blanket implementations that operate on all implementors of the -/// `*Impl`* trait. For example: -/// -/// ```ignore -/// impl ClassInitImpl for T -/// where -/// T: ClassInitImpl + DeviceImpl, -/// ``` -/// -/// The bound on `ClassInitImpl` is needed so that, -/// after initializing the `DeviceClass` part of the class struct, -/// the parent [`ObjectClass`] is initialized as well. -/// -/// The other case is when manual implementation of the trait is needed. -/// This covers the following cases: -/// -/// * if a class implements a QOM interface, the Rust code _has_ to define its -/// own class struct `FooClass` and implement `ClassInitImpl`. -/// `ClassInitImpl`'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` for each subclass and not have a -/// `YourSuperclassImpl` trait at all. -/// -/// ```ignore -/// impl ClassInitImpl for YourSubclass { -/// fn class_init(klass: &mut YourSuperclass) { -/// klass.some_method = Some(Self::some_method); -/// >::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 { - /// 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. - /// - /// 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] -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 -/// 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(dev: *mut Object) { - unsafe { - assert!(!dev.is_null()); - let state = core::ptr::NonNull::new_unchecked(dev.cast::()); - T::UNPARENT.unwrap()(state.as_ref()); - } -} - -impl ClassInitImpl for T -where - T: ObjectImpl, -{ - fn class_init(oc: &mut ObjectClass) { - if ::UNPARENT.is_some() { - oc.unparent = Some(rust_unparent_fn::); - } - } -} - -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 deleted file mode 100644 index a9965d1..0000000 --- a/rust/qemu-api/src/device_class.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::ffi::CStr; - -use crate::{ - bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription}, - definitions::ClassInitImpl, - prelude::*, -}; - -/// 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 = 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 = 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] { - &[] - } - - /// A `VMStateDescription` providing the migration format for the device - /// Not a `const` because referencing statics in constants is unstable - /// until Rust 1.83.0. - fn vmsd() -> Option<&'static VMStateDescription> { - None - } -} - -/// # Safety -/// -/// This function is only called through the QOM machinery and -/// used by the `ClassInitImpl` 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. -unsafe extern "C" fn rust_realize_fn(dev: *mut DeviceState, _errp: *mut *mut Error) { - assert!(!dev.is_null()); - let state = dev.cast::(); - 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(dev: *mut DeviceState) { - assert!(!dev.is_null()); - let state = dev.cast::(); - T::RESET.unwrap()(unsafe { &mut *state }); -} - -impl ClassInitImpl for T -where - T: ClassInitImpl + DeviceImpl, -{ - fn class_init(dc: &mut DeviceClass) { - if ::REALIZE.is_some() { - dc.realize = Some(rust_realize_fn::); - } - if ::RESET.is_some() { - unsafe { - bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); - } - } - if let Some(vmsd) = ::vmsd() { - dc.vmsd = vmsd; - } - let prop = ::properties(); - if !prop.is_empty() { - unsafe { - bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); - } - } - - >::class_init(&mut dc.parent_class); - } -} - -#[macro_export] -macro_rules! define_property { - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: $crate::offset_of!($state, $field) as isize, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..$crate::zeroable::Zeroable::ZERO - } - }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: $crate::offset_of!($state, $field) as isize, - set_default: false, - ..$crate::zeroable::Zeroable::ZERO - } - }; -} - -#[macro_export] -macro_rules! declare_properties { - ($ident:ident, $($prop:expr),*$(,)*) => { - pub static $ident: [$crate::bindings::Property; { - let mut len = 0; - $({ - _ = stringify!($prop); - len += 1; - })* - len - }] = [ - $($prop),*, - ]; - }; -} - -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/lib.rs b/rust/qemu-api/src/lib.rs index 9e007e1..124bece 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -15,10 +15,11 @@ pub mod prelude; pub mod bitops; pub mod c_str; pub mod cell; -pub mod definitions; -pub mod device_class; pub mod irq; +pub mod 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 0000000..fa5cea3 --- /dev/null +++ b/rust/qemu-api/src/module.rs @@ -0,0 +1,43 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// 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 1b8677b..5cc41f0 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,4 +7,4 @@ pub use crate::bitops::IntegerExt; 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/qdev.rs b/rust/qemu-api/src/qdev.rs new file mode 100644 index 0000000..ad4c12d --- /dev/null +++ b/rust/qemu-api/src/qdev.rs @@ -0,0 +1,143 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// 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}, + prelude::*, + qom::ClassInitImpl, +}; + +/// 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 = 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 = 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] { + &[] + } + + /// A `VMStateDescription` providing the migration format for the device + /// Not a `const` because referencing statics in constants is unstable + /// until Rust 1.83.0. + fn vmsd() -> Option<&'static VMStateDescription> { + None + } +} + +/// # Safety +/// +/// This function is only called through the QOM machinery and +/// used by the `ClassInitImpl` 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. +unsafe extern "C" fn rust_realize_fn(dev: *mut DeviceState, _errp: *mut *mut Error) { + assert!(!dev.is_null()); + let state = dev.cast::(); + 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(dev: *mut DeviceState) { + assert!(!dev.is_null()); + let state = dev.cast::(); + T::RESET.unwrap()(unsafe { &mut *state }); +} + +impl ClassInitImpl for T +where + T: ClassInitImpl + DeviceImpl, +{ + fn class_init(dc: &mut DeviceClass) { + if ::REALIZE.is_some() { + dc.realize = Some(rust_realize_fn::); + } + if ::RESET.is_some() { + unsafe { + bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); + } + } + if let Some(vmsd) = ::vmsd() { + dc.vmsd = vmsd; + } + let prop = ::properties(); + if !prop.is_empty() { + unsafe { + bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + } + } + + >::class_init(&mut dc.parent_class); + } +} + +#[macro_export] +macro_rules! define_property { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: $crate::offset_of!($state, $field) as isize, + set_default: true, + defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, + ..$crate::zeroable::Zeroable::ZERO + } + }; + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: $crate::offset_of!($state, $field) as isize, + set_default: false, + ..$crate::zeroable::Zeroable::ZERO + } + }; +} + +#[macro_export] +macro_rules! declare_properties { + ($ident:ident, $($prop:expr),*$(,)*) => { + pub static $ident: [$crate::bindings::Property; { + let mut len = 0; + $({ + _ = stringify!($prop); + len += 1; + })* + len + }] = [ + $($prop),*, + ]; + }; +} + +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/qom.rs b/rust/qemu-api/src/qom.rs new file mode 100644 index 0000000..2222d1a --- /dev/null +++ b/rust/qemu-api/src/qom.rs @@ -0,0 +1,263 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! 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 leaf class only needs a struct holding instance state. The struct must +//! implement the [`ObjectType`] trait, as well as any `*Impl` traits that exist +//! for its superclasses. +//! +//! If a class has subclasses, it 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 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`. This fills the vtable in the class struct; +//! the source for this is the `*Impl` trait; the associated consts and +//! functions if needed are wrapped to map C types into Rust types. + +use std::{ffi::CStr, os::raw::c_void}; + +use crate::bindings::{self, Object, ObjectClass, TypeInfo}; + +unsafe extern "C" fn rust_instance_init(obj: *mut Object) { + // SAFETY: obj is an instance of T, since rust_instance_init + // is called from QOM core as the instance_init function + // for class T + unsafe { T::INSTANCE_INIT.unwrap()(&mut *obj.cast::()) } +} + +unsafe extern "C" fn rust_instance_post_init(obj: *mut Object) { + // SAFETY: obj is an instance of T, since rust_instance_post_init + // 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::() }) +} + +unsafe extern "C" fn rust_class_init>( + klass: *mut ObjectClass, + _data: *mut c_void, +) { + // SAFETY: klass is a T::Class, since rust_class_init + // is called from QOM core as the class_init function + // for class T + T::class_init(unsafe { &mut *klass.cast::() }) +} + +/// Trait exposed by all structs corresponding to QOM objects. +/// +/// # Safety +/// +/// For classes declared in C: +/// +/// - `Class` and `TYPE` must match the data in the `TypeInfo`; +/// +/// - the first field of the struct must be of the instance type corresponding +/// to the superclass, as declared in the `TypeInfo` +/// +/// - likewise, the first field of the `Class` struct must be of the class type +/// corresponding to the superclass +/// +/// For classes declared in Rust and implementing [`ObjectImpl`]: +/// +/// - the struct must be `#[repr(C)]`; +/// +/// - the first field of the struct must be of the instance struct corresponding +/// to the superclass, which is `ObjectImpl::ParentType` +/// +/// - likewise, the first field of the `Class` must be of the class struct +/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`. +pub unsafe trait ObjectType: Sized { + /// The QOM class object corresponding to this struct. 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 + /// 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`: + type ParentType: ObjectType; + + /// Whether the object can be instantiated + const ABSTRACT: bool = false; + const INSTANCE_FINALIZE: Option = 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` would be a better description. + const INSTANCE_INIT: Option = None; + + /// Function that is called to finish initialization of an object, once + /// `INSTANCE_INIT` functions have been called. + const INSTANCE_POST_INIT: Option = 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(), + instance_size: core::mem::size_of::(), + instance_align: core::mem::align_of::(), + instance_init: match Self::INSTANCE_INIT { + None => None, + Some(_) => Some(rust_instance_init::), + }, + instance_post_init: match Self::INSTANCE_POST_INIT { + None => None, + Some(_) => Some(rust_instance_post_init::), + }, + instance_finalize: Self::INSTANCE_FINALIZE, + abstract_: Self::ABSTRACT, + class_size: core::mem::size_of::(), + class_init: Some(rust_class_init::), + class_base_init: Self::CLASS_BASE_INIT, + class_data: core::ptr::null_mut(), + interfaces: core::ptr::null_mut(), + }; + + // methods on ObjectClass + const UNPARENT: Option = None; +} + +/// 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; this trait takes care of +/// initializing the `T` part of the class struct, for the type that +/// implements the trait. +/// +/// 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)`>`. +/// Such implementations are made in one of two ways. +/// +/// 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 ClassInitImpl for T +/// where +/// T: ClassInitImpl + DeviceImpl, +/// ``` +/// +/// The bound on `ClassInitImpl` is needed so that, +/// after initializing the `DeviceClass` part of the class struct, +/// the parent [`ObjectClass`] is initialized as well. +/// +/// The other case is when manual implementation of the trait is needed. +/// This covers the following cases: +/// +/// * if a class implements a QOM interface, the Rust code _has_ to define its +/// own class struct `FooClass` and implement `ClassInitImpl`. +/// `ClassInitImpl`'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` for each subclass and not have a +/// `YourSuperclassImpl` trait at all. +/// +/// ```ignore +/// impl ClassInitImpl for YourSubclass { +/// fn class_init(klass: &mut YourSuperclass) { +/// klass.some_method = Some(Self::some_method); +/// >::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 { + /// 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. + /// + /// The virtual method implementations usually come from another + /// 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 + /// 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); +} + +/// # 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(dev: *mut Object) { + unsafe { + assert!(!dev.is_null()); + let state = core::ptr::NonNull::new_unchecked(dev.cast::()); + T::UNPARENT.unwrap()(state.as_ref()); + } +} + +impl ClassInitImpl for T +where + T: ObjectImpl, +{ + fn class_init(oc: &mut ObjectClass) { + if ::UNPARENT.is_some() { + oc.unparent = Some(rust_unparent_fn::); + } + } +} + +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/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 5d15b31..fa69cad 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -9,9 +9,9 @@ pub use bindings::{SysBusDevice, SysBusDeviceClass}; use crate::{ bindings::{self, DeviceClass}, cell::bql_locked, - definitions::ClassInitImpl, irq::InterruptSource, prelude::*, + qom::ClassInitImpl, }; unsafe impl ObjectType for SysBusDevice { -- cgit v1.1 From 716d89f9cc14faf784d83c945c40b7e8256ae525 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Oct 2024 10:14:11 +0100 Subject: rust: re-export C types from qemu-api submodules 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. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qdev.rs | 7 +++++-- rust/qemu-api/src/qom.rs | 12 +++++++----- rust/qemu-api/src/sysbus.rs | 5 +---- rust/qemu-api/src/vmstate.rs | 9 +++++---- 4 files changed, 18 insertions(+), 15 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index ad4c12d..07a502a 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, }; /// Trait providing the contents of [`DeviceClass`]. diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 2222d1a..a663647 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -33,7 +33,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(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init @@ -164,9 +166,9 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl { /// /// 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)`>`. -/// Such implementations are made in one of two ways. +/// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and +/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are made +/// in one of two ways. /// /// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` /// crate itself. The Rust implementation of methods will come from a @@ -221,7 +223,7 @@ pub trait ClassInitImpl { /// /// 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 fa69cad..9abc687 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -7,10 +7,7 @@ use std::{ffi::CStr, ptr::addr_of}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; use crate::{ - bindings::{self, DeviceClass}, - cell::bql_locked, - irq::InterruptSource, - prelude::*, + 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 bedcf1e..25c68b7 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 @@ pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMSta 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),*$(,)*) => {{ -- cgit v1.1 From d4873c5d4fdd11f484df183e01c0825fe347fd8b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Nov 2024 12:08:43 +0100 Subject: bql: add a "mock" BQL for Rust unit tests Right now, the stub BQL in stubs/iothread-lock.c always reports itself as unlocked. However, Rust would like to run its tests in an environment where the BQL *is* locked. Provide an extremely dirty function that flips the return value of bql_is_locked() to true. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/cell.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 28349de..eae4e2c 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -124,9 +124,18 @@ use std::{ 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. +/// An internal function that is used by doctests. +pub fn bql_start_test() { + if cfg!(MESON) { + // SAFETY: integration tests are run with --test-threads=1, while + // unit tests and doctests are not multithreaded and do not have + // any BQL-protected data. Just set bql_locked to true. + unsafe { + bindings::rust_bql_mock_lock(); + } + } +} + pub fn bql_locked() -> bool { // SAFETY: the function does nothing but return a thread-local bool !cfg!(MESON) || unsafe { bindings::bql_locked() } @@ -220,6 +229,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// ``` @@ -236,6 +246,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -253,6 +264,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let cell = BqlCell::new(5); /// assert_eq!(cell.get(), 5); @@ -274,6 +286,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// let five = c.into_inner(); @@ -293,6 +306,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -315,6 +329,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -333,6 +348,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// let five = c.take(); @@ -461,6 +477,7 @@ impl BqlRefCell { /// /// ``` /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -472,6 +489,7 @@ impl BqlRefCell { /// /// ```should_panic /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -513,6 +531,7 @@ impl BqlRefCell { /// /// ``` /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new("hello".to_owned()); /// @@ -525,6 +544,7 @@ impl BqlRefCell { /// /// ```should_panic /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// let m = c.borrow(); -- cgit v1.1 From f50cd85c8475c16374d0e138efda222ce4455f53 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Dec 2024 14:32:16 +0100 Subject: rust: qom: add casting functionality 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. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/prelude.rs | 7 ++ rust/qemu-api/src/qdev.rs | 1 + rust/qemu-api/src/qom.rs | 283 +++++++++++++++++++++++++++++++++++++++++-- rust/qemu-api/src/sysbus.rs | 7 +- 4 files changed, 290 insertions(+), 8 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 5cc41f0..a0a71fc 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,4 +7,11 @@ pub use crate::bitops::IntegerExt; 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 07a502a..686054e 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -144,3 +144,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 a663647..74ea572 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -4,15 +4,22 @@ //! 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 //! //! A leaf class only needs a struct holding instance state. The struct must -//! implement the [`ObjectType`] trait, as well as any `*Impl` traits that exist -//! for its superclasses. +//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` +//! traits that exist for its superclasses. //! //! If a class has subclasses, it will also provide a struct for instance data, //! with the same characteristics as for concrete classes, but it also needs @@ -31,11 +38,57 @@ //! the source for this is the `*Impl` trait; the associated consts and //! functions if needed are wrapped to map C types into Rust types. -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: ObjectType {} + +// SAFETY: it is always safe to cast to your own type +unsafe impl IsA 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, which is + // declared above. + let ptr: *const Self = self; + unsafe { &*ptr.cast::<$parent>() } + } + } + )* + }; +} unsafe extern "C" fn rust_instance_init(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init @@ -96,8 +149,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(&self) -> *const U + where + Self::Target: IsA, + { + let ptr: *const Self::Target = self.deref(); + ptr.cast::() + } + + /// 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(&self) -> *mut U + where + Self::Target: IsA, + { + #[allow(clippy::as_ptr_cast_mut)] + { + self.as_ptr::() 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, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast::() } + } + + /// 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) -> Option<&'a U> + where + Self: 'a, + { + self.dynamic_cast::() + } + + /// 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::()` is always safe. + unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U + where + Self: 'a, + { + unsafe { &*(self.as_ptr::().cast::()) } + } +} + +impl ObjectDeref for &T {} +impl 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::() { +/// 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, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast_mut::() } + } + + /// 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) -> Result<&'a mut U, Self> + where + Self: 'a, + { + self.dynamic_cast_mut::() + } + + /// 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::()` is always safe. + unsafe fn unsafe_cast_mut<'a, U: ObjectType>(self) -> &'a mut U + where + Self: 'a, + { + unsafe { &mut *self.as_mut_ptr::().cast::() } + } +} + +impl ObjectDeref for &mut T {} +impl ObjectCastMut for &mut T {} + /// 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 diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 9abc687..8193734 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -7,7 +7,11 @@ use std::{ffi::CStr, ptr::addr_of}; pub use bindings::{SysBusDevice, SysBusDeviceClass}; use crate::{ - bindings, cell::bql_locked, irq::InterruptSource, prelude::*, qdev::DeviceClass, + bindings, + cell::bql_locked, + irq::InterruptSource, + prelude::*, + qdev::{DeviceClass, DeviceState}, qom::ClassInitImpl, }; @@ -16,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 ClassInitImpl for T -- cgit v1.1 From ba3b81f3b668d3faa6cbdb39e123394f7bf637c7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Nov 2024 00:01:57 +0100 Subject: rust: qom: add initial subset of methods on Object 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; but that's transparent as long as it derefs to &str) and a QOM class. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/prelude.rs | 1 + rust/qemu-api/src/qom.rs | 56 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 3 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index a0a71fc..6f32dee 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -12,6 +12,7 @@ pub use crate::qom::Object; 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 74ea572..7d5fbef 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 leaf class only needs a struct holding instance state. The struct must @@ -37,6 +42,16 @@ //! `ClassInitImpl`. This fills the vtable in the class struct; //! the source for this is the `*Impl` trait; the associated consts and //! functions if needed are wrapped to map C types into Rust types. +//! +//! * 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, @@ -46,7 +61,7 @@ use std::{ 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`). @@ -532,3 +547,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`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Return the name of the type of `self` + fn typename(&self) -> std::borrow::Cow<'_, str> { + let obj = self.upcast::(); + // 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 ::Class { + let obj = self.upcast::(); + + // SAFETY: all objects can call object_get_class; the actual class + // type is guaranteed by the implementation of `ObjectType` and + // `ObjectImpl`. + let klass: &'static ::Class = + unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; + + klass + } +} + +impl ObjectMethods for R where R::Target: IsA {} -- cgit v1.1 From e05fbacd20366de436db82b776dfbc77071f6e29 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 Nov 2024 17:26:24 +0100 Subject: rust: qemu-api: add a module to wrap functions and zero-sized closures 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. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/callbacks.rs | 144 +++++++++++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + 2 files changed, 145 insertions(+) create mode 100644 rust/qemu-api/src/callbacks.rs (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs new file mode 100644 index 0000000..314f9dc --- /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::`. +/// +/// 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 FnCall<(&'a T,)>>( +/// opaque: *mut c_void, +/// ) { +/// // SAFETY: the opaque was passed as a reference to `T`. +/// F::call((unsafe { &*(opaque.cast::()) }, )) +/// } +/// +/// // 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 FnCall<(&'a T,)>>(_f: &F, opaque: &T) { +/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; +/// 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::`. +/// +/// `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 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: '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::() == 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 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::::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 124bece..4b43e02 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -14,6 +14,7 @@ pub mod prelude; pub mod bitops; pub mod c_str; +pub mod callbacks; pub mod cell; pub mod irq; pub mod module; -- cgit v1.1 From 6b4f7b0705be31c7df6ea01c81a42a42950959a9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Dec 2024 12:53:22 +0100 Subject: rust: pl011: fix migration stream The Rust vmstate macros lack the type-safety of their C equivalents (so safe, much abstraction), and therefore they were predictably wrong. The registers have already been changed to 32-bits in the previous patch, but read_pos/read_count/read_trigger also have to be u32 instead of usize. The easiest way to do so is to let the FIFO use u32 indices instead of usize. My plan for making VMStateField typesafe is to have a trait 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)". That is, vmstate_of retrieves the basic VMStateField and fills in the offset, and then more changes can be applied on top. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'rust/qemu-api/src') diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 25c68b7..63c897a 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -106,28 +106,6 @@ macro_rules! vmstate_uint32 { }}; } -#[doc(alias = "VMSTATE_INT32_V")] -#[macro_export] -macro_rules! vmstate_int32_v { - ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ - $crate::vmstate_single!( - $field_name, - $struct_name, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32), - ::core::mem::size_of::() - ) - }}; -} - -#[doc(alias = "VMSTATE_INT32")] -#[macro_export] -macro_rules! vmstate_int32 { - ($field_name:ident, $struct_name:ty) => {{ - $crate::vmstate_int32_v!($field_name, $struct_name, 0) - }}; -} - #[doc(alias = "VMSTATE_ARRAY")] #[macro_export] macro_rules! vmstate_array { -- cgit v1.1