good_os_framework/drivers/
hpet.rs

1use core::sync::atomic::{AtomicBool, Ordering};
2use core::{cell::UnsafeCell, ptr};
3use x86_64::PhysAddr;
4
5use crate::arch::acpi::ACPI;
6use crate::memory::convert_physical_to_virtual;
7
8pub static HPET: Hpet = Hpet::uninit();
9pub static HPET_INIT: AtomicBool = AtomicBool::new(false);
10
11pub fn init() {
12    let acpi = ACPI.try_get().unwrap();
13    let physical_address = PhysAddr::new(acpi.hpet_info.base_address as u64);
14    let virtual_address = convert_physical_to_virtual(physical_address);
15
16    HPET.init(virtual_address.as_u64());
17    HPET.enable_counter();
18
19    log::debug!("HPET clock speed: {} femto seconds", HPET.clock_speed());
20    log::debug!("HPET timers: {} available", HPET.timers_count());
21
22    HPET_INIT.store(true, Ordering::SeqCst);
23}
24
25pub struct Hpet {
26    base_addr: UnsafeCell<u64>,
27}
28
29impl Hpet {
30    #[inline]
31    pub const fn uninit() -> Self {
32        Hpet {
33            base_addr: UnsafeCell::new(0),
34        }
35    }
36
37    pub fn init(&self, base_addr: u64) {
38        unsafe {
39            self.base_addr.get().write(base_addr);
40        }
41    }
42
43    /// Gets the clock speed of the HPET.
44    pub fn clock_speed(&self) -> u32 {
45        unsafe {
46            let base_addr = *self.base_addr.get();
47            let value = ptr::read_volatile(base_addr as *const u64);
48            (value >> 32) as u32
49        }
50    }
51
52    /// Gets the timers count of the HPET.
53    pub fn timers_count(&self) -> u32 {
54        unsafe {
55            let base_addr = *self.base_addr.get();
56            let value = ptr::read_volatile(base_addr as *const u64);
57            (((value >> 8) & 0b11111) + 1) as u32
58        }
59    }
60
61    /// Enable the HPET counter.
62    pub fn enable_counter(&self) {
63        unsafe {
64            let configuration_addr = *self.base_addr.get() + 0x10;
65            let old = ptr::read_volatile(configuration_addr as *const u64);
66            ptr::write_volatile(configuration_addr as *mut u64, old | 1);
67        }
68    }
69
70    /// Read the current value of the HPET counter.
71    pub fn get_counter(&self) -> u64 {
72        unsafe {
73            let counter_l_addr = *self.base_addr.get() + 0xf0;
74            let counter_h_addr = *self.base_addr.get() + 0xf4;
75            loop {
76                let high1 = ptr::read_volatile(counter_h_addr as *const u32);
77                let low = ptr::read_volatile(counter_l_addr as *const u32);
78                let high2 = ptr::read_volatile(counter_h_addr as *const u32);
79                if high1 == high2 {
80                    return (high1 as u64) << 32 | low as u64;
81                }
82            }
83        }
84    }
85
86    /// Get the time
87    #[inline]
88    pub fn get_time_elapsed(&self) -> u64 {
89        self.get_counter() * (self.clock_speed() as u64 / 1_000_000)
90    }
91}
92
93unsafe impl Sync for Hpet {}