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