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 mod vmstate_tests; 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 #[repr(C)] 30 #[derive(qemu_api_macros::Object)] 31 pub struct DummyState { 32 parent: ParentField<DeviceState>, 33 migrate_clock: bool, 34 } 35 36 qom_isa!(DummyState: Object, DeviceState); 37 38 pub struct DummyClass { 39 parent_class: <DeviceState as ObjectType>::Class, 40 } 41 42 impl DummyClass { 43 pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) { 44 self.parent_class.class_init::<T>(); 45 } 46 } 47 48 declare_properties! { 49 DUMMY_PROPERTIES, 50 define_property!( 51 c_str!("migrate-clk"), 52 DummyState, 53 migrate_clock, 54 unsafe { &qdev_prop_bool }, 55 bool 56 ), 57 } 58 59 unsafe impl ObjectType for DummyState { 60 type Class = DummyClass; 61 const TYPE_NAME: &'static CStr = c_str!("dummy"); 62 } 63 64 impl ObjectImpl for DummyState { 65 type ParentType = DeviceState; 66 const ABSTRACT: bool = false; 67 const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::<Self>; 68 } 69 70 impl ResettablePhasesImpl for DummyState {} 71 72 impl DeviceImpl for DummyState { 73 fn properties() -> &'static [Property] { 74 &DUMMY_PROPERTIES 75 } 76 fn vmsd() -> Option<&'static VMStateDescription> { 77 Some(&VMSTATE) 78 } 79 } 80 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