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