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 core::ptr::NonNull; 6 use std::os::raw::{c_int, c_void}; 7 8 use qemu_api::{ 9 bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, 10 vmstate_uint32, vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, 11 }; 12 13 use crate::device::{PL011State, PL011_FIFO_DEPTH}; 14 15 extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { 16 unsafe { 17 debug_assert!(!opaque.is_null()); 18 let state = NonNull::new_unchecked(opaque.cast::<PL011State>()); 19 state.as_ref().migrate_clock 20 } 21 } 22 23 /// Migration subsection for [`PL011State`] clock. 24 pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { 25 name: c_str!("pl011/clock").as_ptr(), 26 version_id: 1, 27 minimum_version_id: 1, 28 needed: Some(pl011_clock_needed), 29 fields: vmstate_fields! { 30 vmstate_clock!(clock, PL011State), 31 }, 32 ..Zeroable::ZERO 33 }; 34 35 extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { 36 unsafe { 37 debug_assert!(!opaque.is_null()); 38 let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); 39 let result = state.as_mut().post_load(version_id as u32); 40 if result.is_err() { 41 -1 42 } else { 43 0 44 } 45 } 46 } 47 48 pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { 49 name: c_str!("pl011").as_ptr(), 50 version_id: 2, 51 minimum_version_id: 2, 52 post_load: Some(pl011_post_load), 53 fields: vmstate_fields! { 54 vmstate_unused!(core::mem::size_of::<u32>()), 55 vmstate_uint32!(flags, PL011State), 56 vmstate_uint32!(line_control, PL011State), 57 vmstate_uint32!(receive_status_error_clear, PL011State), 58 vmstate_uint32!(control, PL011State), 59 vmstate_uint32!(dmacr, PL011State), 60 vmstate_uint32!(int_enabled, PL011State), 61 vmstate_uint32!(int_level, PL011State), 62 vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH), 63 vmstate_uint32!(ilpr, PL011State), 64 vmstate_uint32!(ibrd, PL011State), 65 vmstate_uint32!(fbrd, PL011State), 66 vmstate_uint32!(ifl, PL011State), 67 vmstate_int32!(read_pos, PL011State), 68 vmstate_int32!(read_count, PL011State), 69 vmstate_int32!(read_trigger, PL011State), 70 }, 71 subsections: vmstate_subsections! { 72 VMSTATE_PL011_CLOCK 73 }, 74 ..Zeroable::ZERO 75 }; 76 77 qemu_api::declare_properties! { 78 PL011_PROPERTIES, 79 qemu_api::define_property!( 80 c_str!("chardev"), 81 PL011State, 82 char_backend, 83 unsafe { &qdev_prop_chr }, 84 CharBackend 85 ), 86 qemu_api::define_property!( 87 c_str!("migrate-clk"), 88 PL011State, 89 migrate_clock, 90 unsafe { &qdev_prop_bool }, 91 bool, 92 default = true 93 ), 94 } 95 96 qemu_api::device_class_init! { 97 pl011_class_init, 98 props => PL011_PROPERTIES, 99 realize_fn => Some(pl011_realize), 100 legacy_reset_fn => Some(pl011_reset), 101 vmsd => VMSTATE_PL011, 102 } 103 104 /// # Safety 105 /// 106 /// We expect the FFI user of this function to pass a valid pointer, that has 107 /// the same size as [`PL011State`]. We also expect the device is 108 /// readable/writeable from one thread at any time. 109 pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) { 110 unsafe { 111 assert!(!dev.is_null()); 112 let mut state = NonNull::new_unchecked(dev.cast::<PL011State>()); 113 state.as_mut().realize(); 114 } 115 } 116 117 /// # Safety 118 /// 119 /// We expect the FFI user of this function to pass a valid pointer, that has 120 /// the same size as [`PL011State`]. We also expect the device is 121 /// readable/writeable from one thread at any time. 122 pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) { 123 unsafe { 124 assert!(!dev.is_null()); 125 let mut state = NonNull::new_unchecked(dev.cast::<PL011State>()); 126 state.as_mut().reset(); 127 } 128 } 129