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 use std::{ 6 ffi::{c_void, CStr}, 7 ptr::{addr_of, addr_of_mut}, 8 }; 9 10 use qemu_api::{ 11 bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool}, 12 c_str, 13 cell::{self, BqlCell}, 14 declare_properties, define_property, 15 prelude::*, 16 qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, 17 qom::{ClassInitImpl, ObjectImpl, ParentField}, 18 sysbus::SysBusDevice, 19 vmstate::VMStateDescription, 20 zeroable::Zeroable, 21 }; 22 23 // Test that macros can compile. 24 pub static VMSTATE: VMStateDescription = VMStateDescription { 25 name: c_str!("name").as_ptr(), 26 unmigratable: true, 27 ..Zeroable::ZERO 28 }; 29 30 #[derive(qemu_api_macros::offsets)] 31 #[repr(C)] 32 #[derive(qemu_api_macros::Object)] 33 pub struct DummyState { 34 parent: ParentField<DeviceState>, 35 migrate_clock: bool, 36 } 37 38 qom_isa!(DummyState: Object, DeviceState); 39 40 pub struct DummyClass { 41 parent_class: <DeviceState as ObjectType>::Class, 42 } 43 44 declare_properties! { 45 DUMMY_PROPERTIES, 46 define_property!( 47 c_str!("migrate-clk"), 48 DummyState, 49 migrate_clock, 50 unsafe { &qdev_prop_bool }, 51 bool 52 ), 53 } 54 55 unsafe impl ObjectType for DummyState { 56 type Class = DummyClass; 57 const TYPE_NAME: &'static CStr = c_str!("dummy"); 58 } 59 60 impl ObjectImpl for DummyState { 61 type ParentType = DeviceState; 62 const ABSTRACT: bool = false; 63 const CLASS_INIT: fn(&mut DummyClass) = <Self as ClassInitImpl<DummyClass>>::class_init; 64 } 65 66 impl ResettablePhasesImpl for DummyState {} 67 68 impl DeviceImpl for DummyState { 69 fn properties() -> &'static [Property] { 70 &DUMMY_PROPERTIES 71 } 72 fn vmsd() -> Option<&'static VMStateDescription> { 73 Some(&VMSTATE) 74 } 75 } 76 77 // `impl<T> ClassInitImpl<DummyClass> for T` doesn't work since it violates 78 // orphan rule. 79 impl ClassInitImpl<DummyClass> for DummyState { 80 fn class_init(klass: &mut DummyClass) { 81 <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); 82 } 83 } 84 85 #[derive(qemu_api_macros::offsets)] 86 #[repr(C)] 87 #[derive(qemu_api_macros::Object)] 88 pub struct DummyChildState { 89 parent: ParentField<DummyState>, 90 } 91 92 qom_isa!(DummyChildState: Object, DeviceState, DummyState); 93 94 pub struct DummyChildClass { 95 parent_class: <DummyState as ObjectType>::Class, 96 } 97 98 unsafe impl ObjectType for DummyChildState { 99 type Class = DummyChildClass; 100 const TYPE_NAME: &'static CStr = c_str!("dummy_child"); 101 } 102 103 impl ObjectImpl for DummyChildState { 104 type ParentType = DummyState; 105 const ABSTRACT: bool = false; 106 const CLASS_INIT: fn(&mut DummyChildClass) = 107 <Self as ClassInitImpl<DummyChildClass>>::class_init; 108 } 109 110 impl ResettablePhasesImpl for DummyChildState {} 111 impl DeviceImpl for DummyChildState {} 112 113 impl ClassInitImpl<DummyClass> for DummyChildState { 114 fn class_init(klass: &mut DummyClass) { 115 <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); 116 } 117 } 118 119 impl ClassInitImpl<DummyChildClass> for DummyChildState { 120 fn class_init(klass: &mut DummyChildClass) { 121 <Self as ClassInitImpl<DummyClass>>::class_init(&mut klass.parent_class); 122 } 123 } 124 125 fn init_qom() { 126 static ONCE: BqlCell<bool> = BqlCell::new(false); 127 128 cell::bql_start_test(); 129 if !ONCE.get() { 130 unsafe { 131 module_call_init(module_init_type::MODULE_INIT_QOM); 132 } 133 ONCE.set(true); 134 } 135 } 136 137 #[test] 138 /// Create and immediately drop an instance. 139 fn test_object_new() { 140 init_qom(); 141 drop(DummyState::new()); 142 drop(DummyChildState::new()); 143 } 144 145 #[test] 146 #[allow(clippy::redundant_clone)] 147 /// Create, clone and then drop an instance. 148 fn test_clone() { 149 init_qom(); 150 let p = DummyState::new(); 151 assert_eq!(p.clone().typename(), "dummy"); 152 drop(p); 153 } 154 155 #[test] 156 /// Try invoking a method on an object. 157 fn test_typename() { 158 init_qom(); 159 let p = DummyState::new(); 160 assert_eq!(p.typename(), "dummy"); 161 } 162 163 // a note on all "cast" tests: usually, especially for downcasts the desired 164 // class would be placed on the right, for example: 165 // 166 // let sbd_ref = p.dynamic_cast::<SysBusDevice>(); 167 // 168 // Here I am doing the opposite to check that the resulting type is correct. 169 170 #[test] 171 #[allow(clippy::shadow_unrelated)] 172 /// Test casts on shared references. 173 fn test_cast() { 174 init_qom(); 175 let p = DummyState::new(); 176 let p_ptr: *mut DummyState = p.as_mut_ptr(); 177 let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; 178 179 let obj_ref: &Object = p_ref.upcast(); 180 assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); 181 182 let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); 183 assert!(sbd_ref.is_none()); 184 185 let dev_ref: Option<&DeviceState> = obj_ref.downcast(); 186 assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); 187 188 // SAFETY: the cast is wrong, but the value is only used for comparison 189 unsafe { 190 let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); 191 assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); 192 } 193 } 194 195 #[test] 196 #[allow(clippy::shadow_unrelated)] 197 /// Test casts on mutable references. 198 fn test_cast_mut() { 199 init_qom(); 200 let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; 201 202 let p_ref: &mut DummyState = unsafe { &mut *p }; 203 let obj_ref: &mut Object = p_ref.upcast_mut(); 204 assert_eq!(addr_of_mut!(*obj_ref), p.cast()); 205 206 let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut(); 207 let obj_ref = sbd_ref.unwrap_err(); 208 209 let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut(); 210 let dev_ref = dev_ref.unwrap(); 211 assert_eq!(addr_of_mut!(*dev_ref), p.cast()); 212 213 // SAFETY: the cast is wrong, but the value is only used for comparison 214 unsafe { 215 let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut(); 216 assert_eq!(addr_of_mut!(*sbd_ref), p.cast()); 217 218 object_unref(p_ref.as_object_mut_ptr().cast::<c_void>()); 219 } 220 } 221