xref: /qemu/rust/qemu-api/src/qdev.rs (revision 68da5402df003a855c581563acc6f5f8c5d563f0)
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