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 #[derive(qemu_api_macros::offsets)] 30 #[repr(C)] 31 #[derive(qemu_api_macros::Object)] 32 pub struct DummyState { 33 parent: ParentField<DeviceState>, 34 migrate_clock: bool, 35 } 36 37 qom_isa!(DummyState: Object, DeviceState); 38 39 pub struct DummyClass { 40 parent_class: <DeviceState as ObjectType>::Class, 41 } 42 43 impl DummyClass { 44 pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) { 45 self.parent_class.class_init::<T>(); 46 } 47 } 48 49 declare_properties! { 50 DUMMY_PROPERTIES, 51 define_property!( 52 c_str!("migrate-clk"), 53 DummyState, 54 migrate_clock, 55 unsafe { &qdev_prop_bool }, 56 bool 57 ), 58 } 59 60 unsafe impl ObjectType for DummyState { 61 type Class = DummyClass; 62 const TYPE_NAME: &'static CStr = c_str!("dummy"); 63 } 64 65 impl ObjectImpl for DummyState { 66 type ParentType = DeviceState; 67 const ABSTRACT: bool = false; 68 const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::<Self>; 69 } 70 71 impl ResettablePhasesImpl for DummyState {} 72 73 impl DeviceImpl for DummyState { 74 fn properties() -> &'static [Property] { 75 &DUMMY_PROPERTIES 76 } 77 fn vmsd() -> Option<&'static VMStateDescription> { 78 Some(&VMSTATE) 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 const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::<Self>; 104 } 105 106 impl ResettablePhasesImpl for DummyChildState {} 107 impl DeviceImpl for DummyChildState {} 108 109 impl DummyChildClass { 110 pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) { 111 self.parent_class.class_init::<T>(); 112 } 113 } 114 115 fn init_qom() { 116 static ONCE: BqlCell<bool> = BqlCell::new(false); 117 118 cell::bql_start_test(); 119 if !ONCE.get() { 120 unsafe { 121 module_call_init(module_init_type::MODULE_INIT_QOM); 122 } 123 ONCE.set(true); 124 } 125 } 126 127 #[test] 128 /// Create and immediately drop an instance. 129 fn test_object_new() { 130 init_qom(); 131 drop(DummyState::new()); 132 drop(DummyChildState::new()); 133 } 134 135 #[test] 136 #[allow(clippy::redundant_clone)] 137 /// Create, clone and then drop an instance. 138 fn test_clone() { 139 init_qom(); 140 let p = DummyState::new(); 141 assert_eq!(p.clone().typename(), "dummy"); 142 drop(p); 143 } 144 145 #[test] 146 /// Try invoking a method on an object. 147 fn test_typename() { 148 init_qom(); 149 let p = DummyState::new(); 150 assert_eq!(p.typename(), "dummy"); 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 = DummyState::new(); 166 let p_ptr: *mut DummyState = p.as_mut_ptr(); 167 let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; 168 169 let obj_ref: &Object = p_ref.upcast(); 170 assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); 171 172 let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); 173 assert!(sbd_ref.is_none()); 174 175 let dev_ref: Option<&DeviceState> = obj_ref.downcast(); 176 assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); 177 178 // SAFETY: the cast is wrong, but the value is only used for comparison 179 unsafe { 180 let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); 181 assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); 182 } 183 } 184