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::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, 17 qom::{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 impl DummyClass { 45 pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) { 46 self.parent_class.class_init::<T>(); 47 } 48 } 49 50 declare_properties! { 51 DUMMY_PROPERTIES, 52 define_property!( 53 c_str!("migrate-clk"), 54 DummyState, 55 migrate_clock, 56 unsafe { &qdev_prop_bool }, 57 bool 58 ), 59 } 60 61 unsafe impl ObjectType for DummyState { 62 type Class = DummyClass; 63 const TYPE_NAME: &'static CStr = c_str!("dummy"); 64 } 65 66 impl ObjectImpl for DummyState { 67 type ParentType = DeviceState; 68 const ABSTRACT: bool = false; 69 const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::<Self>; 70 } 71 72 impl ResettablePhasesImpl for DummyState {} 73 74 impl DeviceImpl for DummyState { 75 fn properties() -> &'static [Property] { 76 &DUMMY_PROPERTIES 77 } 78 fn vmsd() -> Option<&'static VMStateDescription> { 79 Some(&VMSTATE) 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 const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::<Self>; 105 } 106 107 impl ResettablePhasesImpl for DummyChildState {} 108 impl DeviceImpl for DummyChildState {} 109 110 impl DummyChildClass { 111 pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) { 112 self.parent_class.class_init::<T>(); 113 } 114 } 115 116 fn init_qom() { 117 static ONCE: BqlCell<bool> = BqlCell::new(false); 118 119 cell::bql_start_test(); 120 if !ONCE.get() { 121 unsafe { 122 module_call_init(module_init_type::MODULE_INIT_QOM); 123 } 124 ONCE.set(true); 125 } 126 } 127 128 #[test] 129 /// Create and immediately drop an instance. 130 fn test_object_new() { 131 init_qom(); 132 drop(DummyState::new()); 133 drop(DummyChildState::new()); 134 } 135 136 #[test] 137 #[allow(clippy::redundant_clone)] 138 /// Create, clone and then drop an instance. 139 fn test_clone() { 140 init_qom(); 141 let p = DummyState::new(); 142 assert_eq!(p.clone().typename(), "dummy"); 143 drop(p); 144 } 145 146 #[test] 147 /// Try invoking a method on an object. 148 fn test_typename() { 149 init_qom(); 150 let p = DummyState::new(); 151 assert_eq!(p.typename(), "dummy"); 152 } 153 154 // a note on all "cast" tests: usually, especially for downcasts the desired 155 // class would be placed on the right, for example: 156 // 157 // let sbd_ref = p.dynamic_cast::<SysBusDevice>(); 158 // 159 // Here I am doing the opposite to check that the resulting type is correct. 160 161 #[test] 162 #[allow(clippy::shadow_unrelated)] 163 /// Test casts on shared references. 164 fn test_cast() { 165 init_qom(); 166 let p = DummyState::new(); 167 let p_ptr: *mut DummyState = p.as_mut_ptr(); 168 let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; 169 170 let obj_ref: &Object = p_ref.upcast(); 171 assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); 172 173 let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); 174 assert!(sbd_ref.is_none()); 175 176 let dev_ref: Option<&DeviceState> = obj_ref.downcast(); 177 assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); 178 179 // SAFETY: the cast is wrong, but the value is only used for comparison 180 unsafe { 181 let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); 182 assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); 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