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