kernel/devices/clock/hardware_timer/
hpet.rs

1use 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    // make sure we don't get interrupted before `HPET_CLOCK`
23    // is initialized
24    cpu::cpu().push_cli();
25
26    // just to make sure that we don't initialize it twice
27    if HPET_CLOCK.try_get().is_some() {
28        panic!("HPET already initialized");
29    }
30
31    let clock = HPET_CLOCK.get_or_init(|| {
32        // only executed once
33        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); // memory space
188        let mmio = unsafe { VirtualSpace::new(hpet.base_address.address).unwrap() };
189
190        // enable the timer
191        let mut s = Self { mmio };
192        let clock_period = s.counter_clock_period();
193
194        // setup interrupts for the first timer only for now
195        let timer = &mut s.mmio.timers[0];
196        let mut config = timer.config();
197        assert!(config.is_periodic_capable); // must be periodic capable
198        assert!(config.is_64bit_capable); // must be 64-bit capable
199
200        config.is_interrupt_level_triggered = false;
201        config.interrupt_enabled = true;
202        config.is_periodic = true; // periodic
203        config.force_32bit_mode = false; // don't force 32-bit mode
204        config.interrupt_via_fsb = false; // don't use FSB
205        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        // check if we have available routes that are higher than 15, which
210        // is the range of legacy ISA interrupts.
211        // if we have any above those, it's best to use them
212        // otherwise, we will use the first available route
213        for route in available_routes {
214            if first_available_route.is_none() && !apic::is_irq_assigned(route) {
215                // we can use this route
216                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; // write the timer value
232        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        // setup ioapic
237        apic::assign_io_irq(
238            timer0_handler as InterruptHandlerWithAllState,
239            chosen_route,
240            cpu::cpu(),
241        );
242
243        s.set_enabled(true);
244        // use normal routing
245        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    /// Returns the number of femtoseconds per counter tick
279    fn counter_clock_period(&self) -> u64 {
280        (self.mmio.general_capabilities_id.read() >> 32) & 0xFFFFFFFF
281    }
282
283    fn current_counter(&self) -> u64 {
284        // Safety: we know that the counter is 64-bit, aligned, valid pointer
285        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 we are level triggered, we must clear the interrupt bit
339    if clock.mmio.timers[0].config().is_interrupt_level_triggered {
340        if let Some(interrupt) = clock.status_interrupts_iter().next() {
341            // clear the interrupt (must for level triggered interrupts)
342            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}