⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/arch/
timer.rs

1//! Architecture-independent timer interface.
2//!
3//! Provides the [`PlatformTimer`] trait for architecture-agnostic timer access
4//! and free functions that delegate to the current platform's implementation.
5//!
6//! # Architecture implementations
7//!
8//! * **x86_64**: [`X86_64Timer`] -- PIT-based tick counter and RDTSC timestamp.
9//! * **AArch64**: [`AArch64Timer`] -- Generic timer counter (CNTVCT_EL0 /
10//!   CNTFRQ_EL0).
11//! * **RISC-V**: [`RiscVTimer`] -- `rdtime` instruction via SBI timer.
12
13/// Platform timer abstraction trait.
14///
15/// Each architecture provides a zero-sized struct implementing this trait,
16/// exposing a common interface for tick counting, frequency queries, and
17/// hardware timer programming.
18pub trait PlatformTimer {
19    /// Read the current hardware tick/cycle counter.
20    ///
21    /// This is a monotonically increasing counter whose frequency is
22    /// given by [`ticks_per_second`](PlatformTimer::ticks_per_second).
23    fn current_ticks() -> u64;
24
25    /// Return the frequency of the tick counter in Hz.
26    ///
27    /// For x86_64 this is an approximate TSC frequency; for AArch64 the
28    /// architectural `CNTFRQ_EL0`; for RISC-V the timebase frequency.
29    fn ticks_per_second() -> u64;
30
31    /// Program the next timer interrupt to fire after `ticks` ticks from now.
32    fn set_timer(ticks: u64);
33}
34
35// ---------------------------------------------------------------------------
36// x86_64 implementation
37// ---------------------------------------------------------------------------
38
39#[cfg(target_arch = "x86_64")]
40pub struct X86_64Timer;
41
42#[cfg(target_arch = "x86_64")]
43impl PlatformTimer for X86_64Timer {
44    #[inline]
45    fn current_ticks() -> u64 {
46        // SAFETY: RDTSC is a non-privileged x86_64 instruction that reads the
47        // Time Stamp Counter. No side effects; always safe to call.
48        unsafe { core::arch::x86_64::_rdtsc() }
49    }
50
51    #[inline]
52    fn ticks_per_second() -> u64 {
53        // Approximate TSC frequency -- 2 GHz is a reasonable default for QEMU.
54        // A proper implementation would calibrate against the PIT or HPET.
55        2_000_000_000
56    }
57
58    fn set_timer(ticks: u64) {
59        // Convert ticks to PIT divisor.
60        // PIT frequency is 1.193182 MHz.
61        const PIT_FREQUENCY: u64 = 1_193_182;
62        let divisor = if ticks == 0 {
63            1u16
64        } else {
65            let d = PIT_FREQUENCY * ticks / Self::ticks_per_second();
66            if d == 0 {
67                1
68            } else if d > u16::MAX as u64 {
69                u16::MAX
70            } else {
71                d as u16
72            }
73        };
74
75        // SAFETY: I/O port writes to the 8254 PIT command (0x43) and channel 0
76        // data (0x40) registers. Standard PIT programming sequence.
77        unsafe {
78            use x86_64::instructions::port::Port;
79            let mut cmd: Port<u8> = Port::new(0x43);
80            let mut data: Port<u8> = Port::new(0x40);
81            cmd.write(0x36);
82            data.write((divisor & 0xFF) as u8);
83            data.write((divisor >> 8) as u8);
84        }
85    }
86}
87
88// ---------------------------------------------------------------------------
89// AArch64 implementation
90// ---------------------------------------------------------------------------
91
92#[cfg(target_arch = "aarch64")]
93pub struct AArch64Timer;
94
95#[cfg(target_arch = "aarch64")]
96impl PlatformTimer for AArch64Timer {
97    #[inline]
98    fn current_ticks() -> u64 {
99        let cnt: u64;
100        // SAFETY: Reading CNTVCT_EL0 (virtual timer count) is a non-privileged
101        // AArch64 operation with no side effects.
102        unsafe {
103            core::arch::asm!("mrs {}, CNTVCT_EL0", out(reg) cnt);
104        }
105        cnt
106    }
107
108    #[inline]
109    fn ticks_per_second() -> u64 {
110        let freq: u64;
111        // SAFETY: Reading CNTFRQ_EL0 returns the system counter frequency.
112        // Read-only, no side effects.
113        unsafe {
114            core::arch::asm!("mrs {}, CNTFRQ_EL0", out(reg) freq);
115        }
116        freq
117    }
118
119    fn set_timer(ticks: u64) {
120        // SAFETY: Writing CNTP_TVAL_EL0 sets the physical timer value;
121        // writing CNTP_CTL_EL0 enables the timer. Standard EL1 operations.
122        unsafe {
123            core::arch::asm!("msr CNTP_TVAL_EL0, {}", in(reg) ticks);
124            core::arch::asm!("msr CNTP_CTL_EL0, {}", in(reg) 1u64);
125        }
126    }
127}
128
129// ---------------------------------------------------------------------------
130// RISC-V implementation
131// ---------------------------------------------------------------------------
132
133#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
134pub struct RiscVTimer;
135
136#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
137impl PlatformTimer for RiscVTimer {
138    #[inline]
139    fn current_ticks() -> u64 {
140        let time: u64;
141        // SAFETY: `rdtime` reads the real-time counter CSR. Always accessible
142        // in supervisor mode, no side effects.
143        unsafe {
144            core::arch::asm!("rdtime {}", out(reg) time);
145        }
146        time
147    }
148
149    #[inline]
150    fn ticks_per_second() -> u64 {
151        // QEMU virt machine timebase frequency is 10 MHz.
152        10_000_000
153    }
154
155    fn set_timer(ticks: u64) {
156        let current = Self::current_ticks();
157        let _ = crate::arch::riscv::sbi::set_timer(current + ticks);
158    }
159}
160
161// ---------------------------------------------------------------------------
162// Free functions (delegate to the active platform)
163// ---------------------------------------------------------------------------
164
165/// Get current timer tick count (from the software tick counter maintained by
166/// each architecture's interrupt handler).
167pub fn get_ticks() -> u64 {
168    #[cfg(target_arch = "x86_64")]
169    {
170        crate::arch::x86_64::timer::get_ticks()
171    }
172
173    #[cfg(target_arch = "aarch64")]
174    {
175        crate::arch::aarch64::timer::get_ticks()
176    }
177
178    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
179    {
180        crate::arch::riscv::timer::get_ticks()
181    }
182}
183
184/// Read the hardware timestamp counter directly.
185///
186/// Unlike [`get_ticks`], which returns a software-maintained counter
187/// incremented on each timer interrupt, this reads the raw hardware
188/// cycle/time counter for high-resolution timing.
189pub fn read_hw_timestamp() -> u64 {
190    #[cfg(target_arch = "x86_64")]
191    {
192        X86_64Timer::current_ticks()
193    }
194
195    #[cfg(target_arch = "aarch64")]
196    {
197        AArch64Timer::current_ticks()
198    }
199
200    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
201    {
202        RiscVTimer::current_ticks()
203    }
204}
205
206/// Return the hardware timer frequency in Hz.
207pub fn hw_ticks_per_second() -> u64 {
208    #[cfg(target_arch = "x86_64")]
209    {
210        X86_64Timer::ticks_per_second()
211    }
212
213    #[cfg(target_arch = "aarch64")]
214    {
215        AArch64Timer::ticks_per_second()
216    }
217
218    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
219    {
220        RiscVTimer::ticks_per_second()
221    }
222}
223
224/// Get the current timestamp in seconds since boot.
225///
226/// Computed by dividing the raw hardware tick counter by the
227/// platform-specific tick frequency.
228pub fn get_timestamp_secs() -> u64 {
229    let tps = hw_ticks_per_second();
230    if tps == 0 {
231        return 0;
232    }
233    read_hw_timestamp() / tps
234}
235
236/// Get the current timestamp in milliseconds since boot.
237pub fn get_timestamp_ms() -> u64 {
238    let tps = hw_ticks_per_second();
239    if tps == 0 {
240        return 0;
241    }
242    // Avoid overflow: (ticks * 1000) / tps  =>  ticks / (tps / 1000) when tps >=
243    // 1000
244    let ticks = read_hw_timestamp();
245    if tps >= 1000 {
246        ticks / (tps / 1000)
247    } else {
248        ticks * (1000 / tps)
249    }
250}
251
252/// Program the next timer interrupt.
253pub fn set_hw_timer(ticks: u64) {
254    #[cfg(target_arch = "x86_64")]
255    {
256        X86_64Timer::set_timer(ticks);
257    }
258
259    #[cfg(target_arch = "aarch64")]
260    {
261        AArch64Timer::set_timer(ticks);
262    }
263
264    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
265    {
266        RiscVTimer::set_timer(ticks);
267    }
268}