Skip to main content

ax_cpu/arch/aarch64/
timer.rs

1//! CPU generic-timer counters and comparator banks.
2
3use core::{arch::asm, marker::PhantomData};
4
5macro_rules! read_register {
6    ($name:literal) => {{
7        let value: u64;
8        // SAFETY: the caller has access to this architectural register bank.
9        unsafe { asm!(concat!("mrs {}, ", $name), out(reg) value, options(nomem, nostack)); }
10        value
11    }};
12}
13
14macro_rules! write_register {
15    ($name:literal, $value:expr) => {{
16        // SAFETY: the timer session owns the current CPU's comparator bank.
17        unsafe { asm!(concat!("msr ", $name, ", {}"), in(reg) $value, options(nostack)); }
18    }};
19}
20
21/// Reads the frequency advertised by CNTFRQ_EL0, in Hz.
22/// Platform calibration and validation of this value remain with the caller.
23pub fn counter_frequency() -> u64 {
24    read_register!("CNTFRQ_EL0")
25}
26
27/// Reads the physical system counter after instruction synchronization.
28pub fn physical_counter() -> u64 {
29    synchronize();
30    read_register!("CNTPCT_EL0")
31}
32
33/// Reads the virtual system counter, including the installed virtual offset.
34pub fn virtual_counter() -> u64 {
35    synchronize();
36    read_register!("CNTVCT_EL0")
37}
38
39/// A hardware comparator bank; selecting a bank does not select an IRQ route.
40#[derive(Clone, Copy, Debug, Eq, PartialEq)]
41pub enum TimerKind {
42    /// Non-secure physical timer, CNTP_*_EL0.
43    Physical,
44    /// Virtual timer, CNTV_*_EL0.
45    Virtual,
46    /// Non-VHE hypervisor physical timer, CNTHP_*_EL2.
47    HypervisorPhysical,
48}
49
50bitflags::bitflags! {
51    /// Generic timer control and read-only condition flags.
52    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
53    pub struct TimerControl: u64 {
54        /// Enables the comparator.
55        const ENABLE = 1;
56        /// Masks the timer's interrupt output without stopping the counter.
57        const MASKED = 1 << 1;
58        /// Read-only comparator condition; meaningful while enabled.
59        const PENDING = 1 << 2;
60    }
61}
62
63/// Exclusive, non-migrating access to one CPU timer comparator.
64/// Dropping this view does not change timer or interrupt state.
65#[derive(Debug)]
66pub struct Timer {
67    kind: TimerKind,
68    _not_send_sync: PhantomData<*mut ()>,
69}
70
71impl Timer {
72    /// Borrows a comparator without changing its registers.
73    ///
74    /// # Safety
75    /// The selected bank must be accessible at the current exception level.
76    /// The caller must keep this CPU pinned and exclude conflicting accesses,
77    /// including timer IRQ and guest entry/exit, for the returned view's life.
78    pub unsafe fn current(kind: TimerKind) -> Self {
79        Self {
80            kind,
81            _not_send_sync: PhantomData,
82        }
83    }
84
85    /// Reads the counter used by this comparator.
86    pub fn counter(&self) -> u64 {
87        match self.kind {
88            TimerKind::Virtual => virtual_counter(),
89            TimerKind::Physical | TimerKind::HypervisorPhysical => physical_counter(),
90        }
91    }
92
93    /// Reads the absolute comparator value, in the selected counter's ticks.
94    pub fn compare(&self) -> u64 {
95        match self.kind {
96            TimerKind::Physical => read_register!("CNTP_CVAL_EL0"),
97            TimerKind::Virtual => read_register!("CNTV_CVAL_EL0"),
98            TimerKind::HypervisorPhysical => read_register!("CNTHP_CVAL_EL2"),
99        }
100    }
101
102    /// Writes an absolute comparator value without changing ENABLE or IMASK.
103    /// This does not clamp expired deadlines or choose scheduling policy.
104    pub fn set_compare(&mut self, ticks: u64) {
105        match self.kind {
106            TimerKind::Physical => write_register!("CNTP_CVAL_EL0", ticks),
107            TimerKind::Virtual => write_register!("CNTV_CVAL_EL0", ticks),
108            TimerKind::HypervisorPhysical => write_register!("CNTHP_CVAL_EL2", ticks),
109        }
110        synchronize();
111    }
112
113    /// Reads ENABLE, IMASK and the read-only comparator condition.
114    pub fn control(&self) -> TimerControl {
115        let value = match self.kind {
116            TimerKind::Physical => read_register!("CNTP_CTL_EL0"),
117            TimerKind::Virtual => read_register!("CNTV_CTL_EL0"),
118            TimerKind::HypervisorPhysical => read_register!("CNTHP_CTL_EL2"),
119        };
120        TimerControl::from_bits_truncate(value)
121    }
122
123    /// Replaces ENABLE and IMASK, ignoring the read-only PENDING flag.
124    /// The update is synchronized before returning; the IRQ controller remains
125    /// owned by the platform and is neither acknowledged nor reconfigured.
126    pub fn set_control(&mut self, control: TimerControl) {
127        let value = (control & (TimerControl::ENABLE | TimerControl::MASKED)).bits();
128        match self.kind {
129            TimerKind::Physical => write_register!("CNTP_CTL_EL0", value),
130            TimerKind::Virtual => write_register!("CNTV_CTL_EL0", value),
131            TimerKind::HypervisorPhysical => write_register!("CNTHP_CTL_EL2", value),
132        }
133        synchronize();
134    }
135}
136
137fn synchronize() {
138    // SAFETY: ISB synchronizes architectural state without accessing memory.
139    unsafe {
140        asm!("isb", options(nostack));
141    }
142}