Skip to main content

islet_rmm/rec/
timer.rs

1use super::Rec;
2use crate::asm::isb;
3use crate::rmi::error::Error;
4use crate::rmi::rec::run::Run;
5
6use aarch64_cpu::registers::*;
7
8#[cfg(feature = "ns_state_save")]
9mod ns_timer {
10    use super::*;
11    use crate::config::NUM_OF_CPU;
12    use crate::cpu::get_cpu_id;
13    use crate::rec::context::TimerRegister;
14    use core::array::from_fn;
15    use lazy_static::lazy_static;
16    use spin::mutex::Mutex;
17
18    lazy_static! {
19        static ref NS_TIMER: [Mutex<TimerRegister>; NUM_OF_CPU] =
20            from_fn(|_| Mutex::new(TimerRegister::default()));
21    }
22
23    pub(super) fn restore() {
24        let ns_timer = NS_TIMER[get_cpu_id()].lock();
25        CNTVOFF_EL2.set(ns_timer.cntvoff_el2);
26        CNTPOFF_EL2.set(ns_timer.cntpoff_el2);
27        CNTV_CVAL_EL0.set(ns_timer.cntv_cval_el0);
28        CNTV_CTL_EL0.set(ns_timer.cntv_ctl_el0);
29        CNTP_CVAL_EL0.set(ns_timer.cntp_cval_el0);
30        CNTP_CTL_EL0.set(ns_timer.cntp_ctl_el0);
31        CNTHCTL_EL2.set(ns_timer.cnthctl_el2);
32    }
33
34    pub(super) fn save() {
35        let mut timer = NS_TIMER[get_cpu_id()].lock();
36        timer.cntvoff_el2 = CNTVOFF_EL2.get();
37        timer.cntv_cval_el0 = CNTV_CVAL_EL0.get();
38        timer.cntv_ctl_el0 = CNTV_CTL_EL0.get();
39        timer.cntpoff_el2 = CNTPOFF_EL2.get();
40        timer.cntp_cval_el0 = CNTP_CVAL_EL0.get();
41        timer.cntp_ctl_el0 = CNTP_CTL_EL0.get();
42        timer.cnthctl_el2 = CNTHCTL_EL2.get();
43    }
44}
45
46pub fn init_timer(rec: &mut Rec<'_>) {
47    let timer = &mut rec.context.timer;
48    timer.cnthctl_el2 = (CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET).into();
49}
50
51pub fn set_cnthctl(rec: &mut Rec<'_>, val: u64) {
52    let timer = &mut rec.context.timer;
53    timer.cnthctl_el2 = val;
54}
55
56#[cfg(not(fuzzing))]
57pub fn restore_state(rec: &Rec<'_>) {
58    let timer = &rec.context.timer;
59
60    CNTVOFF_EL2.set(timer.cntvoff_el2);
61    CNTPOFF_EL2.set(timer.cntpoff_el2);
62    CNTV_CVAL_EL0.set(timer.cntv_cval_el0);
63    CNTV_CTL_EL0.set(timer.cntv_ctl_el0);
64    CNTP_CVAL_EL0.set(timer.cntp_cval_el0);
65    CNTP_CTL_EL0.set(timer.cntp_ctl_el0);
66    CNTHCTL_EL2.set(timer.cnthctl_el2);
67}
68
69#[cfg(not(fuzzing))]
70pub fn save_state(rec: &mut Rec<'_>) {
71    let timer = &mut rec.context.timer;
72
73    timer.cntvoff_el2 = CNTVOFF_EL2.get();
74    timer.cntv_cval_el0 = CNTV_CVAL_EL0.get();
75    timer.cntv_ctl_el0 = CNTV_CTL_EL0.get();
76    timer.cntpoff_el2 = CNTPOFF_EL2.get();
77    timer.cntp_cval_el0 = CNTP_CVAL_EL0.get();
78    timer.cntp_ctl_el0 = CNTP_CTL_EL0.get();
79    timer.cnthctl_el2 = CNTHCTL_EL2.get();
80}
81
82pub fn send_state_to_host(rec: &Rec<'_>, run: &mut Run) -> Result<(), Error> {
83    let timer = &rec.context.timer;
84
85    run.set_cntv_ctl(timer.cntv_ctl_el0);
86    run.set_cntv_cval(timer.cntv_cval_el0 - timer.cntvoff_el2);
87    run.set_cntp_ctl(timer.cntp_ctl_el0);
88    run.set_cntp_cval(timer.cntp_cval_el0 - timer.cntpoff_el2);
89    Ok(())
90}
91
92pub fn save_host_state(_rec: &Rec<'_>) {
93    #[cfg(feature = "ns_state_save")]
94    ns_timer::save();
95}
96
97pub fn restore_host_state(_rec: &Rec<'_>) {
98    #[cfg(feature = "ns_state_save")]
99    ns_timer::restore();
100}
101
102// RMM spec A6.2 Realm timers, I<VRWGS>
103// On REC entry, for both the EL1 Virtual Timer and the EL1 Physical Timer,
104// if the EL1 timer asserts its output in the state described in the REC exit
105// structure from the previous REC exit then the RMM masks the hardware timer
106// signal before returning to the Realm.
107pub fn update_timer_assertion(rec: &mut Rec<'_>) {
108    const CNTHCTL_EL2_CNTVMASK: u64 = 0x1 << 18;
109    const CNTHCTL_EL2_CNTPMASK: u64 = 0x1 << 19;
110    let timer = &mut rec.context.timer;
111
112    // Get recently saved timer control registers
113    let cnthctl_old = timer.cnthctl_el2;
114
115    // Check if virtual timer is asserted
116    if CNTV_CTL_EL0.matches_all(
117        CNTV_CTL_EL0::ISTATUS::SET + CNTV_CTL_EL0::IMASK::CLEAR + CNTV_CTL_EL0::ENABLE::SET,
118    ) {
119        timer.cnthctl_el2 |= CNTHCTL_EL2_CNTVMASK;
120    } else {
121        timer.cnthctl_el2 &= !CNTHCTL_EL2_CNTVMASK; // Clear MASK
122    }
123
124    // Check if physical timer is asserted
125    if CNTP_CTL_EL0.matches_all(
126        CNTP_CTL_EL0::ISTATUS::SET + CNTP_CTL_EL0::IMASK::CLEAR + CNTP_CTL_EL0::ENABLE::SET,
127    ) {
128        timer.cnthctl_el2 |= CNTHCTL_EL2_CNTPMASK;
129    } else {
130        timer.cnthctl_el2 &= !CNTHCTL_EL2_CNTPMASK; // Clear MASK
131    }
132
133    // If cnthctl changed, write it back and ensure synchronization
134    if cnthctl_old != timer.cnthctl_el2 {
135        CNTHCTL_EL2.set(timer.cnthctl_el2);
136        isb();
137    }
138}