diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 6e9f357..ca780b2 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -38,6 +38,7 @@ mod rtc; mod sched; mod selftest; mod sync; +mod time; mod x86_util; #[cfg(not(test))] @@ -81,6 +82,7 @@ pub extern "C" fn kinit(_mbinfop: *const multiboot::Info, boot_infop: *const han mm::init(mem_map.clone()); acpi::init(); interrupts::init(); + time::init(); rtc::init(); selftest::run_tests(); diff --git a/kernel/src/time/mod.rs b/kernel/src/time/mod.rs new file mode 100644 index 0000000..d8c48c0 --- /dev/null +++ b/kernel/src/time/mod.rs @@ -0,0 +1,15 @@ +mod pit; + +pub fn init() { + pit::init(); +} + +/// Represents a periodic tick generator. +trait TickSource { + /// Get an estimation of the ticks emitted per second. + fn approx_ticks_per_second(&self) -> u64; + // Set a function to be called for each tick. + fn set_tick_handler(&mut self, fn(u64)); + // Get the current number of elapsed ticks. + fn get_ticks(&self) -> u64; +} diff --git a/kernel/src/time/pit.rs b/kernel/src/time/pit.rs new file mode 100644 index 0000000..e2554c3 --- /dev/null +++ b/kernel/src/time/pit.rs @@ -0,0 +1,109 @@ +use spin::Mutex; + +use interrupts::set_irq_handler; +use x86_util::{inb, outb}; + +use super::TickSource; + +/// A `TickSource` adapter for the programmable interval timer. +pub struct Pit { + data: spin::Mutex, +} + +struct PitData { + ticks: u64, + handler: Option, +} + +impl TickSource for Pit { + fn approx_ticks_per_second(&self) -> u64 { + 1193182 / PIT_RELOAD_VALUE as u64 + } + + fn set_tick_handler(&mut self, tick_handler: fn(u64)) { + unsafe { + asm!("cli"); + } + + { + let mut data = self.data.lock(); + data.handler = Some(tick_handler); + } + + unsafe { + asm!("sti"); + } + } + + fn get_ticks(&self) -> u64 { + let ticks; + + unsafe { + asm!("cli"); + } + + { + let mut data = self.data.lock(); + ticks = data.ticks; + } + + unsafe { + asm!("sti"); + } + + ticks + } +} + +static PIT: Pit = Pit { + data: spin::Mutex::new(PitData { + ticks: 0, + handler: None, + }), +}; + +pub fn init() { + // The PIT IRQ is 0. Set our handler for that IRQ. + set_irq_handler(0, Some(timer_handler)); + + unsafe { + // Set channel 0 to lobyte/hibyte access mode and rate + // generator operating mode. + outb(0x43, 0b00_11_010_0); + // Output our desired reload value. + outb(0x40, PIT_RELOAD_VALUE as u8); + outb(0x40, (PIT_RELOAD_VALUE >> 8) as u8); + } +} + +fn timer_handler() { + let now_ticks; + let maybe_handler; + + { + unsafe { + asm!("cli"); + } + + let mut pit_data = PIT.data.lock(); + pit_data.ticks += 1; + + now_ticks = pit_data.ticks; + maybe_handler = pit_data.handler; + + unsafe { + asm!("sti"); + } + } + + match maybe_handler { + Some(handler) => handler(now_ticks), + None => (), + }; +} + +// The counter value at which we want the PIT to generate an +// interrupt. The interrupt frequency is +// 1193182 / `PIT_RELOAD_VALUE`. For an interrupt about every +// 10 microseconds, we use a value of 20. +const PIT_RELOAD_VALUE: u16 = 1193;