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_int, c_void}, 10 ptr::NonNull, 11 }; 12 13 pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType}; 14 15 use crate::{ 16 bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass}, 17 callbacks::FnCall, 18 cell::bql_locked, 19 chardev::Chardev, 20 irq::InterruptSource, 21 prelude::*, 22 qom::{ObjectClass, ObjectImpl, Owned}, 23 vmstate::VMStateDescription, 24 }; 25 26 /// Trait providing the contents of the `ResettablePhases` struct, 27 /// which is part of the QOM `Resettable` interface. 28 pub trait ResettablePhasesImpl { 29 /// If not None, this is called when the object enters reset. It 30 /// can reset local state of the object, but it must not do anything that 31 /// has a side-effect on other objects, such as raising or lowering an 32 /// [`InterruptSource`], or reading or writing guest memory. It takes the 33 /// reset's type as argument. 34 const ENTER: Option<fn(&Self, ResetType)> = None; 35 36 /// If not None, this is called when the object for entry into reset, once 37 /// every object in the system which is being reset has had its 38 /// `ResettablePhasesImpl::ENTER` method called. At this point devices 39 /// can do actions that affect other objects. 40 /// 41 /// If in doubt, implement this method. 42 const HOLD: Option<fn(&Self, ResetType)> = None; 43 44 /// If not None, this phase is called when the object leaves the reset 45 /// state. Actions affecting other objects are permitted. 46 const EXIT: Option<fn(&Self, ResetType)> = None; 47 } 48 49 /// # Safety 50 /// 51 /// We expect the FFI user of this function to pass a valid pointer that 52 /// can be downcasted to type `T`. We also expect the device is 53 /// readable/writeable from one thread at any time. 54 unsafe extern "C" fn rust_resettable_enter_fn<T: ResettablePhasesImpl>( 55 obj: *mut bindings::Object, 56 typ: ResetType, 57 ) { 58 let state = NonNull::new(obj).unwrap().cast::<T>(); 59 T::ENTER.unwrap()(unsafe { state.as_ref() }, typ); 60 } 61 62 /// # Safety 63 /// 64 /// We expect the FFI user of this function to pass a valid pointer that 65 /// can be downcasted to type `T`. We also expect the device is 66 /// readable/writeable from one thread at any time. 67 unsafe extern "C" fn rust_resettable_hold_fn<T: ResettablePhasesImpl>( 68 obj: *mut bindings::Object, 69 typ: ResetType, 70 ) { 71 let state = NonNull::new(obj).unwrap().cast::<T>(); 72 T::HOLD.unwrap()(unsafe { state.as_ref() }, typ); 73 } 74 75 /// # Safety 76 /// 77 /// We expect the FFI user of this function to pass a valid pointer that 78 /// can be downcasted to type `T`. We also expect the device is 79 /// readable/writeable from one thread at any time. 80 unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>( 81 obj: *mut bindings::Object, 82 typ: ResetType, 83 ) { 84 let state = NonNull::new(obj).unwrap().cast::<T>(); 85 T::EXIT.unwrap()(unsafe { state.as_ref() }, typ); 86 } 87 88 /// Trait providing the contents of [`DeviceClass`]. 89 pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> { 90 /// _Realization_ is the second stage of device creation. It contains 91 /// all operations that depend on device properties and can fail (note: 92 /// this is not yet supported for Rust devices). 93 /// 94 /// If not `None`, the parent class's `realize` method is overridden 95 /// with the function pointed to by `REALIZE`. 96 const REALIZE: Option<fn(&Self)> = None; 97 98 /// An array providing the properties that the user can set on the 99 /// device. Not a `const` because referencing statics in constants 100 /// is unstable until Rust 1.83.0. 101 fn properties() -> &'static [Property] { 102 &[] 103 } 104 105 /// A `VMStateDescription` providing the migration format for the device 106 /// Not a `const` because referencing statics in constants is unstable 107 /// until Rust 1.83.0. 108 fn vmsd() -> Option<&'static VMStateDescription> { 109 None 110 } 111 } 112 113 /// # Safety 114 /// 115 /// This function is only called through the QOM machinery and 116 /// used by `DeviceClass::class_init`. 117 /// We expect the FFI user of this function to pass a valid pointer that 118 /// can be downcasted to type `T`. We also expect the device is 119 /// readable/writeable from one thread at any time. 120 unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) { 121 let state = NonNull::new(dev).unwrap().cast::<T>(); 122 T::REALIZE.unwrap()(unsafe { state.as_ref() }); 123 } 124 125 unsafe impl InterfaceType for ResettableClass { 126 const TYPE_NAME: &'static CStr = 127 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; 128 } 129 130 impl ResettableClass { 131 /// Fill in the virtual methods of `ResettableClass` based on the 132 /// definitions in the `ResettablePhasesImpl` trait. 133 pub fn class_init<T: ResettablePhasesImpl>(&mut self) { 134 if <T as ResettablePhasesImpl>::ENTER.is_some() { 135 self.phases.enter = Some(rust_resettable_enter_fn::<T>); 136 } 137 if <T as ResettablePhasesImpl>::HOLD.is_some() { 138 self.phases.hold = Some(rust_resettable_hold_fn::<T>); 139 } 140 if <T as ResettablePhasesImpl>::EXIT.is_some() { 141 self.phases.exit = Some(rust_resettable_exit_fn::<T>); 142 } 143 } 144 } 145 146 impl DeviceClass { 147 /// Fill in the virtual methods of `DeviceClass` based on the definitions in 148 /// the `DeviceImpl` trait. 149 pub fn class_init<T: DeviceImpl>(&mut self) { 150 if <T as DeviceImpl>::REALIZE.is_some() { 151 self.realize = Some(rust_realize_fn::<T>); 152 } 153 if let Some(vmsd) = <T as DeviceImpl>::vmsd() { 154 self.vmsd = vmsd; 155 } 156 let prop = <T as DeviceImpl>::properties(); 157 if !prop.is_empty() { 158 unsafe { 159 bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); 160 } 161 } 162 163 ResettableClass::cast::<DeviceState>(self).class_init::<T>(); 164 self.parent_class.class_init::<T>(); 165 } 166 } 167 168 #[macro_export] 169 macro_rules! define_property { 170 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => { 171 $crate::bindings::Property { 172 // use associated function syntax for type checking 173 name: ::std::ffi::CStr::as_ptr($name), 174 info: $prop, 175 offset: $crate::offset_of!($state, $field) as isize, 176 bitnr: $bitnr, 177 set_default: true, 178 defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, 179 ..$crate::zeroable::Zeroable::ZERO 180 } 181 }; 182 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { 183 $crate::bindings::Property { 184 // use associated function syntax for type checking 185 name: ::std::ffi::CStr::as_ptr($name), 186 info: $prop, 187 offset: $crate::offset_of!($state, $field) as isize, 188 set_default: true, 189 defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, 190 ..$crate::zeroable::Zeroable::ZERO 191 } 192 }; 193 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { 194 $crate::bindings::Property { 195 // use associated function syntax for type checking 196 name: ::std::ffi::CStr::as_ptr($name), 197 info: $prop, 198 offset: $crate::offset_of!($state, $field) as isize, 199 set_default: false, 200 ..$crate::zeroable::Zeroable::ZERO 201 } 202 }; 203 } 204 205 #[macro_export] 206 macro_rules! declare_properties { 207 ($ident:ident, $($prop:expr),*$(,)*) => { 208 pub static $ident: [$crate::bindings::Property; { 209 let mut len = 0; 210 $({ 211 _ = stringify!($prop); 212 len += 1; 213 })* 214 len 215 }] = [ 216 $($prop),*, 217 ]; 218 }; 219 } 220 221 unsafe impl ObjectType for DeviceState { 222 type Class = DeviceClass; 223 const TYPE_NAME: &'static CStr = 224 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; 225 } 226 qom_isa!(DeviceState: Object); 227 228 /// Trait for methods exposed by the [`DeviceState`] class. The methods can be 229 /// called on all objects that have the trait `IsA<DeviceState>`. 230 /// 231 /// The trait should only be used through the blanket implementation, 232 /// which guarantees safety via `IsA`. 233 pub trait DeviceMethods: ObjectDeref 234 where 235 Self::Target: IsA<DeviceState>, 236 { 237 /// Add an input clock named `name`. Invoke the callback with 238 /// `self` as the first parameter for the events that are requested. 239 /// 240 /// The resulting clock is added as a child of `self`, but it also 241 /// stays alive until after `Drop::drop` is called because C code 242 /// keeps an extra reference to it until `device_finalize()` calls 243 /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in 244 /// which Rust code has a reference to a child object) it would be 245 /// possible for this function to return a `&Clock` too. 246 #[inline] 247 fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>( 248 &self, 249 name: &str, 250 _cb: &F, 251 events: ClockEvent, 252 ) -> Owned<Clock> { 253 fn do_init_clock_in( 254 dev: *mut DeviceState, 255 name: &str, 256 cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>, 257 events: ClockEvent, 258 ) -> Owned<Clock> { 259 assert!(bql_locked()); 260 261 // SAFETY: the clock is heap allocated, but qdev_init_clock_in() 262 // does not gift the reference to its caller; so use Owned::from to 263 // add one. The callback is disabled automatically when the clock 264 // is unparented, which happens before the device is finalized. 265 unsafe { 266 let cstr = CString::new(name).unwrap(); 267 let clk = bindings::qdev_init_clock_in( 268 dev, 269 cstr.as_ptr(), 270 cb, 271 dev.cast::<c_void>(), 272 events.0, 273 ); 274 275 Owned::from(&*clk) 276 } 277 } 278 279 let cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)> = if F::is_some() { 280 unsafe extern "C" fn rust_clock_cb<T, F: for<'a> FnCall<(&'a T, ClockEvent)>>( 281 opaque: *mut c_void, 282 event: ClockEvent, 283 ) { 284 // SAFETY: the opaque is "this", which is indeed a pointer to T 285 F::call((unsafe { &*(opaque.cast::<T>()) }, event)) 286 } 287 Some(rust_clock_cb::<Self::Target, F>) 288 } else { 289 None 290 }; 291 292 do_init_clock_in(self.as_mut_ptr(), name, cb, events) 293 } 294 295 /// Add an output clock named `name`. 296 /// 297 /// The resulting clock is added as a child of `self`, but it also 298 /// stays alive until after `Drop::drop` is called because C code 299 /// keeps an extra reference to it until `device_finalize()` calls 300 /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in 301 /// which Rust code has a reference to a child object) it would be 302 /// possible for this function to return a `&Clock` too. 303 #[inline] 304 fn init_clock_out(&self, name: &str) -> Owned<Clock> { 305 unsafe { 306 let cstr = CString::new(name).unwrap(); 307 let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr()); 308 309 Owned::from(&*clk) 310 } 311 } 312 313 fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) { 314 assert!(bql_locked()); 315 let c_propname = CString::new(propname).unwrap(); 316 unsafe { 317 bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr()); 318 } 319 } 320 321 fn init_gpio_in<F: for<'a> FnCall<(&'a Self::Target, u32, u32)>>( 322 &self, 323 num_lines: u32, 324 _cb: F, 325 ) { 326 let _: () = F::ASSERT_IS_SOME; 327 328 unsafe extern "C" fn rust_irq_handler<T, F: for<'a> FnCall<(&'a T, u32, u32)>>( 329 opaque: *mut c_void, 330 line: c_int, 331 level: c_int, 332 ) { 333 // SAFETY: the opaque was passed as a reference to `T` 334 F::call((unsafe { &*(opaque.cast::<T>()) }, line as u32, level as u32)) 335 } 336 337 let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) = 338 rust_irq_handler::<Self::Target, F>; 339 340 unsafe { 341 qdev_init_gpio_in( 342 self.as_mut_ptr::<DeviceState>(), 343 Some(gpio_in_cb), 344 num_lines as c_int, 345 ); 346 } 347 } 348 349 fn init_gpio_out(&self, pins: &[InterruptSource]) { 350 unsafe { 351 qdev_init_gpio_out( 352 self.as_mut_ptr::<DeviceState>(), 353 InterruptSource::slice_as_ptr(pins), 354 pins.len() as c_int, 355 ); 356 } 357 } 358 } 359 360 impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {} 361 362 unsafe impl ObjectType for Clock { 363 type Class = ObjectClass; 364 const TYPE_NAME: &'static CStr = 365 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; 366 } 367 qom_isa!(Clock: Object); 368