use std::convert::TryFrom;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
const RTCDR: u16 = 0x000; const RTCMR: u16 = 0x004; const RTCLR: u16 = 0x008; const RTCCR: u16 = 0x00C; const RTCIMSC: u16 = 0x010; const RTCRIS: u16 = 0x014; const RTCMIS: u16 = 0x018; const RTCICR: u16 = 0x01C;
const AMBA_IDS: [u8; 8] = [0x31, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
const AMBA_ID_LOW: u16 = 0xFE0;
const AMBA_ID_HIGH: u16 = 0xFFF;
pub trait RtcEvents {
fn invalid_read(&self);
fn invalid_write(&self);
}
#[derive(Debug, Clone, Copy)]
pub struct NoEvents;
impl RtcEvents for NoEvents {
fn invalid_read(&self) {}
fn invalid_write(&self) {}
}
impl<EV: RtcEvents> RtcEvents for Arc<EV> {
fn invalid_read(&self) {
self.as_ref().invalid_read();
}
fn invalid_write(&self) {
self.as_ref().invalid_write();
}
}
#[derive(Debug)]
pub struct Rtc<EV: RtcEvents> {
lr: u32,
offset: i64,
mr: u32,
imsc: u32,
ris: u32,
events: EV,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RtcState {
pub lr: u32,
pub offset: i64,
pub mr: u32,
pub imsc: u32,
pub ris: u32,
}
fn get_current_time() -> u32 {
let epoch_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime::duration_since failed");
epoch_time.as_secs() as u32
}
impl Default for Rtc<NoEvents> {
fn default() -> Self {
Self::new()
}
}
#[allow(clippy::derivable_impls)]
impl Default for RtcState {
fn default() -> Self {
RtcState {
lr: 0,
offset: 0,
mr: 0,
imsc: 0,
ris: 0,
}
}
}
impl Rtc<NoEvents> {
pub fn new() -> Self {
Self::from_state(&RtcState::default(), NoEvents)
}
}
impl<EV: RtcEvents> Rtc<EV> {
pub fn from_state(state: &RtcState, rtc_events: EV) -> Self {
Rtc {
lr: state.lr,
offset: state.offset,
mr: state.mr,
imsc: state.imsc,
ris: state.ris,
events: rtc_events,
}
}
pub fn with_events(rtc_events: EV) -> Self {
Self::from_state(&RtcState::default(), rtc_events)
}
pub fn state(&self) -> RtcState {
RtcState {
lr: self.lr,
offset: self.offset,
mr: self.mr,
imsc: self.imsc,
ris: self.ris,
}
}
pub fn events(&self) -> &EV {
&self.events
}
fn get_rtc_value(&self) -> u32 {
let current_host_time = get_current_time();
u32::try_from(
(current_host_time as i64)
.checked_add(self.offset)
.unwrap_or(current_host_time as i64),
)
.unwrap_or(current_host_time)
}
pub fn write(&mut self, offset: u16, data: &[u8; 4]) {
let val = u32::from_le_bytes(*data);
match offset {
RTCMR => {
self.mr = val;
}
RTCLR => {
self.lr = val;
self.offset = self.lr as i64 - get_current_time() as i64;
}
RTCCR => {
if val == 1 {
self.lr = 0;
self.offset = 0;
}
}
RTCIMSC => {
self.imsc = val & 1;
}
RTCICR => {
self.ris &= !val;
}
_ => {
self.events.invalid_write();
}
};
}
pub fn read(&mut self, offset: u16, data: &mut [u8; 4]) {
let v = if (AMBA_ID_LOW..=AMBA_ID_HIGH).contains(&offset) {
let index = ((offset - AMBA_ID_LOW) >> 2) as usize;
u32::from(AMBA_IDS[index])
} else {
match offset {
RTCDR => self.get_rtc_value(),
RTCMR => {
self.mr
}
RTCLR => self.lr,
RTCCR => 1, RTCIMSC => self.imsc,
RTCRIS => self.ris,
RTCMIS => self.ris & self.imsc,
_ => {
self.events.invalid_read();
return;
}
}
};
*data = v.to_le_bytes();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use vmm_sys_util::metric::Metric;
#[derive(Default)]
struct ExampleRtcMetrics {
invalid_read_count: AtomicU64,
invalid_write_count: AtomicU64,
}
impl RtcEvents for ExampleRtcMetrics {
fn invalid_read(&self) {
self.invalid_read_count.inc();
}
fn invalid_write(&self) {
self.invalid_write_count.inc();
}
}
#[test]
fn test_regression_year_1970() {
let mut rtc = Rtc::new();
let expected_time = get_current_time();
let mut actual_time = [0u8; 4];
rtc.read(RTCDR, &mut actual_time);
assert!(u32::from_le_bytes(actual_time) - expected_time <= 1);
}
#[test]
fn test_data_register() {
let metrics = Arc::new(ExampleRtcMetrics::default());
let mut rtc = Rtc::with_events(metrics);
let mut data = [0; 4];
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 0);
rtc.read(RTCDR, &mut data);
let first_read = u32::from_le_bytes(data);
let delay = Duration::from_millis(1500);
thread::sleep(delay);
rtc.read(RTCDR, &mut data);
let second_read = u32::from_le_bytes(data);
assert!(second_read > first_read);
let delay = Duration::from_millis(1500);
thread::sleep(delay);
data = 0u32.to_le_bytes();
rtc.write(RTCDR, &data);
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 1);
rtc.read(RTCDR, &mut data);
let third_read = u32::from_le_bytes(data);
assert!(third_read > second_read);
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 1);
}
#[test]
fn test_match_register() {
let mut rtc = Rtc::new();
let mut data: [u8; 4];
data = 123u32.to_le_bytes();
rtc.write(RTCMR, &data);
rtc.read(RTCMR, &mut data);
assert_eq!(123, u32::from_le_bytes(data));
}
#[test]
fn test_load_register() {
let mut rtc: Rtc<NoEvents> = Default::default();
let mut data = [0; 4];
rtc.read(RTCDR, &mut data);
let old_val = u32::from_le_bytes(data);
let lr = get_current_time() + 100;
data = lr.to_le_bytes();
rtc.write(RTCLR, &data);
rtc.read(RTCLR, &mut data);
assert_eq!(lr, u32::from_le_bytes(data));
rtc.read(RTCDR, &mut data);
assert_eq!(lr, u32::from_le_bytes(data));
let new_val = u32::from_le_bytes(data);
assert!(new_val > old_val);
let lr = get_current_time() - 100;
data = lr.to_le_bytes();
rtc.write(RTCLR, &data);
rtc.read(RTCDR, &mut data);
let rtc_value = u32::from_le_bytes(data);
assert!(rtc_value < get_current_time());
let lr = u32::MAX;
data = lr.to_le_bytes();
rtc.write(RTCLR, &data);
rtc.read(RTCDR, &mut data);
assert!(rtc.offset > -(u32::MAX as i64) && rtc.offset < u32::MAX as i64);
assert_ne!(u32::from_le_bytes(data), 0);
let lr = 0u32;
data = lr.to_le_bytes();
rtc.write(RTCLR, &data);
rtc.read(RTCDR, &mut data);
assert_eq!(lr, u32::from_le_bytes(data));
}
#[test]
fn test_rtc_value_overflow() {
let mut rtc = Rtc::new();
let mut data: [u8; 4];
let lr_max = u32::MAX;
data = lr_max.to_le_bytes();
rtc.write(RTCLR, &data);
rtc.read(RTCLR, &mut data);
assert_eq!(lr_max, u32::from_le_bytes(data));
rtc.read(RTCDR, &mut data);
assert_eq!(lr_max, u32::from_le_bytes(data));
let delay = Duration::from_millis(1500);
thread::sleep(delay);
rtc.read(RTCDR, &mut data);
assert!(lr_max > u32::from_le_bytes(data));
}
#[test]
fn test_interrupt_mask_set_clear_register() {
let mut rtc = Rtc::new();
let mut data: [u8; 4];
rtc.ris = 1;
data = 1u32.to_le_bytes();
rtc.write(RTCIMSC, &data);
rtc.read(RTCIMSC, &mut data);
assert_eq!(1, u32::from_le_bytes(data));
rtc.read(RTCRIS, &mut data);
assert_eq!(1, u32::from_le_bytes(data));
rtc.read(RTCMIS, &mut data);
assert_eq!(1, u32::from_le_bytes(data));
data = 0u32.to_le_bytes();
rtc.write(RTCIMSC, &data);
rtc.read(RTCIMSC, &mut data);
assert_eq!(0, u32::from_le_bytes(data));
rtc.read(RTCRIS, &mut data);
assert_eq!(1, u32::from_le_bytes(data));
rtc.read(RTCMIS, &mut data);
assert_eq!(0, u32::from_le_bytes(data));
}
#[test]
fn test_interrupt_clear_register() {
let metrics = Arc::new(ExampleRtcMetrics::default());
let mut rtc = Rtc::with_events(metrics);
let mut data = [0; 4];
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 0);
rtc.ris = 1;
rtc.imsc = 1;
rtc.read(RTCRIS, &mut data);
assert_eq!(1, u32::from_le_bytes(data));
rtc.read(RTCMIS, &mut data);
assert_eq!(1, u32::from_le_bytes(data));
data = 1u32.to_le_bytes();
rtc.write(RTCICR, &data);
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 0);
rtc.read(RTCRIS, &mut data);
assert_eq!(0, u32::from_le_bytes(data));
rtc.read(RTCMIS, &mut data);
assert_eq!(0, u32::from_le_bytes(data));
data = 123u32.to_le_bytes();
rtc.read(RTCICR, &mut data);
let v = u32::from_le_bytes(data);
assert_eq!(v, 123);
assert_eq!(rtc.events.invalid_read_count.count(), 1);
assert_eq!(rtc.events.invalid_write_count.count(), 0);
}
#[test]
fn test_control_register() {
let mut rtc = Rtc::new();
let mut data: [u8; 4];
let lr = get_current_time() + 100;
data = lr.to_le_bytes();
rtc.write(RTCLR, &data);
rtc.read(RTCDR, &mut data);
let old_val = u32::from_le_bytes(data);
data = 1u32.to_le_bytes();
rtc.write(RTCCR, &data);
rtc.read(RTCDR, &mut data);
let new_val = u32::from_le_bytes(data);
assert!(new_val < old_val);
data = 0u32.to_le_bytes();
rtc.write(RTCCR, &data);
rtc.read(RTCCR, &mut data);
let v = u32::from_le_bytes(data);
assert_eq!(v, 1);
let delay = Duration::from_millis(1500);
thread::sleep(delay);
let old_val = new_val;
rtc.read(RTCDR, &mut data);
let new_val = u32::from_le_bytes(data);
assert!(new_val > old_val);
}
#[test]
fn test_raw_interrupt_status_register() {
let mut rtc = Rtc::new();
let mut data = [0; 4];
rtc.ris = 1u32;
rtc.read(RTCRIS, &mut data);
assert_eq!(u32::from_le_bytes(data), 1);
data = 0u32.to_le_bytes();
rtc.write(RTCRIS, &data);
rtc.read(RTCRIS, &mut data);
assert_eq!(u32::from_le_bytes(data), 1);
}
#[test]
fn test_mask_interrupt_status_register() {
let mut rtc = Rtc::new();
let mut data = [0; 4];
rtc.ris = 1u32;
rtc.read(RTCIMSC, &mut data);
assert_eq!(0, u32::from_le_bytes(data));
rtc.read(RTCMIS, &mut data);
assert_eq!(u32::from_le_bytes(data), 0);
data = 1u32.to_le_bytes();
rtc.write(RTCIMSC, &data);
rtc.read(RTCMIS, &mut data);
assert_eq!(u32::from_le_bytes(data), 1);
data = 0u32.to_le_bytes();
rtc.write(RTCMIS, &data);
rtc.read(RTCMIS, &mut data);
assert_eq!(u32::from_le_bytes(data), 1);
}
#[test]
fn test_read_only_register_addresses() {
let mut rtc = Rtc::new();
let mut data = [0; 4];
rtc.read(AMBA_ID_LOW, &mut data);
assert_eq!(data[0], AMBA_IDS[0]);
data = 123u32.to_le_bytes();
rtc.write(AMBA_ID_LOW, &data);
rtc.read(AMBA_ID_LOW, &mut data);
assert_eq!(data[0], AMBA_IDS[0]);
data = [0; 4];
rtc.read(AMBA_ID_LOW, &mut data);
assert_eq!(data[0], AMBA_IDS[0]);
data = [0; 4];
rtc.read(AMBA_ID_LOW + 5, &mut data);
assert_eq!(data[0], AMBA_IDS[1]);
}
#[test]
fn test_invalid_write_offset() {
let metrics = Arc::new(ExampleRtcMetrics::default());
let mut rtc = Rtc::with_events(metrics);
let mut data = [0; 4];
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 0);
rtc.read(RTCDR, &mut data);
let first_read = u32::from_le_bytes(data);
data = 123u32.to_le_bytes();
rtc.write(AMBA_ID_HIGH + 4, &data);
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 1);
rtc.read(RTCDR, &mut data);
let second_read = u32::from_le_bytes(data);
assert_eq!(second_read, first_read);
rtc.read(RTCDR, &mut data);
let first_read = u32::from_le_bytes(data);
data = 123u32.to_le_bytes();
rtc.write(RTCLR + 1, &data);
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 2);
rtc.read(RTCDR, &mut data);
let second_read = u32::from_le_bytes(data);
assert_eq!(second_read, first_read);
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 2);
}
#[test]
fn test_invalid_read_offset() {
let metrics = Arc::new(ExampleRtcMetrics::default());
let mut rtc = Rtc::with_events(metrics);
let mut data: [u8; 4];
assert_eq!(rtc.events.invalid_read_count.count(), 0);
assert_eq!(rtc.events.invalid_write_count.count(), 0);
data = 123u32.to_le_bytes();
rtc.read(AMBA_ID_HIGH + 4, &mut data);
assert_eq!(123, u32::from_le_bytes(data));
assert_eq!(rtc.events.invalid_read_count.count(), 1);
assert_eq!(rtc.events.invalid_write_count.count(), 0);
data = 321u32.to_le_bytes();
rtc.read(AMBA_ID_HIGH + 4, &mut data);
assert_eq!(321, u32::from_le_bytes(data));
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];
rtc.read(RTCDR, &mut data);
let first_read = u32::from_le_bytes(data);
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);
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.clone());
let state_after_restore = rtc_from_state.state();
assert_eq!(state, state_after_restore);
rtc.read(RTCDR, &mut data);
let second_read = u32::from_le_bytes(data);
assert!(second_read > first_read);
rtc_from_state.read(RTCLR, &mut data2);
assert_eq!(data, data2);
assert_eq!(rtc_from_state.events.invalid_write_count.count(), 0);
let data3 = 123u32.to_le_bytes();
rtc_from_state.write(AMBA_ID_HIGH + 4, &data3);
assert_eq!(rtc_from_state.events.invalid_write_count.count(), 1);
let state2 = rtc_from_state.state();
let saved_metrics = metrics;
let rtc = Rtc::from_state(&state2, saved_metrics);
assert_eq!(rtc.events.invalid_write_count.count(), 1);
}
#[test]
fn test_overflow_offset() {
let rtc_state = RtcState {
lr: 65535,
offset: 9223372036854710636,
mr: 0,
imsc: 0,
ris: 0,
};
let mut rtc = Rtc::from_state(&rtc_state, NoEvents);
let mut data = [0u8; 4];
rtc.read(RTCDR, &mut data);
}
}