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