kernel/devices/clock/hardware_timer/
hpet.rs1use alloc::sync::Arc;
2use tracing::warn;
3
4use crate::{
5 acpi,
6 cpu::{
7 self,
8 idt::{InterruptAllSavedState, InterruptHandlerWithAllState},
9 interrupts::apic,
10 },
11 devices::clock::{hardware_timer::pit, ClockTime, FEMTOS_PER_SEC, NANOS_PER_FEMTO},
12 memory_management::virtual_space::VirtualSpace,
13 sync::{once::OnceLock, spin::mutex::Mutex},
14 utils::vcell::{RO, RW, WO},
15};
16
17use super::super::ClockDevice;
18
19static HPET_CLOCK: OnceLock<Arc<Mutex<Hpet>>> = OnceLock::new();
20
21pub fn init(hpet_table: &acpi::tables::Hpet) -> Arc<Mutex<Hpet>> {
22 cpu::cpu().push_cli();
25
26 if HPET_CLOCK.try_get().is_some() {
28 panic!("HPET already initialized");
29 }
30
31 let clock = HPET_CLOCK.get_or_init(|| {
32 let hpet = Hpet::new(hpet_table);
34 Arc::new(Mutex::new(hpet))
35 });
36
37 cpu::cpu().pop_cli();
38
39 clock.clone()
40}
41
42#[repr(C, packed(8))]
43struct HpetInterruptStatus {
44 status: RW<u32>,
45 reserved: u32,
46}
47
48impl HpetInterruptStatus {
49 fn set_interrupts_iter(&self) -> impl Iterator<Item = u8> {
50 let s = self.status.read();
51 (0..32).filter(move |bit| s & (1 << bit) != 0)
52 }
53
54 fn ack(&mut self, bit: u8) {
55 assert!(bit < 32);
56 unsafe { self.status.write(1 << bit) }
57 }
58}
59
60#[derive(Clone, Copy, Debug)]
61struct InterruptRouteCapabilityBitmap {
62 bitmap: u32,
63}
64
65impl InterruptRouteCapabilityBitmap {
66 fn is_set(&self, bit: u8) -> bool {
67 self.bitmap & (1 << bit) != 0
68 }
69
70 fn enabled_routes(&self) -> impl Iterator<Item = u8> {
71 let s = *self;
72 (0..32).filter(move |bit| s.is_set(*bit))
73 }
74}
75
76struct HpetTimerConfig {
77 is_interrupt_level_triggered: bool,
78 interrupt_enabled: bool,
79 is_periodic: bool,
80 is_periodic_capable: bool,
81 is_64bit_capable: bool,
82 timer_set_value: bool,
83 force_32bit_mode: bool,
84 interrupt_route: u8,
85 interrupt_via_fsb: bool,
86 fsb_capable: bool,
87 interrupt_route_capabilities: InterruptRouteCapabilityBitmap,
88}
89
90impl HpetTimerConfig {
91 fn new(data: u64) -> Self {
92 Self {
93 is_interrupt_level_triggered: data & (1 << 1) != 0,
94 interrupt_enabled: data & (1 << 2) != 0,
95 is_periodic: data & (1 << 3) != 0,
96 is_periodic_capable: data & (1 << 4) != 0,
97 is_64bit_capable: data & (1 << 5) != 0,
98 timer_set_value: data & (1 << 6) != 0,
99 force_32bit_mode: data & (1 << 7) != 0,
100 interrupt_route: ((data >> 9) & 0x1F) as u8,
101 interrupt_via_fsb: data & (1 << 14) != 0,
102 fsb_capable: data & (1 << 15) != 0,
103 interrupt_route_capabilities: InterruptRouteCapabilityBitmap {
104 bitmap: ((data >> 32) & 0xFFFFFFFF) as u32,
105 },
106 }
107 }
108
109 fn as_u64(&self) -> u64 {
110 let mut data = 0;
111 if self.is_interrupt_level_triggered {
112 data |= 1 << 1;
113 }
114 if self.interrupt_enabled {
115 data |= 1 << 2;
116 }
117 if self.is_periodic {
118 data |= 1 << 3;
119 }
120 if self.is_periodic_capable {
121 data |= 1 << 4;
122 }
123 if self.is_64bit_capable {
124 data |= 1 << 5;
125 }
126 if self.timer_set_value {
127 data |= 1 << 6;
128 }
129 if self.force_32bit_mode {
130 data |= 1 << 7;
131 }
132 data |= (self.interrupt_route as u64) << 9;
133 if self.interrupt_via_fsb {
134 data |= 1 << 14;
135 }
136 if self.fsb_capable {
137 data |= 1 << 15;
138 }
139 data |= (self.interrupt_route_capabilities.bitmap as u64) << 32;
140
141 data
142 }
143}
144
145#[repr(C, align(8))]
146struct HpetTimerMmio {
147 config_and_capabilities: RW<u64>,
148 comparator_value: WO<u64>,
149 fsb_interrupt_route: RO<u64>,
150 reserved: u64,
151}
152
153impl HpetTimerMmio {
154 fn config(&self) -> HpetTimerConfig {
155 HpetTimerConfig::new(self.config_and_capabilities.read())
156 }
157
158 fn set_config(&mut self, config: HpetTimerConfig) {
159 unsafe { self.config_and_capabilities.write(config.as_u64()) };
160 }
161
162 fn write_comparator_value(&mut self, value: u64) {
163 unsafe { self.comparator_value.write(value) };
164 }
165}
166
167#[repr(C, align(8))]
168struct HpetMmio {
169 general_capabilities_id: RO<u64>,
170 reserved0: u64,
171 general_configuration: RW<u64>,
172 reserved1: u64,
173 general_interrupt_status: HpetInterruptStatus,
174 reserved2: [u64; 25],
175 main_counter_value: RO<u64>,
176 reserved3: u64,
177 timers: [HpetTimerMmio; 3],
178}
179
180pub struct Hpet {
181 mmio: VirtualSpace<HpetMmio>,
182}
183
184impl Hpet {
185 fn new(hpet: &acpi::tables::Hpet) -> Self {
186 pit::disable();
187 assert_eq!(hpet.base_address.address_space_id, 0); let mmio = unsafe { VirtualSpace::new(hpet.base_address.address).unwrap() };
189
190 let mut s = Self { mmio };
192 let clock_period = s.counter_clock_period();
193
194 let timer = &mut s.mmio.timers[0];
196 let mut config = timer.config();
197 assert!(config.is_periodic_capable); assert!(config.is_64bit_capable); config.is_interrupt_level_triggered = false;
201 config.interrupt_enabled = true;
202 config.is_periodic = true; config.force_32bit_mode = false; config.interrupt_via_fsb = false; let available_routes = config.interrupt_route_capabilities.enabled_routes();
206
207 let mut first_available_route = None;
208 let mut above_15_route = None;
209 for route in available_routes {
214 if first_available_route.is_none() && !apic::is_irq_assigned(route) {
215 first_available_route = Some(route);
217 }
218 if above_15_route.is_none() && route > 15 {
219 above_15_route = Some(route);
220 }
221 if first_available_route.is_some() && above_15_route.is_some() {
222 break;
223 }
224 }
225
226 let chosen_route = above_15_route
227 .or(first_available_route)
228 .expect("No available HPET route");
229
230 config.interrupt_route = chosen_route;
231 config.timer_set_value = true; timer.set_config(config);
233 timer.write_comparator_value(FEMTOS_PER_SEC / clock_period);
234 timer.write_comparator_value(FEMTOS_PER_SEC / clock_period);
235
236 apic::assign_io_irq(
238 timer0_handler as InterruptHandlerWithAllState,
239 chosen_route,
240 cpu::cpu(),
241 );
242
243 s.set_enabled(true);
244 s.set_enable_legacy_replacement_route(false);
246
247 s
248 }
249
250 fn read_general_configuration(&self) -> u64 {
251 self.mmio.general_configuration.read()
252 }
253
254 fn write_general_configuration(&mut self, value: u64) {
255 unsafe { self.mmio.general_configuration.write(value) };
256 }
257
258 pub fn set_enabled(&mut self, enabled: bool) {
259 let mut config = self.read_general_configuration();
260 if enabled {
261 config |= 1;
262 } else {
263 config &= !1;
264 }
265 self.write_general_configuration(config);
266 }
267
268 pub fn set_enable_legacy_replacement_route(&mut self, enabled: bool) {
269 let mut config = self.read_general_configuration();
270 if enabled {
271 config |= 1 << 1;
272 } else {
273 config &= !(1 << 1);
274 }
275 self.write_general_configuration(config);
276 }
277
278 fn counter_clock_period(&self) -> u64 {
280 (self.mmio.general_capabilities_id.read() >> 32) & 0xFFFFFFFF
281 }
282
283 fn current_counter(&self) -> u64 {
284 self.mmio.main_counter_value.read()
286 }
287
288 fn status_interrupts_iter(&self) -> impl Iterator<Item = u8> {
289 self.mmio.general_interrupt_status.set_interrupts_iter()
290 }
291
292 fn ack_interrupt(&mut self, interrupt: u8) {
293 self.mmio.general_interrupt_status.ack(interrupt);
294 }
295}
296
297impl ClockDevice for Mutex<Hpet> {
298 fn name(&self) -> &'static str {
299 "HPET"
300 }
301
302 fn get_time(&self) -> ClockTime {
303 let clock = self.lock();
304 let counter = clock.current_counter();
305 let femtos_per_tick = clock.counter_clock_period();
306 let nanos_per_tick = femtos_per_tick / NANOS_PER_FEMTO;
307 let seconds_divider = FEMTOS_PER_SEC / femtos_per_tick;
308 let seconds = counter / seconds_divider;
309 let nanoseconds = (counter % seconds_divider) * nanos_per_tick;
310
311 ClockTime {
312 seconds,
313 nanoseconds,
314 }
315 }
316
317 fn granularity(&self) -> u64 {
318 let granularity = self.lock().counter_clock_period() / NANOS_PER_FEMTO;
319 if granularity == 0 {
320 1
321 } else {
322 granularity
323 }
324 }
325
326 fn require_calibration(&self) -> bool {
327 false
328 }
329
330 fn rating(&self) -> u64 {
331 50
332 }
333}
334
335extern "C" fn timer0_handler(_all_state: &mut InterruptAllSavedState) {
336 let mut clock = HPET_CLOCK.get().as_ref().lock();
337
338 if clock.mmio.timers[0].config().is_interrupt_level_triggered {
340 if let Some(interrupt) = clock.status_interrupts_iter().next() {
341 clock.ack_interrupt(interrupt);
343 } else {
344 warn!("Looks like we are getting PIT interrupt instead of HPET");
345 }
346 }
347
348 apic::return_from_interrupt();
349}