1 // Copyright 2024, Linaro Limited 2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 //! Bindings to create devices and access device functionality from Rust. 6 7 use std::{ 8 ffi::{CStr, CString}, 9 os::raw::c_void, 10 ptr::NonNull, 11 }; 12 13 pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property}; 14 15 use crate::{ 16 bindings::{self, Error}, 17 callbacks::FnCall, 18 cell::bql_locked, 19 prelude::*, 20 qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, 21 vmstate::VMStateDescription, 22 }; 23 24 /// Trait providing the contents of [`DeviceClass`]. 25 pub trait DeviceImpl: ObjectImpl { 26 /// _Realization_ is the second stage of device creation. It contains 27 /// all operations that depend on device properties and can fail (note: 28 /// this is not yet supported for Rust devices). 29 /// 30 /// If not `None`, the parent class's `realize` method is overridden 31 /// with the function pointed to by `REALIZE`. 32 const REALIZE: Option<fn(&Self)> = None; 33 34 /// If not `None`, the parent class's `reset` method is overridden 35 /// with the function pointed to by `RESET`. 36 /// 37 /// Rust does not yet support the three-phase reset protocol; this is 38 /// usually okay for leaf classes. 39 const RESET: Option<fn(&Self)> = None; 40 41 /// An array providing the properties that the user can set on the 42 /// device. Not a `const` because referencing statics in constants 43 /// is unstable until Rust 1.83.0. 44 fn properties() -> &'static [Property] { 45 &[] 46 } 47 48 /// A `VMStateDescription` providing the migration format for the device 49 /// Not a `const` because referencing statics in constants is unstable 50 /// until Rust 1.83.0. 51 fn vmsd() -> Option<&'static VMStateDescription> { 52 None 53 } 54 } 55 56 /// # Safety 57 /// 58 /// This function is only called through the QOM machinery and 59 /// used by the `ClassInitImpl<DeviceClass>` trait. 60 /// We expect the FFI user of this function to pass a valid pointer that 61 /// can be downcasted to type `T`. We also expect the device is 62 /// readable/writeable from one thread at any time. 63 unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) { 64 let state = NonNull::new(dev).unwrap().cast::<T>(); 65 T::REALIZE.unwrap()(unsafe { state.as_ref() }); 66 } 67 68 /// # Safety 69 /// 70 /// We expect the FFI user of this function to pass a valid pointer that 71 /// can be downcasted to type `T`. We also expect the device is 72 /// readable/writeable from one thread at any time. 73 unsafe extern "C" fn rust_reset_fn<T: DeviceImpl>(dev: *mut DeviceState) { 74 let mut state = NonNull::new(dev).unwrap().cast::<T>(); 75 T::RESET.unwrap()(unsafe { state.as_mut() }); 76 } 77 78 impl<T> ClassInitImpl<DeviceClass> for T 79 where 80 T: ClassInitImpl<ObjectClass> + DeviceImpl, 81 { 82 fn class_init(dc: &mut DeviceClass) { 83 if <T as DeviceImpl>::REALIZE.is_some() { 84 dc.realize = Some(rust_realize_fn::<T>); 85 } 86 if <T as DeviceImpl>::RESET.is_some() { 87 unsafe { 88 bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::<T>)); 89 } 90 } 91 if let Some(vmsd) = <T as DeviceImpl>::vmsd() { 92 dc.vmsd = vmsd; 93 } 94 let prop = <T as DeviceImpl>::properties(); 95 if !prop.is_empty() { 96 unsafe { 97 bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); 98 } 99 } 100 101 <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class); 102 } 103 } 104 105 #[macro_export] 106 macro_rules! define_property { 107 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { 108 $crate::bindings::Property { 109 // use associated function syntax for type checking 110 name: ::std::ffi::CStr::as_ptr($name), 111 info: $prop, 112 offset: $crate::offset_of!($state, $field) as isize, 113 set_default: true, 114 defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, 115 ..$crate::zeroable::Zeroable::ZERO 116 } 117 }; 118 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { 119 $crate::bindings::Property { 120 // use associated function syntax for type checking 121 name: ::std::ffi::CStr::as_ptr($name), 122 info: $prop, 123 offset: $crate::offset_of!($state, $field) as isize, 124 set_default: false, 125 ..$crate::zeroable::Zeroable::ZERO 126 } 127 }; 128 } 129 130 #[macro_export] 131 macro_rules! declare_properties { 132 ($ident:ident, $($prop:expr),*$(,)*) => { 133 pub static $ident: [$crate::bindings::Property; { 134 let mut len = 0; 135 $({ 136 _ = stringify!($prop); 137 len += 1; 138 })* 139 len 140 }] = [ 141 $($prop),*, 142 ]; 143 }; 144 } 145 146 unsafe impl ObjectType for DeviceState { 147 type Class = DeviceClass; 148 const TYPE_NAME: &'static CStr = 149 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; 150 } 151 qom_isa!(DeviceState: Object); 152 153 /// Trait for methods exposed by the [`DeviceState`] class. The methods can be 154 /// called on all objects that have the trait `IsA<DeviceState>`. 155 /// 156 /// The trait should only be used through the blanket implementation, 157 /// which guarantees safety via `IsA`. 158 pub trait DeviceMethods: ObjectDeref 159 where 160 Self::Target: IsA<DeviceState>, 161 { 162 /// Add an input clock named `name`. Invoke the callback with 163 /// `self` as the first parameter for the events that are requested. 164 /// 165 /// The resulting clock is added as a child of `self`, but it also 166 /// stays alive until after `Drop::drop` is called because C code 167 /// keeps an extra reference to it until `device_finalize()` calls 168 /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in 169 /// which Rust code has a reference to a child object) it would be 170 /// possible for this function to return a `&Clock` too. 171 #[inline] 172 fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>( 173 &self, 174 name: &str, 175 _cb: &F, 176 events: ClockEvent, 177 ) -> Owned<Clock> { 178 fn do_init_clock_in( 179 dev: *mut DeviceState, 180 name: &str, 181 cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>, 182 events: ClockEvent, 183 ) -> Owned<Clock> { 184 assert!(bql_locked()); 185 186 // SAFETY: the clock is heap allocated, but qdev_init_clock_in() 187 // does not gift the reference to its caller; so use Owned::from to 188 // add one. The callback is disabled automatically when the clock 189 // is unparented, which happens before the device is finalized. 190 unsafe { 191 let cstr = CString::new(name).unwrap(); 192 let clk = bindings::qdev_init_clock_in( 193 dev, 194 cstr.as_ptr(), 195 cb, 196 dev.cast::<c_void>(), 197 events.0, 198 ); 199 200 Owned::from(&*clk) 201 } 202 } 203 204 let cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)> = if F::is_some() { 205 unsafe extern "C" fn rust_clock_cb<T, F: for<'a> FnCall<(&'a T, ClockEvent)>>( 206 opaque: *mut c_void, 207 event: ClockEvent, 208 ) { 209 // SAFETY: the opaque is "this", which is indeed a pointer to T 210 F::call((unsafe { &*(opaque.cast::<T>()) }, event)) 211 } 212 Some(rust_clock_cb::<Self::Target, F>) 213 } else { 214 None 215 }; 216 217 do_init_clock_in(self.as_mut_ptr(), name, cb, events) 218 } 219 220 /// Add an output clock named `name`. 221 /// 222 /// The resulting clock is added as a child of `self`, but it also 223 /// stays alive until after `Drop::drop` is called because C code 224 /// keeps an extra reference to it until `device_finalize()` calls 225 /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in 226 /// which Rust code has a reference to a child object) it would be 227 /// possible for this function to return a `&Clock` too. 228 #[inline] 229 fn init_clock_out(&self, name: &str) -> Owned<Clock> { 230 unsafe { 231 let cstr = CString::new(name).unwrap(); 232 let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr()); 233 234 Owned::from(&*clk) 235 } 236 } 237 } 238 239 impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {} 240 241 unsafe impl ObjectType for Clock { 242 type Class = ObjectClass; 243 const TYPE_NAME: &'static CStr = 244 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; 245 } 246 qom_isa!(Clock: Object); 247