#[inline]
pub fn timer_freq() -> u64 {
let freq: u64;
unsafe {
core::arch::asm!(
"mrs {reg}, CNTFRQ_EL0",
reg = out(reg) freq,
options(nomem, nostack, preserves_flags),
);
}
freq
}
#[inline]
pub fn timer_current() -> u64 {
let cnt: u64;
unsafe {
core::arch::asm!(
"mrs {reg}, CNTPCT_EL0",
reg = out(reg) cnt,
options(nomem, nostack, preserves_flags),
);
}
cnt
}
pub fn timer_init(_freq_hz: u64) {
let ctl: u64 = 1;
unsafe {
core::arch::asm!(
"msr CNTHP_CTL_EL2, {val}",
"isb",
val = in(reg) ctl,
options(nomem, nostack, preserves_flags),
);
}
}
pub fn timer_set_deadline(ticks: u64) {
unsafe {
core::arch::asm!(
"msr CNTHP_CVAL_EL2, {val}",
"isb",
val = in(reg) ticks,
options(nomem, nostack, preserves_flags),
);
}
}
pub fn timer_disable() {
let ctl: u64 = 1 | (1 << 1);
unsafe {
core::arch::asm!(
"msr CNTHP_CTL_EL2, {val}",
"isb",
val = in(reg) ctl,
options(nomem, nostack, preserves_flags),
);
}
}
#[must_use]
pub const fn ns_to_ticks(ns: u64, freq_hz: u64) -> u64 {
if freq_hz == 0 {
return 0;
}
((ns as u128 * freq_hz as u128) / 1_000_000_000) as u64
}
#[must_use]
pub const fn ticks_to_ns(ticks: u64, freq_hz: u64) -> u64 {
if freq_hz == 0 {
return 0;
}
((ticks as u128 * 1_000_000_000) / freq_hz as u128) as u64
}
pub struct Aarch64Timer {
freq_hz: u64,
deadline_active: bool,
}
impl Aarch64Timer {
#[must_use]
pub const fn new() -> Self {
Self {
freq_hz: 0,
deadline_active: false,
}
}
pub fn init(&mut self) {
self.freq_hz = timer_freq();
timer_init(self.freq_hz);
}
#[must_use]
pub const fn freq(&self) -> u64 {
self.freq_hz
}
}
impl crate::TimerOps for Aarch64Timer {
fn now_ns(&self) -> u64 {
if self.freq_hz == 0 {
return 0;
}
ticks_to_ns(timer_current(), self.freq_hz)
}
fn set_deadline_ns(&mut self, ns_from_now: u64) -> rvm_types::RvmResult<()> {
if self.freq_hz == 0 {
return Err(rvm_types::RvmError::InternalError);
}
let delta_ticks = ns_to_ticks(ns_from_now, self.freq_hz);
let deadline = timer_current().saturating_add(delta_ticks);
timer_set_deadline(deadline);
self.deadline_active = true;
Ok(())
}
fn cancel_deadline(&mut self) -> rvm_types::RvmResult<()> {
if !self.deadline_active {
return Err(rvm_types::RvmError::InternalError);
}
timer_disable();
self.deadline_active = false;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TimerOps;
#[test]
fn test_ns_to_ticks() {
assert_eq!(ns_to_ticks(1_000_000_000, 62_500_000), 62_500_000);
}
#[test]
fn test_ticks_to_ns() {
assert_eq!(ticks_to_ns(62_500_000, 62_500_000), 1_000_000_000);
}
#[test]
fn test_roundtrip() {
let freq = 62_500_000u64;
let ns = 500_000_000u64; let ticks = ns_to_ticks(ns, freq);
let result = ticks_to_ns(ticks, freq);
let one_tick_ns = 1_000_000_000 / freq;
assert!((result as i64 - ns as i64).unsigned_abs() <= one_tick_ns);
}
#[test]
fn test_timer_new() {
let timer = Aarch64Timer::new();
assert_eq!(timer.freq(), 0);
assert!(!timer.deadline_active);
}
#[test]
fn test_ns_to_ticks_zero_freq() {
assert_eq!(ns_to_ticks(1_000_000_000, 0), 0);
}
#[test]
fn test_ticks_to_ns_zero_freq() {
assert_eq!(ticks_to_ns(62_500_000, 0), 0);
}
#[test]
fn test_cancel_without_deadline_fails() {
let mut timer = Aarch64Timer::new();
assert!(timer.cancel_deadline().is_err());
}
}