strat9_kernel/hardware/timer/
hpet.rs1#![allow(dead_code)]
8
9use crate::memory::{self, phys_to_virt};
10use core::{
11 ptr,
12 sync::atomic::{AtomicBool, Ordering},
13};
14use spin::Mutex;
15
16const HPET_GENERAL_CAP_ID: usize = 0x000;
17const HPET_GENERAL_CONFIG: usize = 0x010;
18const HPET_MAIN_COUNTER: usize = 0x0F0;
19
20const HPET_CAP_NUM_TIMERS_MASK: u64 = 0x0000_0000_0000_1F00;
21const HPET_CAP_COUNTER_SIZE: u64 = 1 << 13;
22const HPET_CAP_COUNTER_CLK_PERIOD_SHIFT: u32 = 32;
23
24const HPET_CONFIG_ENABLE: u64 = 0x0000_0000_0000_0001;
25
26const GAS_ADDR_SPACE_MEMORY: u8 = 0;
27
28const HPET_MMIO_MAP_SIZE: u64 = 0x1000;
30
31pub struct HpetInfo {
32 base_addr: u64,
33 mmio_base: usize,
34 num_timers: u8,
35 tick_period_fs: u32,
36 tick_period_ns: u32,
37 is_64bit: bool,
38}
39
40unsafe impl Send for HpetInfo {}
41unsafe impl Sync for HpetInfo {}
42
43static HPET_INFO: Mutex<Option<HpetInfo>> = Mutex::new(None);
44static HPET_INITIALIZED: AtomicBool = AtomicBool::new(false);
45
46unsafe fn hpet_read(mmio_base: usize, offset: usize) -> u64 {
48 core::ptr::read_volatile((mmio_base + offset) as *const u64)
49}
50
51unsafe fn hpet_write(mmio_base: usize, offset: usize, value: u64) {
53 core::ptr::write_volatile((mmio_base + offset) as *mut u64, value);
54}
55
56pub fn init() -> Result<(), &'static str> {
58 log::info!("[HPET] Searching for HPET...");
59
60 let hpet_acpi = crate::acpi::hpet::HpetAcpiTable::get().ok_or("HPET ACPI table not found")?;
61
62 let gas = unsafe { ptr::read_unaligned(core::ptr::addr_of!(hpet_acpi.gen_addr_struct)) };
64 let base_addr = gas.phys_addr;
65 let address_space = gas.address_space;
66 let comparator_desc =
67 unsafe { ptr::read_unaligned(core::ptr::addr_of!(hpet_acpi.comparator_descriptor)) };
68 let min_tick =
69 unsafe { ptr::read_unaligned(core::ptr::addr_of!(hpet_acpi.min_periodic_clock_tick)) };
70
71 log::info!(
72 "[HPET] Found HPET: base=0x{:x}, comparators={}, min_tick={}",
73 base_addr,
74 (comparator_desc >> 3) & 0x1F,
75 min_tick,
76 );
77
78 if address_space != GAS_ADDR_SPACE_MEMORY {
79 return Err("HPET: address space is not memory-mapped");
80 }
81 if base_addr == 0 || (base_addr & 0x7) != 0 {
82 return Err("HPET: base address invalid or misaligned");
83 }
84
85 memory::paging::ensure_identity_map_range(base_addr, HPET_MMIO_MAP_SIZE);
87
88 let mmio_base = phys_to_virt(base_addr) as usize;
89
90 unsafe {
91 let cap_id = hpet_read(mmio_base, HPET_GENERAL_CAP_ID);
92 let num_timers = ((cap_id & HPET_CAP_NUM_TIMERS_MASK) >> 8) as u8 + 1;
93 let is_64bit = (cap_id & HPET_CAP_COUNTER_SIZE) != 0;
94 let tick_period_fs = (cap_id >> HPET_CAP_COUNTER_CLK_PERIOD_SHIFT) as u32;
95 if tick_period_fs == 0 {
96 return Err("HPET counter period is zero");
97 }
98 if tick_period_fs > 1_000_000_000 {
99 return Err("HPET counter period out of range");
100 }
101 let tick_period_ns = core::cmp::max(1, tick_period_fs / 1_000_000);
102
103 let info = HpetInfo {
104 base_addr,
105 mmio_base,
106 num_timers,
107 tick_period_fs,
108 tick_period_ns,
109 is_64bit,
110 };
111
112 *HPET_INFO.lock() = Some(info);
113
114 let mut config = hpet_read(mmio_base, HPET_GENERAL_CONFIG);
115 config &= !0x02;
116 config |= HPET_CONFIG_ENABLE;
117 hpet_write(mmio_base, HPET_GENERAL_CONFIG, config);
118
119 HPET_INITIALIZED.store(true, Ordering::SeqCst);
120
121 log::info!(
122 "[HPET] Initialized: {} timers, {}-bit, {} ns/tick",
123 num_timers,
124 if is_64bit { 64 } else { 32 },
125 tick_period_ns,
126 );
127 }
128
129 Ok(())
130}
131
132pub fn read_counter() -> u64 {
134 let info = HPET_INFO.lock();
135 match *info {
136 Some(ref hpet) => unsafe { hpet_read(hpet.mmio_base, HPET_MAIN_COUNTER) },
137 None => 0,
138 }
139}
140
141pub fn tick_period_ns() -> u32 {
143 let info = HPET_INFO.lock();
144 match *info {
145 Some(ref hpet) => hpet.tick_period_ns,
146 None => 0,
147 }
148}
149
150pub fn frequency_hz() -> u64 {
152 let info = HPET_INFO.lock();
153 match *info {
154 Some(ref hpet) if hpet.tick_period_fs > 0 => {
155 1_000_000_000_000_000u64 / hpet.tick_period_fs as u64
156 }
157 _ => 0,
158 }
159}
160
161pub fn is_available() -> bool {
163 HPET_INITIALIZED.load(Ordering::Relaxed)
164}
165
166pub fn num_timers() -> u8 {
168 let info = HPET_INFO.lock();
169 match *info {
170 Some(ref hpet) => hpet.num_timers,
171 None => 0,
172 }
173}
174
175pub fn delay_us(us: u64) {
177 if !is_available() {
178 for _ in 0..(us * 100) {
180 core::hint::spin_loop();
181 }
182 return;
183 }
184
185 let period_ns = tick_period_ns() as u64;
186 if period_ns == 0 {
187 return;
188 }
189 let start = read_counter();
190 let ticks_needed = if us == 0 {
191 0
192 } else {
193 core::cmp::max(1, (us.saturating_mul(1000)) / period_ns)
194 };
195 while read_counter().wrapping_sub(start) < ticks_needed {
196 core::hint::spin_loop();
197 }
198}
199
200pub fn delay_ms(ms: u64) {
202 delay_us(ms * 1000);
203}
204
205pub fn uptime_ms() -> u64 {
207 if !is_available() {
208 return 0;
209 }
210
211 let counter = read_counter() as u128;
212 let period_ns = tick_period_ns() as u128;
213 ((counter.saturating_mul(period_ns)) / 1_000_000) as u64
214}
215
216pub fn uptime_secs() -> u64 {
218 uptime_ms() / 1000
219}