From 7110a886c1e986e9e0e71325518bec35ce4a5e69 Mon Sep 17 00:00:00 2001 From: Laura Loghin Date: Fri, 30 Jul 2021 13:20:11 +0300 Subject: [PATCH] define RtcState object RtcState represents the state of the Rtc device, and is storing the state needed for restoring the device (no implementation detailis). The generic `events` object is not stored due to complexity reasons. More details in the design proposal: https://github.com/rust-vmm/community/pull/118. Signed-off-by: Laura Loghin --- src/rtc_pl031.rs | 141 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 30 deletions(-) diff --git a/src/rtc_pl031.rs b/src/rtc_pl031.rs index c43ad6d..5d1ce16 100644 --- a/src/rtc_pl031.rs +++ b/src/rtc_pl031.rs @@ -87,7 +87,7 @@ impl RtcEvents for Arc { /// # use vm_superio::Rtc; /// /// let mut data = [0; 4]; -/// let mut rtc = Rtc::new(); +/// let mut rtc = Rtc::default(); /// const RTCDR: u16 = 0x0; // Data Register. /// const RTCLR: u16 = 0x8; // Load Register. /// @@ -134,6 +134,21 @@ pub struct Rtc { events: EV, } +/// RTC State. +#[derive(Default)] +pub struct RtcState { + /// The load register. + pub lr: u32, + /// The offset applied to the counter to get the RTC value. + pub offset: i64, + /// The MR register. + pub mr: u32, + /// The interrupt mask. + pub imsc: u32, + /// The raw interrupt value. + pub ris: u32, +} + fn get_current_time() -> u32 { let epoch_time = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -147,52 +162,69 @@ fn get_current_time() -> u32 { epoch_time.as_secs() as u32 } -impl Rtc { - /// Creates a new `AMBA PL031 RTC` instance without any metric - /// capabilities. - /// - /// # Example - /// - /// You can see an example of how to use this function in the - /// [`Example` section from `Rtc`](struct.Rtc.html#example). - pub fn new() -> Rtc { - Self::with_events(NoEvents) - } -} - impl Default for Rtc { fn default() -> Self { Self::new() } } +impl Rtc { + /// Creates a new `AMBA PL031 RTC` instance without any metric + /// capabilities. + pub fn new() -> Self { + Self::from_state(&RtcState::default(), NoEvents) + } +} + impl Rtc { - /// Creates a new `AMBA PL031 RTC` instance and invokes the `rtc_events` + /// Creates a new `AMBA PL031 RTC` instance from a given `state` and invokes the `rtc_events` /// implementation of `RtcEvents` during operation. /// /// # Arguments + /// * `state` - A reference to the state from which the `Rtc` is constructed. /// * `rtc_events` - The `RtcEvents` implementation used to track the occurrence /// of failure or missed events in the RTC operation. - pub fn with_events(rtc_events: EV) -> Self { + pub fn from_state(state: &RtcState, events: EV) -> Self { Rtc { // The load register is initialized to 0. - lr: 0, + lr: state.lr, - offset: 0, + offset: state.offset, // The match register is initialised to zero (not currently used). // TODO: Implement the match register functionality. - mr: 0, + mr: state.mr, // The interrupt mask is initialised as not set. - imsc: 0, + imsc: state.imsc, // The raw interrupt is initialised as not asserted. - ris: 0, + ris: state.ris, // A struct implementing RtcEvents for tracking the occurrence of // significant events. - events: rtc_events, + events, + } + } + + /// Creates a new `AMBA PL031 RTC` instance and invokes the `rtc_events` + /// implementation of `RtcEvents` during operation. + /// + /// # Arguments + /// * `rtc_events` - The `RtcEvents` implementation used to track the occurrence + /// of failure or missed events in the RTC operation. + pub fn with_events(rtc_events: EV) -> Self { + Self::from_state(&RtcState::default(), rtc_events) + } + + /// Returns the state of the RTC. + pub fn state(&self) -> RtcState { + RtcState { + lr: self.lr, + offset: self.offset, + mr: self.mr, + imsc: self.imsc, + ris: self.ris, } } @@ -345,7 +377,7 @@ mod tests { fn test_regression_year_1970() { // This is a regression test for: https://github.com/rust-vmm/vm-superio/issues/47. // The problem is that the time in the guest would show up as in the 1970s. - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let expected_time = get_current_time(); let mut actual_time = [0u8; 4]; @@ -414,7 +446,7 @@ mod tests { // Test reading and writing to the match register. // TODO: Implement the alarm functionality and confirm an interrupt // is raised when the match register is set. - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let mut data: [u8; 4]; // Write to the match register. @@ -492,7 +524,7 @@ mod tests { #[test] fn test_rtc_value_overflow() { // Verify that the RTC value will wrap on overflow instead of panic. - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let mut data: [u8; 4]; // Write u32::MAX to the load register @@ -524,7 +556,7 @@ mod tests { #[test] fn test_interrupt_mask_set_clear_register() { // Test setting and clearing the interrupt mask bit. - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let mut data: [u8; 4]; // Manually set the raw interrupt. @@ -611,7 +643,7 @@ mod tests { fn test_control_register() { // Writing 1 to the Control Register should reset the RTC value. // Writing 0 should have no effect. - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let mut data: [u8; 4]; // Let's move the guest time in the future. @@ -659,7 +691,7 @@ mod tests { fn test_raw_interrupt_status_register() { // Writing to the Raw Interrupt Status Register should have no effect, // and reading should return the value of RTCRIS. - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let mut data = [0; 4]; // Set the raw interrupt for testing. @@ -682,7 +714,7 @@ mod tests { fn test_mask_interrupt_status_register() { // Writing to the Masked Interrupt Status Register should have no effect, // and reading should return the value of RTCRIS & RTCIMSC. - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let mut data = [0; 4]; // Set the raw interrupt for testing. @@ -718,7 +750,7 @@ mod tests { #[test] fn test_read_only_register_addresses() { - let mut rtc = Rtc::new(); + let mut rtc = Rtc::default(); let mut data = [0; 4]; // Read the current value of AMBA_ID_LOW. @@ -854,4 +886,53 @@ mod tests { assert_eq!(rtc.events.invalid_read_count.count(), 2); assert_eq!(rtc.events.invalid_write_count.count(), 0); } + + #[test] + fn test_state() { + let metrics = Arc::new(ExampleRtcMetrics::default()); + let mut rtc = Rtc::with_events(metrics); + let mut data = [0; 4]; + + // Get the RTC value with a load register of 0 (the initial value). + rtc.read(RTCDR, &mut data); + let first_read = u32::from_le_bytes(data); + + // Increment LR and verify that the value was updated. + let lr = get_current_time() + 100; + data = lr.to_le_bytes(); + rtc.write(RTCLR, &data); + + let state = rtc.state(); + rtc.read(RTCLR, &mut data); + assert_eq!(state.lr.to_le_bytes(), data); + + // Do an invalid `write` in order to increment a metric. + let mut data2 = 123u32.to_le_bytes(); + rtc.write(AMBA_ID_HIGH + 4, &data2); + assert_eq!(rtc.events.invalid_write_count.count(), 1); + + let metrics = Arc::new(ExampleRtcMetrics::default()); + let mut rtc_from_state = Rtc::from_state(&state, metrics); + let state_after_restore = rtc_from_state.state(); + + // Check that the old and the new state are identical. + assert_eq!(state.lr, state_after_restore.lr); + assert_eq!(state.ris, state_after_restore.ris); + assert_eq!(state.imsc, state_after_restore.imsc); + assert_eq!(state.offset, state_after_restore.offset); + assert_eq!(state.mr, state_after_restore.mr); + + // Read the data register again. + rtc.read(RTCDR, &mut data); + let second_read = u32::from_le_bytes(data); + // The RTC values should be different. + assert!(second_read > first_read); + + // Reading from the LR register should return the same value as before saving the state. + rtc_from_state.read(RTCLR, &mut data2); + assert_eq!(data, data2); + + // Check that the restored `Rtc` doesn't keep the state of the old `metrics` object. + assert_eq!(rtc_from_state.events.invalid_write_count.count(), 0); + } }