[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 10/14] rust: add idiomatic bindings to define Device subclasses
From: |
Paolo Bonzini |
Subject: |
[PATCH 10/14] rust: add idiomatic bindings to define Device subclasses |
Date: |
Mon, 1 Jul 2024 16:58:42 +0200 |
Provide a macro to register a type and automatically define qdev
properties. Subclasses of DeviceState must define a trait DeviceImpl, to
point the type definition machinery to the implementation of virtual
functions in DeviceState.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
qemu/src/hw/core/device_impl.rs | 140 ++++++++++++++++++++++++++++++++
qemu/src/hw/core/mod.rs | 1 +
qemu/src/lib.rs | 5 ++
qemu/tests/main.rs | 52 +++++++++++-
4 files changed, 197 insertions(+), 1 deletion(-)
create mode 100644 qemu/src/hw/core/device_impl.rs
diff --git a/qemu/src/hw/core/device_impl.rs b/qemu/src/hw/core/device_impl.rs
new file mode 100644
index 0000000..80b0e5e
--- /dev/null
+++ b/qemu/src/hw/core/device_impl.rs
@@ -0,0 +1,140 @@
+//! Macros and traits to implement subclasses of Device in Rust
+//!
+//! @author Paolo Bonzini
+
+#![allow(clippy::missing_safety_doc)]
+
+use std::ffi::c_void;
+
+use crate::bindings;
+use crate::bindings::DeviceClass;
+use crate::bindings::DeviceState;
+use crate::bindings::Property;
+
+use crate::qom::object_impl::ObjectImpl;
+use crate::qom::object_impl::TypeImpl;
+
+use crate::qom::refs::ObjectCast;
+
+use crate::util::error::Error;
+
+/// Information on which superclass methods are overridden
+/// by a Rust-implemented subclass of Device.
+pub trait DeviceImpl: ObjectImpl + DeviceTypeImpl {
+ /// If not `None`, a function that implements the `realize` member
+ /// of the QOM `DeviceClass`.
+ const REALIZE: Option<fn(obj: &Self) -> crate::Result<()>> = None;
+
+ /// If not `None`, a function that implements the `unrealize` member
+ /// of the QOM `DeviceClass`.
+ const UNREALIZE: Option<fn(obj: &Self)> = None;
+
+ /// If not `None`, a function that implements the `cold_reset` member
+ /// of the QOM `DeviceClass`.
+ const COLD_RESET: Option<fn(obj: &Self)> = None;
+}
+
+impl DeviceClass {
+ pub fn class_init<T: DeviceImpl>(&mut self) {
+ unsafe extern "C" fn rust_cold_reset<T: DeviceImpl>(obj: *mut
DeviceState) {
+ let f = T::COLD_RESET.unwrap();
+ f((&*obj).unsafe_cast::<T>())
+ }
+ self.cold_reset = T::COLD_RESET.map(|_| rust_cold_reset::<T> as _);
+
+ unsafe extern "C" fn rust_realize<T: DeviceImpl>(
+ obj: *mut DeviceState,
+ errp: *mut *mut bindings::Error,
+ ) {
+ let f = T::REALIZE.unwrap();
+ let result = f((&*obj).unsafe_cast::<T>());
+ Error::ok_or_propagate(result, errp);
+ }
+ self.realize = T::REALIZE.map(|_| rust_realize::<T> as _);
+
+ unsafe extern "C" fn rust_unrealize<T: DeviceImpl>(obj: *mut
DeviceState) {
+ let f = T::UNREALIZE.unwrap();
+ f((&*obj).unsafe_cast::<T>())
+ }
+ self.unrealize = T::UNREALIZE.map(|_| rust_unrealize::<T> as _);
+
+ self.properties = <T as DeviceTypeImpl>::properties();
+
+ // Now initialize the ObjectClass from the ObjectImpl.
+ self.oc.class_init::<T>();
+ }
+}
+
+impl DeviceState {
+ pub unsafe extern "C" fn rust_class_init<T: DeviceImpl>(
+ klass: *mut c_void,
+ _data: *mut c_void,
+ ) {
+ let dc: &mut DeviceClass = &mut *(klass.cast());
+ dc.class_init::<T>();
+ }
+}
+
+/// Internal information on a Rust-implemented subclass of Device.
+/// Only public because it is used by macros.
+pub unsafe trait DeviceTypeImpl: TypeImpl {
+ const CONF_OFFSET: usize;
+
+ // This needs to be here, and not in DeviceImpl, because properties
+ // reference statics (for globals defined in C, e.g. qdev_prop_bool)
+ // which is unstable (see https://github.com/rust-lang/rust/issues/119618,
+ // feature const_refs_to_static)
+ fn properties() -> *const Property;
+}
+
+pub struct QdevPropBool;
+impl QdevPropBool {
+ pub const fn convert(value: &bool) -> u64 {
+ *value as u64
+ }
+}
+
+#[macro_export]
+macro_rules! qdev_prop {
+ (@internal bool, $name:expr, $default:expr, $offset:expr) => {
+ $crate::Property {
+ name: $name.as_ptr(),
+ offset: $offset,
+ default:
$crate::hw::core::device_impl::QdevPropBool::convert(&($default)),
+ info: unsafe { &$crate::bindings::qdev_prop_bool },
+ }
+ };
+
+ // Replace field with typechecking expression and offset
+ ($kind:tt, $name:expr, $type:ty, $default:expr, $field:ident) => {
+ qdev_prop!(@internal
+ $kind,
+ $name,
+ (<$crate::conf_type!($type) as ConstDefault>::DEFAULT).$field,
+ <$type as $crate::DeviceTypeImpl>::CONF_OFFSET +
std::mem::offset_of!($crate::conf_type!($type), $field)
+ )
+ };
+}
+
+#[macro_export]
+macro_rules! qdev_define_type {
+ ($name:expr, $struct:ident, $conf_ty:ty, $state_ty:ty;
+ @extends $super:ty $(,$supers:ty)*;
+ @properties [$($props: expr),+]) => {
+ $crate::qom_define_type!(
+ $name, $struct, $conf_ty, $state_ty;
+ @extends $super $(,$supers)*, $crate::Object);
+
+ unsafe impl $crate::DeviceTypeImpl for $struct {
+ const CONF_OFFSET: usize = std::mem::offset_of!($struct, conf);
+
+ fn properties() -> *const $crate::Property {
+ static mut PROPERTIES: &'static [$crate::Property] =
&[$($props),+];
+
+ // SAFETY: The only reference is created here; mut is needed
to refer to
+ // &qdev_prop_xxx.
+ unsafe { PROPERTIES.as_ptr() }
+ }
+ }
+ }
+}
diff --git a/qemu/src/hw/core/mod.rs b/qemu/src/hw/core/mod.rs
index 5458924..6cd9197 100644
--- a/qemu/src/hw/core/mod.rs
+++ b/qemu/src/hw/core/mod.rs
@@ -1 +1,2 @@
pub mod device;
+pub mod device_impl;
diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs
index 81abf9c..3f0491c 100644
--- a/qemu/src/lib.rs
+++ b/qemu/src/lib.rs
@@ -2,12 +2,17 @@
#![allow(dead_code)]
pub mod bindings;
+pub use bindings::DeviceClass;
pub use bindings::DeviceState;
pub use bindings::Object;
+pub use bindings::Property;
+pub use bindings::PropertyInfo;
pub use bindings::TypeInfo;
pub mod hw;
pub use hw::core::device::DeviceMethods;
+pub use hw::core::device_impl::DeviceImpl;
+pub use hw::core::device_impl::DeviceTypeImpl;
pub mod qom;
pub use qom::object::ObjectClassMethods;
diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs
index a7cbeed..e499c14 100644
--- a/qemu/tests/main.rs
+++ b/qemu/tests/main.rs
@@ -5,9 +5,18 @@ use qemu::Object;
use qemu::ObjectClassMethods;
use qemu::ObjectImpl;
+use qemu::qdev_define_type;
+use qemu::qdev_prop;
+use qemu::DeviceImpl;
+use qemu::DeviceMethods;
+use qemu::DeviceState;
+
+use qemu::Result;
+
+use std::cell::RefCell;
+
#[derive(Default, ConstDefault)]
struct TestConf {
- #[allow(dead_code)]
foo: bool,
}
@@ -27,6 +36,47 @@ qom_define_type!(
impl ObjectImpl for TestObject {}
+qdev_define_type!(
+ c"test-device",
+ TestDevice,
+ TestConf,
+ RefCell<TestState>;
+ @extends DeviceState;
+ @properties [qdev_prop!(bool, c"foo", TestDevice, true, foo)]
+);
+
+impl TestDevice {
+ #[allow(clippy::unused_self)]
+ fn unparent(&self) {
+ println!("unparent");
+ }
+
+ #[allow(clippy::unused_self)]
+ fn realize(&self) -> Result<()> {
+ println!("realize");
+ Ok(())
+ }
+
+ #[allow(clippy::unused_self)]
+ fn unrealize(&self) {
+ println!("unrealize");
+ }
+}
+
+impl ObjectImpl for TestDevice {
+ const UNPARENT: Option<fn(&TestDevice)> = Some(TestDevice::unparent);
+}
+
+impl DeviceImpl for TestDevice {
+ const REALIZE: Option<fn(&TestDevice) -> Result<()>> =
Some(TestDevice::realize);
+ const UNREALIZE: Option<fn(&TestDevice)> = Some(TestDevice::unrealize);
+}
+
fn main() {
drop(TestObject::new());
+
+ let d = TestDevice::new();
+ d.realize().unwrap();
+ d.cold_reset();
+ d.unparent();
}
--
2.45.2
- [PATCH 04/14] rust: add tests for util::foreign, (continued)
- [PATCH 04/14] rust: add tests for util::foreign, Paolo Bonzini, 2024/07/01
- [PATCH 05/14] rust: define wrappers for Error, Paolo Bonzini, 2024/07/01
- [PATCH 03/14] rust: define traits and pointer wrappers to convert from/to C representations, Paolo Bonzini, 2024/07/01
- [PATCH 07/14] rust: define wrappers for methods of the QOM Object class, Paolo Bonzini, 2024/07/01
- [PATCH 08/14] rust: define wrappers for methods of the QOM Device class, Paolo Bonzini, 2024/07/01
- [PATCH 06/14] rust: define wrappers for basic QOM concepts, Paolo Bonzini, 2024/07/01
- [PATCH 09/14] rust: add idiomatic bindings to define Object subclasses, Paolo Bonzini, 2024/07/01
- [PATCH 10/14] rust: add idiomatic bindings to define Device subclasses,
Paolo Bonzini <=
- [PATCH 12/14] rust: replace c"" literals with cstr crate, Paolo Bonzini, 2024/07/01
- [PATCH 14/14] rust: use version of toml_edit that does not require new Rust, Paolo Bonzini, 2024/07/01
- [PATCH 13/14] rust: introduce alternative to offset_of!, Paolo Bonzini, 2024/07/01
- [PATCH 11/14] rust: replace std::ffi::c_char with libc::c_char, Paolo Bonzini, 2024/07/01
- Re: [PATCH 00/14] rust: example of bindings code for Rust in QEMU, Pierrick Bouvier, 2024/07/04