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