mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00
rust: qom: change instance_init to take a ParentInit<>
This removes undefined behavior associated to writing to uninitialized fields, and makes it possible to remove "unsafe" from the instance_init implementation. However, the init function itself is still unsafe, because it must promise (as a sort as MaybeUninit::assume_init) that all fields have been initialized. Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
8d394f6cf0
commit
345bef46a1
5 changed files with 63 additions and 57 deletions
|
@ -16,6 +16,7 @@ use crate::{
|
|||
callbacks::FnCall,
|
||||
cell::Opaque,
|
||||
prelude::*,
|
||||
uninit::MaybeUninitField,
|
||||
zeroable::Zeroable,
|
||||
};
|
||||
|
||||
|
@ -147,7 +148,7 @@ impl MemoryRegion {
|
|||
#[inline(always)]
|
||||
unsafe fn do_init_io(
|
||||
slot: *mut bindings::MemoryRegion,
|
||||
owner: *mut Object,
|
||||
owner: *mut bindings::Object,
|
||||
ops: &'static bindings::MemoryRegionOps,
|
||||
name: &'static str,
|
||||
size: u64,
|
||||
|
@ -156,7 +157,7 @@ impl MemoryRegion {
|
|||
let cstr = CString::new(name).unwrap();
|
||||
memory_region_init_io(
|
||||
slot,
|
||||
owner.cast::<bindings::Object>(),
|
||||
owner,
|
||||
ops,
|
||||
owner.cast::<c_void>(),
|
||||
cstr.as_ptr(),
|
||||
|
@ -166,16 +167,15 @@ impl MemoryRegion {
|
|||
}
|
||||
|
||||
pub fn init_io<T: IsA<Object>>(
|
||||
&mut self,
|
||||
owner: *mut T,
|
||||
this: &mut MaybeUninitField<'_, T, Self>,
|
||||
ops: &'static MemoryRegionOps<T>,
|
||||
name: &'static str,
|
||||
size: u64,
|
||||
) {
|
||||
unsafe {
|
||||
Self::do_init_io(
|
||||
self.0.as_mut_ptr(),
|
||||
owner.cast::<Object>(),
|
||||
this.as_mut_ptr().cast(),
|
||||
MaybeUninitField::parent_mut(this).cast(),
|
||||
&ops.0,
|
||||
name,
|
||||
size,
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
error::{Error, Result},
|
||||
irq::InterruptSource,
|
||||
prelude::*,
|
||||
qom::{ObjectClass, ObjectImpl, Owned},
|
||||
qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
|
||||
vmstate::VMStateDescription,
|
||||
};
|
||||
|
||||
|
@ -247,15 +247,9 @@ unsafe impl ObjectType for DeviceState {
|
|||
}
|
||||
qom_isa!(DeviceState: Object);
|
||||
|
||||
/// Trait for methods exposed by the [`DeviceState`] class. The methods can be
|
||||
/// called on all objects that have the trait `IsA<DeviceState>`.
|
||||
///
|
||||
/// The trait should only be used through the blanket implementation,
|
||||
/// which guarantees safety via `IsA`.
|
||||
pub trait DeviceMethods: ObjectDeref
|
||||
where
|
||||
Self::Target: IsA<DeviceState>,
|
||||
{
|
||||
/// Initialization methods take a [`ParentInit`] and can be called as
|
||||
/// associated functions.
|
||||
impl DeviceState {
|
||||
/// Add an input clock named `name`. Invoke the callback with
|
||||
/// `self` as the first parameter for the events that are requested.
|
||||
///
|
||||
|
@ -266,12 +260,15 @@ where
|
|||
/// which Rust code has a reference to a child object) it would be
|
||||
/// possible for this function to return a `&Clock` too.
|
||||
#[inline]
|
||||
fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>(
|
||||
&self,
|
||||
pub fn init_clock_in<T: DeviceImpl, F: for<'a> FnCall<(&'a T, ClockEvent)>>(
|
||||
this: &mut ParentInit<T>,
|
||||
name: &str,
|
||||
_cb: &F,
|
||||
events: ClockEvent,
|
||||
) -> Owned<Clock> {
|
||||
) -> Owned<Clock>
|
||||
where
|
||||
T::ParentType: IsA<DeviceState>,
|
||||
{
|
||||
fn do_init_clock_in(
|
||||
dev: &DeviceState,
|
||||
name: &str,
|
||||
|
@ -287,10 +284,10 @@ where
|
|||
unsafe {
|
||||
let cstr = CString::new(name).unwrap();
|
||||
let clk = bindings::qdev_init_clock_in(
|
||||
dev.as_mut_ptr(),
|
||||
dev.0.as_mut_ptr(),
|
||||
cstr.as_ptr(),
|
||||
cb,
|
||||
dev.as_void_ptr(),
|
||||
dev.0.as_void_ptr(),
|
||||
events.0,
|
||||
);
|
||||
|
||||
|
@ -307,12 +304,12 @@ where
|
|||
// SAFETY: the opaque is "this", which is indeed a pointer to T
|
||||
F::call((unsafe { &*(opaque.cast::<T>()) }, event))
|
||||
}
|
||||
Some(rust_clock_cb::<Self::Target, F>)
|
||||
Some(rust_clock_cb::<T, F>)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
do_init_clock_in(self.upcast(), name, cb, events)
|
||||
do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events)
|
||||
}
|
||||
|
||||
/// Add an output clock named `name`.
|
||||
|
@ -324,16 +321,30 @@ where
|
|||
/// which Rust code has a reference to a child object) it would be
|
||||
/// possible for this function to return a `&Clock` too.
|
||||
#[inline]
|
||||
fn init_clock_out(&self, name: &str) -> Owned<Clock> {
|
||||
pub fn init_clock_out<T: DeviceImpl>(this: &mut ParentInit<T>, name: &str) -> Owned<Clock>
|
||||
where
|
||||
T::ParentType: IsA<DeviceState>,
|
||||
{
|
||||
unsafe {
|
||||
let cstr = CString::new(name).unwrap();
|
||||
let clk = bindings::qdev_init_clock_out(self.upcast().as_mut_ptr(), cstr.as_ptr());
|
||||
let dev: &mut DeviceState = this.upcast_mut();
|
||||
let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr());
|
||||
|
||||
let clk: &Clock = Clock::from_raw(clk);
|
||||
Owned::from(clk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for methods exposed by the [`DeviceState`] class. The methods can be
|
||||
/// called on all objects that have the trait `IsA<DeviceState>`.
|
||||
///
|
||||
/// The trait should only be used through the blanket implementation,
|
||||
/// which guarantees safety via `IsA`.
|
||||
pub trait DeviceMethods: ObjectDeref
|
||||
where
|
||||
Self::Target: IsA<DeviceState>,
|
||||
{
|
||||
fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
|
||||
assert!(bql_locked());
|
||||
let c_propname = CString::new(propname).unwrap();
|
||||
|
|
|
@ -382,12 +382,15 @@ impl<T> DerefMut for ParentInit<'_, T> {
|
|||
}
|
||||
|
||||
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut bindings::Object) {
|
||||
let mut state = NonNull::new(obj).unwrap().cast::<T>();
|
||||
let mut state = NonNull::new(obj).unwrap().cast::<MaybeUninit<T>>();
|
||||
|
||||
// SAFETY: obj is an instance of T, since rust_instance_init<T>
|
||||
// is called from QOM core as the instance_init function
|
||||
// for class T
|
||||
unsafe {
|
||||
T::INSTANCE_INIT.unwrap()(state.as_mut());
|
||||
ParentInit::with(state.as_mut(), |parent_init| {
|
||||
T::INSTANCE_INIT.unwrap()(parent_init);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -654,7 +657,7 @@ pub trait ObjectImpl: ObjectType + IsA<Object> {
|
|||
///
|
||||
/// FIXME: The argument is not really a valid reference. `&mut
|
||||
/// MaybeUninit<Self>` would be a better description.
|
||||
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = None;
|
||||
const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = None;
|
||||
|
||||
/// Function that is called to finish initialization of an object, once
|
||||
/// `INSTANCE_INIT` functions have been called.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue