Skip to content

Commit

Permalink
define RtcState object
Browse files Browse the repository at this point in the history
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:
rust-vmm/community#118.

Signed-off-by: Laura Loghin <[email protected]>
  • Loading branch information
lauralt committed Sep 15, 2021
1 parent 776b803 commit 49d2309
Showing 1 changed file with 111 additions and 30 deletions.
141 changes: 111 additions & 30 deletions src/rtc_pl031.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl<EV: RtcEvents> RtcEvents for Arc<EV> {
/// # 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.
///
Expand Down Expand Up @@ -134,6 +134,21 @@ pub struct Rtc<EV: RtcEvents> {
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)
Expand All @@ -147,52 +162,69 @@ fn get_current_time() -> u32 {
epoch_time.as_secs() as u32
}

impl Rtc<NoEvents> {
/// 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<NoEvents> {
Self::with_events(NoEvents)
}
}

impl Default for Rtc<NoEvents> {
fn default() -> Self {
Self::new()
}
}

impl Rtc<NoEvents> {
/// Creates a new `AMBA PL031 RTC` instance without any metric
/// capabilities.
pub fn new() -> Self {
Self::from_state(&RtcState::default(), NoEvents)
}
}

impl<EV: RtcEvents> Rtc<EV> {
/// 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,
}
}

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 49d2309

Please sign in to comment.