axhal/platform/x86_pc/
time.rs

1use raw_cpuid::CpuId;
2
3#[cfg(feature = "irq")]
4use int_ratio::Ratio;
5
6#[cfg(feature = "irq")]
7const LAPIC_TICKS_PER_SEC: u64 = 1_000_000_000; // TODO: need to calibrate
8
9#[cfg(feature = "irq")]
10static mut NANOS_TO_LAPIC_TICKS_RATIO: Ratio = Ratio::zero();
11
12static mut INIT_TICK: u64 = 0;
13static mut CPU_FREQ_MHZ: u64 = axconfig::devices::TIMER_FREQUENCY as u64 / 1_000_000;
14
15/// RTC wall time offset in nanoseconds at monotonic time base.
16static mut RTC_EPOCHOFFSET_NANOS: u64 = 0;
17
18/// Returns the current clock time in hardware ticks.
19pub fn current_ticks() -> u64 {
20    unsafe { core::arch::x86_64::_rdtsc() - INIT_TICK }
21}
22
23/// Converts hardware ticks to nanoseconds.
24pub fn ticks_to_nanos(ticks: u64) -> u64 {
25    ticks * 1_000 / unsafe { CPU_FREQ_MHZ }
26}
27
28/// Converts nanoseconds to hardware ticks.
29pub fn nanos_to_ticks(nanos: u64) -> u64 {
30    nanos * unsafe { CPU_FREQ_MHZ } / 1_000
31}
32
33/// Return epoch offset in nanoseconds (wall time offset to monotonic clock start).
34pub fn epochoffset_nanos() -> u64 {
35    unsafe { RTC_EPOCHOFFSET_NANOS }
36}
37
38/// Set a one-shot timer.
39///
40/// A timer interrupt will be triggered at the specified monotonic time deadline (in nanoseconds).
41#[cfg(feature = "irq")]
42pub fn set_oneshot_timer(deadline_ns: u64) {
43    let lapic = super::apic::local_apic();
44    let now_ns = crate::time::monotonic_time_nanos();
45    unsafe {
46        if now_ns < deadline_ns {
47            let apic_ticks = NANOS_TO_LAPIC_TICKS_RATIO.mul_trunc(deadline_ns - now_ns);
48            assert!(apic_ticks <= u32::MAX as u64);
49            lapic.set_timer_initial(apic_ticks.max(1) as u32);
50        } else {
51            lapic.set_timer_initial(1);
52        }
53    }
54}
55
56pub(super) fn init_early() {
57    if let Some(freq) = CpuId::new()
58        .get_processor_frequency_info()
59        .map(|info| info.processor_base_frequency())
60        && freq > 0
61    {
62        axlog::ax_println!("Got TSC frequency by CPUID: {} MHz", freq);
63        unsafe { CPU_FREQ_MHZ = freq as u64 }
64    }
65
66    unsafe {
67        INIT_TICK = core::arch::x86_64::_rdtsc();
68    }
69
70    #[cfg(feature = "rtc")]
71    {
72        use x86_rtc::Rtc;
73
74        // Get the current time in microseconds since the epoch (1970-01-01) from the x86 RTC.
75        // Subtract the timer ticks to get the actual time when ArceOS was booted.
76        let eopch_time_nanos = Rtc::new().get_unix_timestamp() * 1_000_000_000;
77        unsafe {
78            RTC_EPOCHOFFSET_NANOS = eopch_time_nanos - ticks_to_nanos(INIT_TICK);
79        }
80    }
81}
82
83pub(super) fn init_primary() {
84    #[cfg(feature = "irq")]
85    unsafe {
86        use x2apic::lapic::{TimerDivide, TimerMode};
87        let lapic = super::apic::local_apic();
88        lapic.set_timer_mode(TimerMode::OneShot);
89        lapic.set_timer_divide(TimerDivide::Div256); // indeed it is Div1, the name is confusing.
90        lapic.enable_timer();
91
92        // TODO: calibrate with HPET
93        NANOS_TO_LAPIC_TICKS_RATIO = Ratio::new(
94            LAPIC_TICKS_PER_SEC as u32,
95            crate::time::NANOS_PER_SEC as u32,
96        );
97    }
98}
99
100#[cfg(feature = "smp")]
101pub(super) fn init_secondary() {
102    #[cfg(feature = "irq")]
103    unsafe {
104        super::apic::local_apic().enable_timer();
105    }
106}