mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-01 14:53:54 -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
|
@ -2,7 +2,7 @@
|
||||||
// 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, mem::size_of, ptr::addr_of_mut};
|
use std::{ffi::CStr, mem::size_of};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
chardev::{CharBackend, Chardev, Event},
|
chardev::{CharBackend, Chardev, Event},
|
||||||
|
@ -11,9 +11,10 @@ use qemu_api::{
|
||||||
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
|
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
||||||
qom::{ObjectImpl, Owned, ParentField},
|
qom::{ObjectImpl, Owned, ParentField, ParentInit},
|
||||||
static_assert,
|
static_assert,
|
||||||
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
||||||
|
uninit_field_mut,
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -163,7 +164,7 @@ impl PL011Impl for PL011State {
|
||||||
impl ObjectImpl for PL011State {
|
impl ObjectImpl for PL011State {
|
||||||
type ParentType = SysBusDevice;
|
type ParentType = SysBusDevice;
|
||||||
|
|
||||||
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
|
const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = Some(Self::init);
|
||||||
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
|
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
|
||||||
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
|
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
|
||||||
}
|
}
|
||||||
|
@ -488,7 +489,7 @@ impl PL011State {
|
||||||
/// `PL011State` type. It must not be called more than once on the same
|
/// `PL011State` type. It must not be called more than once on the same
|
||||||
/// location/instance. All its fields are expected to hold uninitialized
|
/// location/instance. All its fields are expected to hold uninitialized
|
||||||
/// values with the sole exception of `parent_obj`.
|
/// values with the sole exception of `parent_obj`.
|
||||||
unsafe fn init(&mut self) {
|
unsafe fn init(mut this: ParentInit<Self>) {
|
||||||
static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()
|
static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()
|
||||||
.read(&PL011State::read)
|
.read(&PL011State::read)
|
||||||
.write(&PL011State::write)
|
.write(&PL011State::write)
|
||||||
|
@ -496,28 +497,23 @@ impl PL011State {
|
||||||
.impl_sizes(4, 4)
|
.impl_sizes(4, 4)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY: this and this.iomem are guaranteed to be valid at this point
|
||||||
//
|
|
||||||
// self and self.iomem are guaranteed to be valid at this point since callers
|
|
||||||
// must make sure the `self` reference is valid.
|
|
||||||
MemoryRegion::init_io(
|
MemoryRegion::init_io(
|
||||||
unsafe { &mut *addr_of_mut!(self.iomem) },
|
&mut uninit_field_mut!(*this, iomem),
|
||||||
addr_of_mut!(*self),
|
|
||||||
&PL011_OPS,
|
&PL011_OPS,
|
||||||
"pl011",
|
"pl011",
|
||||||
0x1000,
|
0x1000,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.regs = Default::default();
|
uninit_field_mut!(*this, regs).write(Default::default());
|
||||||
|
|
||||||
// SAFETY:
|
let clock = DeviceState::init_clock_in(
|
||||||
//
|
&mut this,
|
||||||
// self.clock is not initialized at this point; but since `Owned<_>` is
|
"clk",
|
||||||
// not Drop, we can overwrite the undefined value without side effects;
|
&Self::clock_update,
|
||||||
// it's not sound but, because for all PL011State instances are created
|
ClockEvent::ClockUpdate,
|
||||||
// by QOM code which calls this function to initialize the fields, at
|
);
|
||||||
// leastno code is able to access an invalid self.clock value.
|
uninit_field_mut!(*this, clock).write(clock);
|
||||||
self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn clock_update(&self, _event: ClockEvent) {
|
const fn clock_update(&self, _event: ClockEvent) {
|
||||||
|
|
|
@ -21,8 +21,8 @@ use qemu_api::{
|
||||||
hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
|
hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
||||||
qom::{ObjectImpl, ObjectType, ParentField},
|
qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
|
||||||
qom_isa,
|
qom_isa,
|
||||||
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
||||||
timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
|
timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
|
||||||
|
@ -697,7 +697,7 @@ impl HPETState {
|
||||||
.set(self.counter.get().deposit(shift, len, val));
|
.set(self.counter.get().deposit(shift, len, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn init(&mut self) {
|
unsafe fn init(mut this: ParentInit<Self>) {
|
||||||
static HPET_RAM_OPS: MemoryRegionOps<HPETState> =
|
static HPET_RAM_OPS: MemoryRegionOps<HPETState> =
|
||||||
MemoryRegionOpsBuilder::<HPETState>::new()
|
MemoryRegionOpsBuilder::<HPETState>::new()
|
||||||
.read(&HPETState::read)
|
.read(&HPETState::read)
|
||||||
|
@ -707,18 +707,14 @@ impl HPETState {
|
||||||
.impl_sizes(4, 8)
|
.impl_sizes(4, 8)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// SAFETY:
|
|
||||||
// self and self.iomem are guaranteed to be valid at this point since callers
|
|
||||||
// must make sure the `self` reference is valid.
|
|
||||||
MemoryRegion::init_io(
|
MemoryRegion::init_io(
|
||||||
unsafe { &mut *addr_of_mut!(self.iomem) },
|
&mut uninit_field_mut!(*this, iomem),
|
||||||
addr_of_mut!(*self),
|
|
||||||
&HPET_RAM_OPS,
|
&HPET_RAM_OPS,
|
||||||
"hpet",
|
"hpet",
|
||||||
HPET_REG_SPACE_LEN,
|
HPET_REG_SPACE_LEN,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self::init_timers(unsafe { &mut *((self as *mut Self).cast::<MaybeUninit<Self>>()) });
|
Self::init_timers(&mut this);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_init(&self) {
|
fn post_init(&self) {
|
||||||
|
@ -900,7 +896,7 @@ unsafe impl ObjectType for HPETState {
|
||||||
impl ObjectImpl for HPETState {
|
impl ObjectImpl for HPETState {
|
||||||
type ParentType = SysBusDevice;
|
type ParentType = SysBusDevice;
|
||||||
|
|
||||||
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
|
const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = Some(Self::init);
|
||||||
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
|
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
|
||||||
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
|
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::{
|
||||||
callbacks::FnCall,
|
callbacks::FnCall,
|
||||||
cell::Opaque,
|
cell::Opaque,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
uninit::MaybeUninitField,
|
||||||
zeroable::Zeroable,
|
zeroable::Zeroable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ impl MemoryRegion {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn do_init_io(
|
unsafe fn do_init_io(
|
||||||
slot: *mut bindings::MemoryRegion,
|
slot: *mut bindings::MemoryRegion,
|
||||||
owner: *mut Object,
|
owner: *mut bindings::Object,
|
||||||
ops: &'static bindings::MemoryRegionOps,
|
ops: &'static bindings::MemoryRegionOps,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
size: u64,
|
size: u64,
|
||||||
|
@ -156,7 +157,7 @@ impl MemoryRegion {
|
||||||
let cstr = CString::new(name).unwrap();
|
let cstr = CString::new(name).unwrap();
|
||||||
memory_region_init_io(
|
memory_region_init_io(
|
||||||
slot,
|
slot,
|
||||||
owner.cast::<bindings::Object>(),
|
owner,
|
||||||
ops,
|
ops,
|
||||||
owner.cast::<c_void>(),
|
owner.cast::<c_void>(),
|
||||||
cstr.as_ptr(),
|
cstr.as_ptr(),
|
||||||
|
@ -166,16 +167,15 @@ impl MemoryRegion {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_io<T: IsA<Object>>(
|
pub fn init_io<T: IsA<Object>>(
|
||||||
&mut self,
|
this: &mut MaybeUninitField<'_, T, Self>,
|
||||||
owner: *mut T,
|
|
||||||
ops: &'static MemoryRegionOps<T>,
|
ops: &'static MemoryRegionOps<T>,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
size: u64,
|
size: u64,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::do_init_io(
|
Self::do_init_io(
|
||||||
self.0.as_mut_ptr(),
|
this.as_mut_ptr().cast(),
|
||||||
owner.cast::<Object>(),
|
MaybeUninitField::parent_mut(this).cast(),
|
||||||
&ops.0,
|
&ops.0,
|
||||||
name,
|
name,
|
||||||
size,
|
size,
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
irq::InterruptSource,
|
irq::InterruptSource,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qom::{ObjectClass, ObjectImpl, Owned},
|
qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -247,15 +247,9 @@ unsafe impl ObjectType for DeviceState {
|
||||||
}
|
}
|
||||||
qom_isa!(DeviceState: Object);
|
qom_isa!(DeviceState: Object);
|
||||||
|
|
||||||
/// Trait for methods exposed by the [`DeviceState`] class. The methods can be
|
/// Initialization methods take a [`ParentInit`] and can be called as
|
||||||
/// called on all objects that have the trait `IsA<DeviceState>`.
|
/// associated functions.
|
||||||
///
|
impl 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>,
|
|
||||||
{
|
|
||||||
/// Add an input clock named `name`. Invoke the callback with
|
/// Add an input clock named `name`. Invoke the callback with
|
||||||
/// `self` as the first parameter for the events that are requested.
|
/// `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
|
/// which Rust code has a reference to a child object) it would be
|
||||||
/// possible for this function to return a `&Clock` too.
|
/// possible for this function to return a `&Clock` too.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>(
|
pub fn init_clock_in<T: DeviceImpl, F: for<'a> FnCall<(&'a T, ClockEvent)>>(
|
||||||
&self,
|
this: &mut ParentInit<T>,
|
||||||
name: &str,
|
name: &str,
|
||||||
_cb: &F,
|
_cb: &F,
|
||||||
events: ClockEvent,
|
events: ClockEvent,
|
||||||
) -> Owned<Clock> {
|
) -> Owned<Clock>
|
||||||
|
where
|
||||||
|
T::ParentType: IsA<DeviceState>,
|
||||||
|
{
|
||||||
fn do_init_clock_in(
|
fn do_init_clock_in(
|
||||||
dev: &DeviceState,
|
dev: &DeviceState,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
@ -287,10 +284,10 @@ where
|
||||||
unsafe {
|
unsafe {
|
||||||
let cstr = CString::new(name).unwrap();
|
let cstr = CString::new(name).unwrap();
|
||||||
let clk = bindings::qdev_init_clock_in(
|
let clk = bindings::qdev_init_clock_in(
|
||||||
dev.as_mut_ptr(),
|
dev.0.as_mut_ptr(),
|
||||||
cstr.as_ptr(),
|
cstr.as_ptr(),
|
||||||
cb,
|
cb,
|
||||||
dev.as_void_ptr(),
|
dev.0.as_void_ptr(),
|
||||||
events.0,
|
events.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -307,12 +304,12 @@ where
|
||||||
// SAFETY: the opaque is "this", which is indeed a pointer to T
|
// SAFETY: the opaque is "this", which is indeed a pointer to T
|
||||||
F::call((unsafe { &*(opaque.cast::<T>()) }, event))
|
F::call((unsafe { &*(opaque.cast::<T>()) }, event))
|
||||||
}
|
}
|
||||||
Some(rust_clock_cb::<Self::Target, F>)
|
Some(rust_clock_cb::<T, F>)
|
||||||
} else {
|
} else {
|
||||||
None
|
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`.
|
/// Add an output clock named `name`.
|
||||||
|
@ -324,16 +321,30 @@ where
|
||||||
/// which Rust code has a reference to a child object) it would be
|
/// which Rust code has a reference to a child object) it would be
|
||||||
/// possible for this function to return a `&Clock` too.
|
/// possible for this function to return a `&Clock` too.
|
||||||
#[inline]
|
#[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 {
|
unsafe {
|
||||||
let cstr = CString::new(name).unwrap();
|
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);
|
let clk: &Clock = Clock::from_raw(clk);
|
||||||
Owned::from(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>) {
|
fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
|
||||||
assert!(bql_locked());
|
assert!(bql_locked());
|
||||||
let c_propname = CString::new(propname).unwrap();
|
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) {
|
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>
|
// SAFETY: obj is an instance of T, since rust_instance_init<T>
|
||||||
// is called from QOM core as the instance_init function
|
// is called from QOM core as the instance_init function
|
||||||
// for class T
|
// for class T
|
||||||
unsafe {
|
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
|
/// FIXME: The argument is not really a valid reference. `&mut
|
||||||
/// MaybeUninit<Self>` would be a better description.
|
/// 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
|
/// Function that is called to finish initialization of an object, once
|
||||||
/// `INSTANCE_INIT` functions have been called.
|
/// `INSTANCE_INIT` functions have been called.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue