mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00
rust: qdev: add clock creation
Add a Rust version of qdev_init_clock_in, which can be used in instance_init. There are a couple differences with the C version: - in Rust the object keeps its own reference to the clock (in addition to the one embedded in the NamedClockList), and the reference is dropped automatically by instance_finalize(); this is encoded in the signature of DeviceClassMethods::init_clock_in, which makes the lifetime of the clock independent of that of the object it holds. This goes unnoticed in the C version and is due to the existence of aliases. - also, anything that happens during instance_init uses the pinned_init framework to operate on a partially initialized object, and is done through class methods (i.e. through DeviceClassMethods rather than DeviceMethods) because the device does not exist yet. Therefore, Rust code *must* create clocks from instance_init, which is stricter than C. Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
66bcc554d2
commit
201ef001dd
4 changed files with 125 additions and 31 deletions
|
@ -10,17 +10,16 @@ use std::{
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::{
|
bindings::{
|
||||||
error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_prop_set_chr,
|
error_fatal, hwaddr, memory_region_init_io, qdev_prop_set_chr, qemu_chr_fe_accept_input,
|
||||||
qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers,
|
qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, qemu_irq,
|
||||||
qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map, sysbus_realize,
|
sysbus_connect_irq, sysbus_mmio_map, sysbus_realize, CharBackend, Chardev, MemoryRegion,
|
||||||
CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, QEMUChrEvent,
|
QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK,
|
||||||
CHR_IOCTL_SERIAL_SET_BREAK,
|
|
||||||
},
|
},
|
||||||
c_str, impl_vmstate_forward,
|
c_str, impl_vmstate_forward,
|
||||||
irq::InterruptSource,
|
irq::InterruptSource,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{DeviceImpl, DeviceState, Property},
|
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property},
|
||||||
qom::{ClassInitImpl, ObjectImpl, ParentField},
|
qom::{ClassInitImpl, ObjectImpl, Owned, ParentField},
|
||||||
sysbus::{SysBusDevice, SysBusDeviceClass},
|
sysbus::{SysBusDevice, SysBusDeviceClass},
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
};
|
};
|
||||||
|
@ -131,7 +130,7 @@ pub struct PL011State {
|
||||||
#[doc(alias = "irq")]
|
#[doc(alias = "irq")]
|
||||||
pub interrupts: [InterruptSource; IRQMASK.len()],
|
pub interrupts: [InterruptSource; IRQMASK.len()],
|
||||||
#[doc(alias = "clk")]
|
#[doc(alias = "clk")]
|
||||||
pub clock: NonNull<Clock>,
|
pub clock: Owned<Clock>,
|
||||||
#[doc(alias = "migrate_clk")]
|
#[doc(alias = "migrate_clk")]
|
||||||
pub migrate_clock: bool,
|
pub migrate_clock: bool,
|
||||||
}
|
}
|
||||||
|
@ -485,8 +484,6 @@ impl PL011State {
|
||||||
/// location/instance. All its fields are expected to hold unitialized
|
/// location/instance. All its fields are expected to hold unitialized
|
||||||
/// 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 self) {
|
||||||
const CLK_NAME: &CStr = c_str!("clk");
|
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
//
|
//
|
||||||
// self and self.iomem are guaranteed to be valid at this point since callers
|
// self and self.iomem are guaranteed to be valid at this point since callers
|
||||||
|
@ -506,22 +503,16 @@ impl PL011State {
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
//
|
//
|
||||||
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
|
// self.clock is not initialized at this point; but since `Owned<_>` is
|
||||||
// we can overwrite the undefined value without side effects. This is
|
// not Drop, we can overwrite the undefined value without side effects;
|
||||||
// safe since all PL011State instances are created by QOM code which
|
// it's not sound but, because for all PL011State instances are created
|
||||||
// calls this function to initialize the fields; therefore no code is
|
// by QOM code which calls this function to initialize the fields, at
|
||||||
// able to access an invalid self.clock value.
|
// leastno code is able to access an invalid self.clock value.
|
||||||
unsafe {
|
self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate);
|
||||||
let dev: &mut DeviceState = self.upcast_mut();
|
}
|
||||||
self.clock = NonNull::new(qdev_init_clock_in(
|
|
||||||
dev,
|
const fn clock_update(&self, _event: ClockEvent) {
|
||||||
CLK_NAME.as_ptr(),
|
/* pl011_trace_baudrate_change(s); */
|
||||||
None, /* pl011_clock_update */
|
|
||||||
addr_of_mut!(*self).cast::<c_void>(),
|
|
||||||
ClockEvent::ClockUpdate.0,
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_init(&self) {
|
fn post_init(&self) {
|
||||||
|
|
|
@ -7,6 +7,8 @@ pub use crate::bitops::IntegerExt;
|
||||||
pub use crate::cell::BqlCell;
|
pub use crate::cell::BqlCell;
|
||||||
pub use crate::cell::BqlRefCell;
|
pub use crate::cell::BqlRefCell;
|
||||||
|
|
||||||
|
pub use crate::qdev::DeviceMethods;
|
||||||
|
|
||||||
pub use crate::qom::IsA;
|
pub use crate::qom::IsA;
|
||||||
pub use crate::qom::Object;
|
pub use crate::qom::Object;
|
||||||
pub use crate::qom::ObjectCast;
|
pub use crate::qom::ObjectCast;
|
||||||
|
|
|
@ -4,14 +4,20 @@
|
||||||
|
|
||||||
//! Bindings to create devices and access device functionality from Rust.
|
//! Bindings to create devices and access device functionality from Rust.
|
||||||
|
|
||||||
use std::{ffi::CStr, ptr::NonNull};
|
use std::{
|
||||||
|
ffi::{CStr, CString},
|
||||||
|
os::raw::c_void,
|
||||||
|
ptr::NonNull,
|
||||||
|
};
|
||||||
|
|
||||||
pub use bindings::{DeviceClass, DeviceState, Property};
|
pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bindings::{self, Error},
|
bindings::{self, Error},
|
||||||
|
callbacks::FnCall,
|
||||||
|
cell::bql_locked,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qom::{ClassInitImpl, ObjectClass},
|
qom::{ClassInitImpl, ObjectClass, Owned},
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,3 +149,98 @@ unsafe impl ObjectType for DeviceState {
|
||||||
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
|
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
|
||||||
}
|
}
|
||||||
qom_isa!(DeviceState: Object);
|
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>,
|
||||||
|
{
|
||||||
|
/// Add an input clock named `name`. Invoke the callback with
|
||||||
|
/// `self` as the first parameter for the events that are requested.
|
||||||
|
///
|
||||||
|
/// The resulting clock is added as a child of `self`, but it also
|
||||||
|
/// stays alive until after `Drop::drop` is called because C code
|
||||||
|
/// keeps an extra reference to it until `device_finalize()` calls
|
||||||
|
/// `qdev_finalize_clocklist()`. Therefore (unlike most cases in
|
||||||
|
/// 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,
|
||||||
|
name: &str,
|
||||||
|
_cb: &F,
|
||||||
|
events: ClockEvent,
|
||||||
|
) -> Owned<Clock> {
|
||||||
|
fn do_init_clock_in(
|
||||||
|
dev: *mut DeviceState,
|
||||||
|
name: &str,
|
||||||
|
cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
|
||||||
|
events: ClockEvent,
|
||||||
|
) -> Owned<Clock> {
|
||||||
|
assert!(bql_locked());
|
||||||
|
|
||||||
|
// SAFETY: the clock is heap allocated, but qdev_init_clock_in()
|
||||||
|
// does not gift the reference to its caller; so use Owned::from to
|
||||||
|
// add one. The callback is disabled automatically when the clock
|
||||||
|
// is unparented, which happens before the device is finalized.
|
||||||
|
unsafe {
|
||||||
|
let cstr = CString::new(name).unwrap();
|
||||||
|
let clk = bindings::qdev_init_clock_in(
|
||||||
|
dev,
|
||||||
|
cstr.as_ptr(),
|
||||||
|
cb,
|
||||||
|
dev.cast::<c_void>(),
|
||||||
|
events.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
Owned::from(&*clk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)> = if F::is_some() {
|
||||||
|
unsafe extern "C" fn rust_clock_cb<T, F: for<'a> FnCall<(&'a T, ClockEvent)>>(
|
||||||
|
opaque: *mut c_void,
|
||||||
|
event: ClockEvent,
|
||||||
|
) {
|
||||||
|
// 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>)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
do_init_clock_in(self.as_mut_ptr(), name, cb, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an output clock named `name`.
|
||||||
|
///
|
||||||
|
/// The resulting clock is added as a child of `self`, but it also
|
||||||
|
/// stays alive until after `Drop::drop` is called because C code
|
||||||
|
/// keeps an extra reference to it until `device_finalize()` calls
|
||||||
|
/// `qdev_finalize_clocklist()`. Therefore (unlike most cases in
|
||||||
|
/// 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> {
|
||||||
|
unsafe {
|
||||||
|
let cstr = CString::new(name).unwrap();
|
||||||
|
let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr());
|
||||||
|
|
||||||
|
Owned::from(&*clk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {}
|
||||||
|
|
||||||
|
unsafe impl ObjectType for Clock {
|
||||||
|
type Class = ObjectClass;
|
||||||
|
const TYPE_NAME: &'static CStr =
|
||||||
|
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
|
||||||
|
}
|
||||||
|
qom_isa!(Clock: Object);
|
||||||
|
|
|
@ -470,11 +470,11 @@ macro_rules! vmstate_clock {
|
||||||
$crate::assert_field_type!(
|
$crate::assert_field_type!(
|
||||||
$struct_name,
|
$struct_name,
|
||||||
$field_name,
|
$field_name,
|
||||||
core::ptr::NonNull<$crate::bindings::Clock>
|
$crate::qom::Owned<$crate::bindings::Clock>
|
||||||
);
|
);
|
||||||
$crate::offset_of!($struct_name, $field_name)
|
$crate::offset_of!($struct_name, $field_name)
|
||||||
},
|
},
|
||||||
size: ::core::mem::size_of::<*const $crate::bindings::Clock>(),
|
size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
|
||||||
flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0),
|
flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0),
|
||||||
vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
|
vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
|
||||||
..$crate::zeroable::Zeroable::ZERO
|
..$crate::zeroable::Zeroable::ZERO
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue