1use core::time::Duration;
2
3use crate::ArchTrait;
4
5const NANOS_PER_SEC: u64 = 1_000_000_000;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub enum CounterStability {
10 Stable,
12 Unstable,
14}
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17#[repr(u8)]
18pub enum ArchTimerMode {
19 El1Phys = 0,
20 El1Virt = 1,
21 El2HypPhys = 2,
22}
23
24impl ArchTimerMode {
25 pub const fn from_raw(raw: u8) -> Self {
26 match raw {
27 1 => Self::El1Virt,
28 2 => Self::El2HypPhys,
29 _ => Self::El1Phys,
30 }
31 }
32}
33
34static mut ARCH_TIMER_MODE: u8 = ArchTimerMode::El1Phys as u8;
35
36pub const fn select_aarch64_timer_mode(kernel_in_el2: bool, el2_available: bool) -> ArchTimerMode {
37 if kernel_in_el2 {
38 ArchTimerMode::El2HypPhys
39 } else if el2_available {
40 ArchTimerMode::El1Phys
41 } else {
42 ArchTimerMode::El1Virt
43 }
44}
45
46pub const fn aarch64_timer_irq_index(mode: ArchTimerMode) -> usize {
47 match mode {
48 ArchTimerMode::El1Phys => 1,
49 ArchTimerMode::El1Virt => 2,
50 ArchTimerMode::El2HypPhys => 3,
51 }
52}
53
54pub fn set_aarch64_timer_mode(mode: ArchTimerMode) {
55 unsafe { ARCH_TIMER_MODE = mode as u8 };
57}
58
59pub fn aarch64_timer_mode() -> ArchTimerMode {
60 unsafe { ArchTimerMode::from_raw(ARCH_TIMER_MODE) }
62}
63
64#[cfg(any(target_arch = "aarch64", test))]
65pub(crate) mod aarch64_deadline {
66 pub(crate) const fn from_interval(current_ticks: u64, interval_ticks: u64) -> u64 {
71 current_ticks.wrapping_add(interval_ticks)
72 }
73
74 #[cfg(any(not(feature = "hv"), test))]
75 pub(crate) mod el1 {
76 use super::{super::ArchTimerMode, from_interval};
77
78 pub(crate) trait TimerRegisters {
79 fn read_virtual_counter(&self) -> u64;
80 fn read_physical_counter(&self) -> u64;
81 fn write_virtual_compare(&self, deadline: u64);
82 fn write_physical_compare(&self, deadline: u64);
83 }
84
85 pub(crate) fn program(
86 registers: &impl TimerRegisters,
87 mode: ArchTimerMode,
88 interval_ticks: u64,
89 ) {
90 match mode {
91 ArchTimerMode::El1Virt => registers.write_virtual_compare(from_interval(
92 registers.read_virtual_counter(),
93 interval_ticks,
94 )),
95 ArchTimerMode::El1Phys | ArchTimerMode::El2HypPhys => registers
96 .write_physical_compare(from_interval(
97 registers.read_physical_counter(),
98 interval_ticks,
99 )),
100 }
101 }
102 }
103
104 #[cfg(any(feature = "hv", test))]
105 pub(crate) mod el2 {
106 use super::from_interval;
107
108 pub(crate) trait TimerRegisters {
109 fn read_physical_counter(&self) -> u64;
110 fn write_hyp_physical_compare(&self, deadline: u64);
111 }
112
113 pub(crate) fn program(registers: &impl TimerRegisters, interval_ticks: u64) {
114 registers.write_hyp_physical_compare(from_interval(
115 registers.read_physical_counter(),
116 interval_ticks,
117 ));
118 }
119 }
120}
121
122#[cfg(any(target_arch = "riscv64", test))]
123pub(crate) mod riscv64_interval {
124 pub(crate) fn absolute_deadline(current_ticks: u64, interval_ticks: u64) -> u64 {
126 let interval_ticks = if interval_ticks == 0 {
127 1
128 } else {
129 interval_ticks
130 };
131 current_ticks.saturating_add(interval_ticks)
132 }
133}
134
135#[cfg(any(target_arch = "loongarch64", test))]
136pub(crate) mod loongarch64_interval {
137 const ALIGNMENT: usize = 4;
138 const MIN_TICKS: usize = 4;
139
140 pub(crate) fn aligned_ticks(interval_ticks: usize) -> usize {
142 let max_aligned = usize::MAX - usize::MAX % ALIGNMENT;
143 let clamped = interval_ticks.max(MIN_TICKS).min(max_aligned);
144 (clamped + (ALIGNMENT - 1)) & !(ALIGNMENT - 1)
145 }
146}
147
148pub fn since_boot() -> Duration {
149 elapsed()
150}
151
152#[inline]
154pub fn freq() -> usize {
155 crate::arch::Arch::systimer_freq()
156}
157
158#[inline]
160pub fn ticks() -> usize {
161 crate::arch::Arch::systimer_tick()
162}
163
164#[inline]
166pub fn scheduler_clock_stability() -> CounterStability {
167 crate::arch::Arch::systimer_stability()
168}
169
170#[inline]
172pub fn ticks_to_duration(ticks: usize) -> Duration {
173 let freq = freq();
174 if freq == 0 {
175 return Duration::ZERO;
176 }
177 let nanos = (ticks as u128 * NANOS_PER_SEC as u128) / freq as u128;
180 Duration::from_nanos(nanos as u64)
181}
182
183#[inline]
185pub fn duration_to_ticks(duration: Duration) -> usize {
186 let freq = freq();
187 if freq == 0 {
188 return 0;
189 }
190 let ticks = (duration.as_nanos() * freq as u128) / NANOS_PER_SEC as u128;
193 ticks as _
194}
195
196#[inline]
198pub fn elapsed() -> Duration {
199 ticks_to_duration(ticks())
200}
201
202#[cfg(test)]
203mod tests {
204 use core::cell::Cell;
205
206 use super::{
207 aarch64_deadline::{
208 self,
209 el1::{self, TimerRegisters as El1TimerRegisters},
210 el2::{self, TimerRegisters as El2TimerRegisters},
211 },
212 *,
213 };
214
215 #[test]
216 fn el2_kernel_uses_hyp_physical_timer() {
217 assert_eq!(
218 select_aarch64_timer_mode(true, true),
219 ArchTimerMode::El2HypPhys
220 );
221 assert_eq!(
222 select_aarch64_timer_mode(true, false),
223 ArchTimerMode::El2HypPhys
224 );
225 }
226
227 #[test]
228 fn el1_kernel_uses_physical_timer_when_el2_is_available() {
229 assert_eq!(
230 select_aarch64_timer_mode(false, true),
231 ArchTimerMode::El1Phys
232 );
233 }
234
235 #[test]
236 fn el1_kernel_uses_virtual_timer_when_el2_is_unavailable() {
237 assert_eq!(
238 select_aarch64_timer_mode(false, false),
239 ArchTimerMode::El1Virt
240 );
241 }
242
243 #[test]
244 fn timer_mode_maps_to_fdt_interrupt_index() {
245 assert_eq!(aarch64_timer_irq_index(ArchTimerMode::El1Phys), 1);
246 assert_eq!(aarch64_timer_irq_index(ArchTimerMode::El1Virt), 2);
247 assert_eq!(aarch64_timer_irq_index(ArchTimerMode::El2HypPhys), 3);
248 }
249
250 #[test]
251 fn compare_value_preserves_intervals_beyond_tval_width() {
252 let current = 0x1234_5678_0000_0000;
253 let interval = u32::MAX as u64 + 17;
254
255 assert_eq!(
256 aarch64_deadline::from_interval(current, interval),
257 current + interval
258 );
259 assert_eq!(aarch64_deadline::from_interval(u64::MAX - 3, 8), 4);
260 }
261
262 #[test]
263 fn riscv64_deadline_saturates_at_counter_limit() {
264 assert_eq!(
265 riscv64_interval::absolute_deadline(u64::MAX - 3, 8),
266 u64::MAX
267 );
268 assert_eq!(riscv64_interval::absolute_deadline(10, 0), 11);
269 assert_eq!(riscv64_interval::absolute_deadline(u64::MAX, 0), u64::MAX);
270 }
271
272 #[test]
273 fn loongarch64_interval_clamps_before_rounding() {
274 assert_eq!(loongarch64_interval::aligned_ticks(1), 4);
275 assert_eq!(loongarch64_interval::aligned_ticks(5), 8);
276 assert_eq!(
277 loongarch64_interval::aligned_ticks(usize::MAX),
278 usize::MAX & !3
279 );
280 }
281
282 #[test]
283 fn el1_virtual_timer_uses_virtual_counter_and_compare_register() {
284 let registers = RecordingEl1TimerRegisters::new(0x1234_5678_0000_0000, 17);
285 let interval = u32::MAX as u64 + 17;
286
287 el1::program(®isters, ArchTimerMode::El1Virt, interval);
288
289 assert_eq!(registers.virtual_compare.get(), Some(0x1234_5679_0000_0010));
290 assert_eq!(registers.physical_compare.get(), None);
291 assert_eq!(registers.virtual_counter_reads.get(), 1);
292 assert_eq!(registers.physical_counter_reads.get(), 0);
293 }
294
295 #[test]
296 fn el1_physical_timer_uses_physical_counter_and_compare_register() {
297 let registers = RecordingEl1TimerRegisters::new(17, u64::MAX - 3);
298
299 el1::program(®isters, ArchTimerMode::El1Phys, 8);
300
301 assert_eq!(registers.virtual_compare.get(), None);
302 assert_eq!(registers.physical_compare.get(), Some(4));
303 assert_eq!(registers.virtual_counter_reads.get(), 0);
304 assert_eq!(registers.physical_counter_reads.get(), 1);
305 }
306
307 #[test]
308 fn el2_hyp_timer_uses_physical_counter_and_hyp_compare_register() {
309 let registers = RecordingEl2TimerRegisters::new(u64::MAX - 3);
310
311 el2::program(®isters, 8);
312
313 assert_eq!(registers.hyp_physical_compare.get(), Some(4));
314 assert_eq!(registers.physical_counter_reads.get(), 1);
315 }
316
317 struct RecordingEl1TimerRegisters {
318 virtual_counter: u64,
319 physical_counter: u64,
320 virtual_counter_reads: Cell<usize>,
321 physical_counter_reads: Cell<usize>,
322 virtual_compare: Cell<Option<u64>>,
323 physical_compare: Cell<Option<u64>>,
324 }
325
326 impl RecordingEl1TimerRegisters {
327 fn new(virtual_counter: u64, physical_counter: u64) -> Self {
328 Self {
329 virtual_counter,
330 physical_counter,
331 virtual_counter_reads: Cell::new(0),
332 physical_counter_reads: Cell::new(0),
333 virtual_compare: Cell::new(None),
334 physical_compare: Cell::new(None),
335 }
336 }
337 }
338
339 impl El1TimerRegisters for RecordingEl1TimerRegisters {
340 fn read_virtual_counter(&self) -> u64 {
341 self.virtual_counter_reads
342 .set(self.virtual_counter_reads.get() + 1);
343 self.virtual_counter
344 }
345
346 fn read_physical_counter(&self) -> u64 {
347 self.physical_counter_reads
348 .set(self.physical_counter_reads.get() + 1);
349 self.physical_counter
350 }
351
352 fn write_virtual_compare(&self, deadline: u64) {
353 self.virtual_compare.set(Some(deadline));
354 }
355
356 fn write_physical_compare(&self, deadline: u64) {
357 self.physical_compare.set(Some(deadline));
358 }
359 }
360
361 struct RecordingEl2TimerRegisters {
362 physical_counter: u64,
363 physical_counter_reads: Cell<usize>,
364 hyp_physical_compare: Cell<Option<u64>>,
365 }
366
367 impl RecordingEl2TimerRegisters {
368 fn new(physical_counter: u64) -> Self {
369 Self {
370 physical_counter,
371 physical_counter_reads: Cell::new(0),
372 hyp_physical_compare: Cell::new(None),
373 }
374 }
375 }
376
377 impl El2TimerRegisters for RecordingEl2TimerRegisters {
378 fn read_physical_counter(&self) -> u64 {
379 self.physical_counter_reads
380 .set(self.physical_counter_reads.get() + 1);
381 self.physical_counter
382 }
383
384 fn write_hyp_physical_compare(&self, deadline: u64) {
385 self.hyp_physical_compare.set(Some(deadline));
386 }
387 }
388}