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