xref: /qemu/rust/qemu-api/src/qdev.rs (revision a630055df39e1960275d0e273af036f794b15662)
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::{ClassInitImpl, 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 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 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 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 {
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 the `ClassInitImpl<DeviceClass>` trait.
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<T> ClassInitImpl<ResettableClass> for T
131 where
132     T: ResettablePhasesImpl,
133 {
134     fn class_init(rc: &mut ResettableClass) {
135         if <T as ResettablePhasesImpl>::ENTER.is_some() {
136             rc.phases.enter = Some(rust_resettable_enter_fn::<T>);
137         }
138         if <T as ResettablePhasesImpl>::HOLD.is_some() {
139             rc.phases.hold = Some(rust_resettable_hold_fn::<T>);
140         }
141         if <T as ResettablePhasesImpl>::EXIT.is_some() {
142             rc.phases.exit = Some(rust_resettable_exit_fn::<T>);
143         }
144     }
145 }
146 
147 impl<T> ClassInitImpl<DeviceClass> for T
148 where
149     T: ClassInitImpl<ObjectClass> + ClassInitImpl<ResettableClass> + DeviceImpl,
150 {
151     fn class_init(dc: &mut DeviceClass) {
152         if <T as DeviceImpl>::REALIZE.is_some() {
153             dc.realize = Some(rust_realize_fn::<T>);
154         }
155         if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
156             dc.vmsd = vmsd;
157         }
158         let prop = <T as DeviceImpl>::properties();
159         if !prop.is_empty() {
160             unsafe {
161                 bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len());
162             }
163         }
164 
165         ResettableClass::interface_init::<T, DeviceState>(dc);
166         <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class);
167     }
168 }
169 
170 #[macro_export]
171 macro_rules! define_property {
172     ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => {
173         $crate::bindings::Property {
174             // use associated function syntax for type checking
175             name: ::std::ffi::CStr::as_ptr($name),
176             info: $prop,
177             offset: $crate::offset_of!($state, $field) as isize,
178             bitnr: $bitnr,
179             set_default: true,
180             defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
181             ..$crate::zeroable::Zeroable::ZERO
182         }
183     };
184     ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => {
185         $crate::bindings::Property {
186             // use associated function syntax for type checking
187             name: ::std::ffi::CStr::as_ptr($name),
188             info: $prop,
189             offset: $crate::offset_of!($state, $field) as isize,
190             set_default: true,
191             defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
192             ..$crate::zeroable::Zeroable::ZERO
193         }
194     };
195     ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => {
196         $crate::bindings::Property {
197             // use associated function syntax for type checking
198             name: ::std::ffi::CStr::as_ptr($name),
199             info: $prop,
200             offset: $crate::offset_of!($state, $field) as isize,
201             set_default: false,
202             ..$crate::zeroable::Zeroable::ZERO
203         }
204     };
205 }
206 
207 #[macro_export]
208 macro_rules! declare_properties {
209     ($ident:ident, $($prop:expr),*$(,)*) => {
210         pub static $ident: [$crate::bindings::Property; {
211             let mut len = 0;
212             $({
213                 _ = stringify!($prop);
214                 len += 1;
215             })*
216             len
217         }] = [
218             $($prop),*,
219         ];
220     };
221 }
222 
223 unsafe impl ObjectType for DeviceState {
224     type Class = DeviceClass;
225     const TYPE_NAME: &'static CStr =
226         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
227 }
228 qom_isa!(DeviceState: Object);
229 
230 /// Trait for methods exposed by the [`DeviceState`] class.  The methods can be
231 /// called on all objects that have the trait `IsA<DeviceState>`.
232 ///
233 /// The trait should only be used through the blanket implementation,
234 /// which guarantees safety via `IsA`.
235 pub trait DeviceMethods: ObjectDeref
236 where
237     Self::Target: IsA<DeviceState>,
238 {
239     /// Add an input clock named `name`.  Invoke the callback with
240     /// `self` as the first parameter for the events that are requested.
241     ///
242     /// The resulting clock is added as a child of `self`, but it also
243     /// stays alive until after `Drop::drop` is called because C code
244     /// keeps an extra reference to it until `device_finalize()` calls
245     /// `qdev_finalize_clocklist()`.  Therefore (unlike most cases in
246     /// which Rust code has a reference to a child object) it would be
247     /// possible for this function to return a `&Clock` too.
248     #[inline]
249     fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>(
250         &self,
251         name: &str,
252         _cb: &F,
253         events: ClockEvent,
254     ) -> Owned<Clock> {
255         fn do_init_clock_in(
256             dev: *mut DeviceState,
257             name: &str,
258             cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
259             events: ClockEvent,
260         ) -> Owned<Clock> {
261             assert!(bql_locked());
262 
263             // SAFETY: the clock is heap allocated, but qdev_init_clock_in()
264             // does not gift the reference to its caller; so use Owned::from to
265             // add one.  The callback is disabled automatically when the clock
266             // is unparented, which happens before the device is finalized.
267             unsafe {
268                 let cstr = CString::new(name).unwrap();
269                 let clk = bindings::qdev_init_clock_in(
270                     dev,
271                     cstr.as_ptr(),
272                     cb,
273                     dev.cast::<c_void>(),
274                     events.0,
275                 );
276 
277                 Owned::from(&*clk)
278             }
279         }
280 
281         let cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)> = if F::is_some() {
282             unsafe extern "C" fn rust_clock_cb<T, F: for<'a> FnCall<(&'a T, ClockEvent)>>(
283                 opaque: *mut c_void,
284                 event: ClockEvent,
285             ) {
286                 // SAFETY: the opaque is "this", which is indeed a pointer to T
287                 F::call((unsafe { &*(opaque.cast::<T>()) }, event))
288             }
289             Some(rust_clock_cb::<Self::Target, F>)
290         } else {
291             None
292         };
293 
294         do_init_clock_in(self.as_mut_ptr(), name, cb, events)
295     }
296 
297     /// Add an output clock named `name`.
298     ///
299     /// The resulting clock is added as a child of `self`, but it also
300     /// stays alive until after `Drop::drop` is called because C code
301     /// keeps an extra reference to it until `device_finalize()` calls
302     /// `qdev_finalize_clocklist()`.  Therefore (unlike most cases in
303     /// which Rust code has a reference to a child object) it would be
304     /// possible for this function to return a `&Clock` too.
305     #[inline]
306     fn init_clock_out(&self, name: &str) -> Owned<Clock> {
307         unsafe {
308             let cstr = CString::new(name).unwrap();
309             let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr());
310 
311             Owned::from(&*clk)
312         }
313     }
314 
315     fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
316         assert!(bql_locked());
317         let c_propname = CString::new(propname).unwrap();
318         unsafe {
319             bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr());
320         }
321     }
322 
323     fn init_gpio_in<F: for<'a> FnCall<(&'a Self::Target, u32, u32)>>(
324         &self,
325         num_lines: u32,
326         _cb: F,
327     ) {
328         let _: () = F::ASSERT_IS_SOME;
329 
330         unsafe extern "C" fn rust_irq_handler<T, F: for<'a> FnCall<(&'a T, u32, u32)>>(
331             opaque: *mut c_void,
332             line: c_int,
333             level: c_int,
334         ) {
335             // SAFETY: the opaque was passed as a reference to `T`
336             F::call((unsafe { &*(opaque.cast::<T>()) }, line as u32, level as u32))
337         }
338 
339         let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) =
340             rust_irq_handler::<Self::Target, F>;
341 
342         unsafe {
343             qdev_init_gpio_in(
344                 self.as_mut_ptr::<DeviceState>(),
345                 Some(gpio_in_cb),
346                 num_lines as c_int,
347             );
348         }
349     }
350 
351     fn init_gpio_out(&self, pins: &[InterruptSource]) {
352         unsafe {
353             qdev_init_gpio_out(
354                 self.as_mut_ptr::<DeviceState>(),
355                 InterruptSource::slice_as_ptr(pins),
356                 pins.len() as c_int,
357             );
358         }
359     }
360 }
361 
362 impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {}
363 
364 unsafe impl ObjectType for Clock {
365     type Class = ObjectClass;
366     const TYPE_NAME: &'static CStr =
367         unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
368 }
369 qom_isa!(Clock: Object);
370