mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-06 02:25:23 +00:00
153 lines
4.5 KiB
Rust
153 lines
4.5 KiB
Rust
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||
|
use sys_util::EventFd;
|
||
|
|
||
|
use BusDevice;
|
||
|
|
||
|
// Register offsets
|
||
|
// Data register
|
||
|
const RTCDR: u64 = 0x0;
|
||
|
// Match register
|
||
|
const RTCMR: u64 = 0x4;
|
||
|
// Interrupt status register
|
||
|
const RTCSTAT: u64 = 0x8;
|
||
|
// Interrupt clear register
|
||
|
const RTCEOI: u64 = 0x8;
|
||
|
// Counter load register
|
||
|
const RTCLR: u64 = 0xC;
|
||
|
// Counter register
|
||
|
const RTCCR: u64 = 0x10;
|
||
|
|
||
|
// A single 4K page is mapped for this device
|
||
|
pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;
|
||
|
|
||
|
// AMBA id registers are at the end of the allocated memory space
|
||
|
const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
|
||
|
const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;
|
||
|
|
||
|
// This is the AMBA id for this device
|
||
|
pub const PL030_AMBA_ID: u32 = 0x00041030;
|
||
|
pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;
|
||
|
|
||
|
/// An emulated ARM pl030 RTC
|
||
|
pub struct Pl030 {
|
||
|
// EventFD to be used to interrupt the guest for an alarm event
|
||
|
alarm_evt: EventFd,
|
||
|
|
||
|
// This is the delta we subtract from current time to get the
|
||
|
// counter value
|
||
|
counter_delta_time: u32,
|
||
|
|
||
|
// This is the value that triggers an alarm interrupt when it
|
||
|
// matches with the rtc time
|
||
|
match_value: u32,
|
||
|
|
||
|
// status flag to keep track of whether the interrupt is cleared
|
||
|
// or not
|
||
|
interrupt_active: bool,
|
||
|
}
|
||
|
|
||
|
fn get_epoch_time() -> u32 {
|
||
|
let epoch_time =
|
||
|
SystemTime::now().duration_since(UNIX_EPOCH).
|
||
|
expect("SystemTime::duration_since failed");
|
||
|
epoch_time.as_secs() as u32
|
||
|
}
|
||
|
|
||
|
|
||
|
impl Pl030 {
|
||
|
/// Constructs a Pl030 device
|
||
|
pub fn new(evt: EventFd) -> Pl030 {
|
||
|
Pl030 {
|
||
|
alarm_evt: evt,
|
||
|
counter_delta_time: get_epoch_time(),
|
||
|
match_value: 0,
|
||
|
interrupt_active: false,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl BusDevice for Pl030 {
|
||
|
fn write(&mut self, offset: u64, data: &[u8]) {
|
||
|
if data.len() != 4 {
|
||
|
warn!("bad write size: {} for pl030", data.len());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let reg_val: u32 = (data[0] as u32) << 24 | (data[1] as u32) << 16 |
|
||
|
(data[2] as u32) << 8 | (data[3] as u32);
|
||
|
match offset {
|
||
|
RTCDR => {
|
||
|
warn!("invalid write to read-only RTCDR register");
|
||
|
},
|
||
|
RTCMR => {
|
||
|
self.match_value = reg_val;
|
||
|
// TODO(sonnyrao): here we need to set up a timer for
|
||
|
// when host time equals the value written here and
|
||
|
// fire the interrupt
|
||
|
warn!("Not implemented: VM tried to set an RTC alarm");
|
||
|
},
|
||
|
RTCEOI => {
|
||
|
if reg_val == 0 {
|
||
|
self.interrupt_active = false;
|
||
|
} else {
|
||
|
self.alarm_evt.write(1).unwrap();
|
||
|
self.interrupt_active = true;
|
||
|
}
|
||
|
},
|
||
|
RTCLR => {
|
||
|
// TODO(sonnyrao): if we ever need to let the VM set it's own time
|
||
|
// then we'll need to keep track of the delta between
|
||
|
// the rtc time it sets and the host's rtc time and
|
||
|
// record that here
|
||
|
warn!("Not implemented: VM tried to set the RTC");
|
||
|
},
|
||
|
RTCCR => {
|
||
|
self.counter_delta_time = get_epoch_time();
|
||
|
},
|
||
|
o => panic!("pl030: bad write offset {}", o)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||
|
if data.len() != 4 {
|
||
|
warn!("bad read size: {} for pl030", data.len());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let reg_content: u32 = match offset {
|
||
|
RTCDR => {
|
||
|
get_epoch_time()
|
||
|
},
|
||
|
RTCMR => {
|
||
|
self.match_value
|
||
|
},
|
||
|
RTCSTAT => {
|
||
|
self.interrupt_active as u32
|
||
|
},
|
||
|
RTCLR => {
|
||
|
warn!("invalid read of RTCLR register");
|
||
|
0
|
||
|
},
|
||
|
RTCCR => {
|
||
|
get_epoch_time() - self.counter_delta_time
|
||
|
},
|
||
|
AMBA_ID_OFFSET => {
|
||
|
PL030_AMBA_ID
|
||
|
},
|
||
|
AMBA_MASK_OFFSET => {
|
||
|
PL030_AMBA_MASK
|
||
|
},
|
||
|
|
||
|
o => panic!("pl030: bad read offset {}", o)
|
||
|
};
|
||
|
data[0] = reg_content as u8;
|
||
|
data[1] = (reg_content >> 8) as u8;
|
||
|
data[2] = (reg_content >> 16) as u8;
|
||
|
data[3] = (reg_content >> 24) as u8;
|
||
|
}
|
||
|
}
|