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