@@ -20,6 +20,12 @@
static PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
+/// Integer Baud Rate Divider, `UARTIBRD`
+const IBRD_MASK: u32 = 0x3f;
+
+/// Fractional Baud Rate Divider, `UARTFBRD`
+const FBRD_MASK: u32 = 0xffff;
+
const DATA_BREAK: u32 = 1 << 10;
/// QEMU sourced constant.
@@ -492,6 +498,27 @@ pub fn update(&self) {
unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) };
}
}
+
+ pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> {
+ /* Sanity-check input state */
+ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
+ return Err(());
+ }
+
+ if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
+ // Older versions of PL011 didn't ensure that the single
+ // character in the FIFO in FIFO-disabled mode is in
+ // element 0 of the array; convert to follow the current
+ // code's assumptions.
+ self.read_fifo[0] = self.read_fifo[self.read_pos];
+ self.read_pos = 0;
+ }
+
+ self.ibrd &= IBRD_MASK;
+ self.fbrd &= FBRD_MASK;
+
+ Ok(())
+ }
}
/// Which bits in the interrupt status matter for each outbound IRQ line ?
@@ -2,16 +2,73 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use core::ptr::NonNull;
+use core::{
+ ffi::{c_int, c_void},
+ ptr::NonNull,
+};
-use qemu_api::{bindings::*, definitions::ObjectImpl, zeroable::Zeroable};
+use qemu_api::{
+ bindings::*, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, vmstate_uint32,
+ vmstate_uint32_array, vmstate_unused, zeroable::Zeroable,
+};
-use crate::device::PL011State;
+use crate::device::{PL011State, PL011_FIFO_DEPTH};
+
+extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
+ unsafe {
+ debug_assert!(!opaque.is_null());
+ let state = NonNull::new_unchecked(opaque.cast::<PL011State>());
+ state.as_ref().migrate_clock
+ }
+}
+
+/// Migration subsection for [`PL011State`] clock.
+pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
+ name: c"pl011/clock".as_ptr(),
+ version_id: 1,
+ minimum_version_id: 1,
+ needed: Some(pl011_clock_needed),
+ fields: vmstate_fields! {
+ vmstate_clock!(clock, PL011State),
+ },
+ ..Zeroable::ZERO
+};
+
+extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
+ unsafe {
+ debug_assert!(!opaque.is_null());
+ let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
+ let result = state.as_mut().post_load(version_id as u32);
+ if result.is_err() { -1 } else { 0 }
+ }
+}
-#[used]
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
- name: PL011State::TYPE_INFO.name,
- unmigratable: true,
+ name: c"pl011".as_ptr(),
+ version_id: 2,
+ minimum_version_id: 2,
+ post_load: Some(pl011_post_load),
+ fields: vmstate_fields! {
+ vmstate_unused!(::core::mem::size_of::<u32>()),
+ vmstate_uint32!(flags, PL011State),
+ vmstate_uint32!(line_control, PL011State),
+ vmstate_uint32!(receive_status_error_clear, PL011State),
+ vmstate_uint32!(control, PL011State),
+ vmstate_uint32!(dmacr, PL011State),
+ vmstate_uint32!(int_enabled, PL011State),
+ vmstate_uint32!(int_level, PL011State),
+ vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH),
+ vmstate_uint32!(ilpr, PL011State),
+ vmstate_uint32!(ibrd, PL011State),
+ vmstate_uint32!(fbrd, PL011State),
+ vmstate_uint32!(ifl, PL011State),
+ vmstate_int32!(read_pos, PL011State),
+ vmstate_int32!(read_count, PL011State),
+ vmstate_int32!(read_trigger, PL011State),
+ },
+ subsections: vmstate_subsections! {
+ VMSTATE_PL011_CLOCK
+ },
..Zeroable::ZERO
};