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::CStr, 7 os::raw::c_void, 8 ptr::{addr_of, addr_of_mut}, 9 }; 10 11 use qemu_api::{ 12 bindings::*, 13 c_str, 14 cell::{self, BqlCell}, 15 declare_properties, define_property, 16 prelude::*, 17 qdev::{DeviceImpl, DeviceState, Property}, 18 qom::{ObjectImpl, ParentField}, 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 declare_properties! { 41 DUMMY_PROPERTIES, 42 define_property!( 43 c_str!("migrate-clk"), 44 DummyState, 45 migrate_clock, 46 unsafe { &qdev_prop_bool }, 47 bool 48 ), 49 } 50 51 unsafe impl ObjectType for DummyState { 52 type Class = <DeviceState as ObjectType>::Class; 53 const TYPE_NAME: &'static CStr = c_str!("dummy"); 54 } 55 56 impl ObjectImpl for DummyState { 57 type ParentType = DeviceState; 58 const ABSTRACT: bool = false; 59 } 60 61 impl DeviceImpl for DummyState { 62 fn properties() -> &'static [Property] { 63 &DUMMY_PROPERTIES 64 } 65 fn vmsd() -> Option<&'static VMStateDescription> { 66 Some(&VMSTATE) 67 } 68 } 69 70 fn init_qom() { 71 static ONCE: BqlCell<bool> = BqlCell::new(false); 72 73 cell::bql_start_test(); 74 if !ONCE.get() { 75 unsafe { 76 module_call_init(module_init_type::MODULE_INIT_QOM); 77 } 78 ONCE.set(true); 79 } 80 } 81 82 #[test] 83 /// Create and immediately drop an instance. 84 fn test_object_new() { 85 init_qom(); 86 unsafe { 87 object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); 88 } 89 } 90 91 #[test] 92 /// Try invoking a method on an object. 93 fn test_typename() { 94 init_qom(); 95 let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; 96 let p_ref: &DummyState = unsafe { &*p }; 97 assert_eq!(p_ref.typename(), "dummy"); 98 unsafe { 99 object_unref(p_ref.as_object_mut_ptr().cast::<c_void>()); 100 } 101 } 102 103 // a note on all "cast" tests: usually, especially for downcasts the desired 104 // class would be placed on the right, for example: 105 // 106 // let sbd_ref = p.dynamic_cast::<SysBusDevice>(); 107 // 108 // Here I am doing the opposite to check that the resulting type is correct. 109 110 #[test] 111 #[allow(clippy::shadow_unrelated)] 112 /// Test casts on shared references. 113 fn test_cast() { 114 init_qom(); 115 let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; 116 117 let p_ref: &DummyState = unsafe { &*p }; 118 let obj_ref: &Object = p_ref.upcast(); 119 assert_eq!(addr_of!(*obj_ref), p.cast()); 120 121 let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); 122 assert!(sbd_ref.is_none()); 123 124 let dev_ref: Option<&DeviceState> = obj_ref.downcast(); 125 assert_eq!(addr_of!(*dev_ref.unwrap()), p.cast()); 126 127 // SAFETY: the cast is wrong, but the value is only used for comparison 128 unsafe { 129 let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); 130 assert_eq!(addr_of!(*sbd_ref), p.cast()); 131 132 object_unref(p_ref.as_object_mut_ptr().cast::<c_void>()); 133 } 134 } 135 136 #[test] 137 #[allow(clippy::shadow_unrelated)] 138 /// Test casts on mutable references. 139 fn test_cast_mut() { 140 init_qom(); 141 let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; 142 143 let p_ref: &mut DummyState = unsafe { &mut *p }; 144 let obj_ref: &mut Object = p_ref.upcast_mut(); 145 assert_eq!(addr_of_mut!(*obj_ref), p.cast()); 146 147 let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut(); 148 let obj_ref = sbd_ref.unwrap_err(); 149 150 let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut(); 151 let dev_ref = dev_ref.unwrap(); 152 assert_eq!(addr_of_mut!(*dev_ref), p.cast()); 153 154 // SAFETY: the cast is wrong, but the value is only used for comparison 155 unsafe { 156 let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut(); 157 assert_eq!(addr_of_mut!(*sbd_ref), p.cast()); 158 159 object_unref(p_ref.as_object_mut_ptr().cast::<c_void>()); 160 } 161 } 162