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 cell::{self, BqlCell}, 10 declare_properties, define_property, 11 prelude::*, 12 qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, 13 qom::{ObjectImpl, ParentField}, 14 sysbus::SysBusDevice, 15 vmstate::VMStateDescription, 16 zeroable::Zeroable, 17 }; 18 19 mod vmstate_tests; 20 21 // Test that macros can compile. 22 pub static VMSTATE: VMStateDescription = VMStateDescription { 23 name: c"name".as_ptr(), 24 unmigratable: true, 25 ..Zeroable::ZERO 26 }; 27 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"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"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 #[repr(C)] 81 #[derive(qemu_api_macros::Object)] 82 pub struct DummyChildState { 83 parent: ParentField<DummyState>, 84 } 85 86 qom_isa!(DummyChildState: Object, DeviceState, DummyState); 87 88 pub struct DummyChildClass { 89 parent_class: <DummyState as ObjectType>::Class, 90 } 91 92 unsafe impl ObjectType for DummyChildState { 93 type Class = DummyChildClass; 94 const TYPE_NAME: &'static CStr = c"dummy_child"; 95 } 96 97 impl ObjectImpl for DummyChildState { 98 type ParentType = DummyState; 99 const ABSTRACT: bool = false; 100 const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::<Self>; 101 } 102 103 impl ResettablePhasesImpl for DummyChildState {} 104 impl DeviceImpl for DummyChildState {} 105 106 impl DummyChildClass { 107 pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) { 108 self.parent_class.class_init::<T>(); 109 } 110 } 111 112 fn init_qom() { 113 static ONCE: BqlCell<bool> = BqlCell::new(false); 114 115 cell::bql_start_test(); 116 if !ONCE.get() { 117 unsafe { 118 module_call_init(module_init_type::MODULE_INIT_QOM); 119 } 120 ONCE.set(true); 121 } 122 } 123 124 #[test] 125 /// Create and immediately drop an instance. 126 fn test_object_new() { 127 init_qom(); 128 drop(DummyState::new()); 129 drop(DummyChildState::new()); 130 } 131 132 #[test] 133 #[allow(clippy::redundant_clone)] 134 /// Create, clone and then drop an instance. 135 fn test_clone() { 136 init_qom(); 137 let p = DummyState::new(); 138 assert_eq!(p.clone().typename(), "dummy"); 139 drop(p); 140 } 141 142 #[test] 143 /// Try invoking a method on an object. 144 fn test_typename() { 145 init_qom(); 146 let p = DummyState::new(); 147 assert_eq!(p.typename(), "dummy"); 148 } 149 150 // a note on all "cast" tests: usually, especially for downcasts the desired 151 // class would be placed on the right, for example: 152 // 153 // let sbd_ref = p.dynamic_cast::<SysBusDevice>(); 154 // 155 // Here I am doing the opposite to check that the resulting type is correct. 156 157 #[test] 158 #[allow(clippy::shadow_unrelated)] 159 /// Test casts on shared references. 160 fn test_cast() { 161 init_qom(); 162 let p = DummyState::new(); 163 let p_ptr: *mut DummyState = p.as_mut_ptr(); 164 let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; 165 166 let obj_ref: &Object = p_ref.upcast(); 167 assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); 168 169 let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); 170 assert!(sbd_ref.is_none()); 171 172 let dev_ref: Option<&DeviceState> = obj_ref.downcast(); 173 assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); 174 175 // SAFETY: the cast is wrong, but the value is only used for comparison 176 unsafe { 177 let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); 178 assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); 179 } 180 } 181