mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00
rust: qdev: move device_class_init! body to generic function, ClassInitImpl implementation to macro
Use a trait to access the former parameters to device_class_init!. This allows hiding the details of the class_init implementation behind a generic function and makes higher-level functionality available from qemu_api. The implementation of ClassInitImpl is then the same for all devices and is easily macroized. Later on, we can remove the need to implement ClassInitImpl by hand for all device types, and stop making rust_device_class_init<>() public. While at it, document the members of DeviceImpl. Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
c6c4f3e0d9
commit
8c80c472da
4 changed files with 103 additions and 56 deletions
|
@ -12,11 +12,13 @@ use qemu_api::{
|
||||||
bindings::{self, *},
|
bindings::{self, *},
|
||||||
c_str,
|
c_str,
|
||||||
definitions::ObjectImpl,
|
definitions::ObjectImpl,
|
||||||
device_class::TYPE_SYS_BUS_DEVICE,
|
device_class::{DeviceImpl, TYPE_SYS_BUS_DEVICE},
|
||||||
|
impl_device_class,
|
||||||
irq::InterruptSource,
|
irq::InterruptSource,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
device_class,
|
||||||
memory_ops::PL011_OPS,
|
memory_ops::PL011_OPS,
|
||||||
registers::{self, Interrupt},
|
registers::{self, Interrupt},
|
||||||
RegisterOffset,
|
RegisterOffset,
|
||||||
|
@ -116,14 +118,20 @@ pub struct PL011Class {
|
||||||
_inner: [u8; 0],
|
_inner: [u8; 0],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl qemu_api::definitions::ClassInitImpl for PL011State {
|
impl DeviceImpl for PL011State {
|
||||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
fn properties() -> &'static [Property] {
|
||||||
Some(crate::device_class::pl011_class_init);
|
&device_class::PL011_PROPERTIES
|
||||||
const CLASS_BASE_INIT: Option<
|
}
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
fn vmsd() -> Option<&'static VMStateDescription> {
|
||||||
> = None;
|
Some(&device_class::VMSTATE_PL011)
|
||||||
|
}
|
||||||
|
const REALIZE: Option<unsafe extern "C" fn(*mut DeviceState, *mut *mut Error)> =
|
||||||
|
Some(device_class::pl011_realize);
|
||||||
|
const RESET: Option<unsafe extern "C" fn(*mut DeviceState)> = Some(device_class::pl011_reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_device_class!(PL011State);
|
||||||
|
|
||||||
impl PL011State {
|
impl PL011State {
|
||||||
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
||||||
///
|
///
|
||||||
|
@ -649,17 +657,13 @@ pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl qemu_api::definitions::ClassInitImpl for PL011Luminary {
|
|
||||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
|
||||||
None;
|
|
||||||
const CLASS_BASE_INIT: Option<
|
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
|
||||||
> = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for PL011Luminary {
|
impl ObjectImpl for PL011Luminary {
|
||||||
type Class = PL011LuminaryClass;
|
type Class = PL011LuminaryClass;
|
||||||
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
|
const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
|
||||||
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
|
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
|
||||||
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
|
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DeviceImpl for PL011Luminary {}
|
||||||
|
|
||||||
|
impl_device_class!(PL011Luminary);
|
||||||
|
|
|
@ -93,14 +93,6 @@ qemu_api::declare_properties! {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_api::device_class_init! {
|
|
||||||
pl011_class_init,
|
|
||||||
props => PL011_PROPERTIES,
|
|
||||||
realize_fn => Some(pl011_realize),
|
|
||||||
legacy_reset_fn => Some(pl011_reset),
|
|
||||||
vmsd => VMSTATE_PL011,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// We expect the FFI user of this function to pass a valid pointer, that has
|
/// We expect the FFI user of this function to pass a valid pointer, that has
|
||||||
|
|
|
@ -2,25 +2,80 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::{ffi::CStr, os::raw::c_void};
|
||||||
|
|
||||||
use crate::bindings;
|
use crate::{
|
||||||
|
bindings::{self, DeviceClass, DeviceState, Error, ObjectClass, Property, VMStateDescription},
|
||||||
|
zeroable::Zeroable,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Trait providing the contents of [`DeviceClass`].
|
||||||
|
pub trait DeviceImpl {
|
||||||
|
/// _Realization_ is the second stage of device creation. It contains
|
||||||
|
/// all operations that depend on device properties and can fail (note:
|
||||||
|
/// this is not yet supported for Rust devices).
|
||||||
|
///
|
||||||
|
/// If not `None`, the parent class's `realize` method is overridden
|
||||||
|
/// with the function pointed to by `REALIZE`.
|
||||||
|
const REALIZE: Option<unsafe extern "C" fn(*mut DeviceState, *mut *mut Error)> = None;
|
||||||
|
|
||||||
|
/// If not `None`, the parent class's `reset` method is overridden
|
||||||
|
/// with the function pointed to by `RESET`.
|
||||||
|
///
|
||||||
|
/// Rust does not yet support the three-phase reset protocol; this is
|
||||||
|
/// usually okay for leaf classes.
|
||||||
|
const RESET: Option<unsafe extern "C" fn(dev: *mut DeviceState)> = None;
|
||||||
|
|
||||||
|
/// An array providing the properties that the user can set on the
|
||||||
|
/// device. Not a `const` because referencing statics in constants
|
||||||
|
/// is unstable until Rust 1.83.0.
|
||||||
|
fn properties() -> &'static [Property] {
|
||||||
|
&[Zeroable::ZERO; 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `VMStateDescription` providing the migration format for the device
|
||||||
|
/// Not a `const` because referencing statics in constants is unstable
|
||||||
|
/// until Rust 1.83.0.
|
||||||
|
fn vmsd() -> Option<&'static VMStateDescription> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// We expect the FFI user of this function to pass a valid pointer that
|
||||||
|
/// can be downcasted to type `DeviceClass`, because `T` implements
|
||||||
|
/// `DeviceImpl`.
|
||||||
|
pub unsafe extern "C" fn rust_device_class_init<T: DeviceImpl>(
|
||||||
|
klass: *mut ObjectClass,
|
||||||
|
_: *mut c_void,
|
||||||
|
) {
|
||||||
|
let mut dc = ::core::ptr::NonNull::new(klass.cast::<DeviceClass>()).unwrap();
|
||||||
|
unsafe {
|
||||||
|
let dc = dc.as_mut();
|
||||||
|
if let Some(realize_fn) = <T as DeviceImpl>::REALIZE {
|
||||||
|
dc.realize = Some(realize_fn);
|
||||||
|
}
|
||||||
|
if let Some(reset_fn) = <T as DeviceImpl>::RESET {
|
||||||
|
bindings::device_class_set_legacy_reset(dc, Some(reset_fn));
|
||||||
|
}
|
||||||
|
if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
|
||||||
|
dc.vmsd = vmsd;
|
||||||
|
}
|
||||||
|
bindings::device_class_set_props(dc, <T as DeviceImpl>::properties().as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! device_class_init {
|
macro_rules! impl_device_class {
|
||||||
($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => {
|
($type:ty) => {
|
||||||
pub unsafe extern "C" fn $func(
|
impl $crate::definitions::ClassInitImpl for $type {
|
||||||
klass: *mut $crate::bindings::ObjectClass,
|
const CLASS_INIT: Option<
|
||||||
_: *mut ::std::os::raw::c_void,
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void),
|
||||||
) {
|
> = Some($crate::device_class::rust_device_class_init::<$type>);
|
||||||
let mut dc =
|
const CLASS_BASE_INIT: Option<
|
||||||
::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
|
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut ::std::os::raw::c_void),
|
||||||
unsafe {
|
> = None;
|
||||||
dc.as_mut().realize = $realize_fn;
|
|
||||||
dc.as_mut().vmsd = &$vmsd;
|
|
||||||
$crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn);
|
|
||||||
$crate::bindings::device_class_set_props(dc.as_mut(), $props.as_ptr());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::{ffi::CStr, os::raw::c_void};
|
use std::ffi::CStr;
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::*,
|
bindings::*,
|
||||||
c_str, declare_properties, define_property,
|
c_str, declare_properties, define_property,
|
||||||
definitions::{ClassInitImpl, ObjectImpl},
|
definitions::ObjectImpl,
|
||||||
device_class, device_class_init,
|
device_class::{self, DeviceImpl},
|
||||||
|
impl_device_class,
|
||||||
zeroable::Zeroable,
|
zeroable::Zeroable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,28 +46,23 @@ fn test_device_decl_macros() {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
device_class_init! {
|
|
||||||
dummy_class_init,
|
|
||||||
props => DUMMY_PROPERTIES,
|
|
||||||
realize_fn => None,
|
|
||||||
legacy_reset_fn => None,
|
|
||||||
vmsd => VMSTATE,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for DummyState {
|
impl ObjectImpl for DummyState {
|
||||||
type Class = DummyClass;
|
type Class = DummyClass;
|
||||||
const TYPE_NAME: &'static CStr = c_str!("dummy");
|
const TYPE_NAME: &'static CStr = c_str!("dummy");
|
||||||
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
|
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClassInitImpl for DummyState {
|
impl DeviceImpl for DummyState {
|
||||||
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
|
fn properties() -> &'static [Property] {
|
||||||
Some(dummy_class_init);
|
&DUMMY_PROPERTIES
|
||||||
const CLASS_BASE_INIT: Option<
|
}
|
||||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
|
fn vmsd() -> Option<&'static VMStateDescription> {
|
||||||
> = None;
|
Some(&VMSTATE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_device_class!(DummyState);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
module_call_init(module_init_type::MODULE_INIT_QOM);
|
module_call_init(module_init_type::MODULE_INIT_QOM);
|
||||||
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
|
object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue