diff --git a/src/kernel/clock.rs b/src/kernel/clock.rs index 13d8f234e..8d0511a9f 100644 --- a/src/kernel/clock.rs +++ b/src/kernel/clock.rs @@ -7,21 +7,24 @@ const DAYS_BEFORE_MONTH: [u64; 13] = [ // NOTE: This clock is monotonic pub fn uptime() -> f64 { - 1.0 / (1.193182 * 1000000.0 / 65536.0) * kernel::time::ticks() as f64 + kernel::time::time_between_ticks() * kernel::time::ticks() as f64 } // NOTE: This clock is not monotonic pub fn realtime() -> f64 { let rtc = CMOS::new().rtc(); // Assuming GMT - let t = 86400 * days_before_year(rtc.year as u64) - + 86400 * days_before_month(rtc.year as u64, rtc.month as u64) - + 86400 * (rtc.day - 1) as u64 - + 3600 * rtc.hour as u64 - + 60 * rtc.minute as u64 - + rtc.second as u64; + let timestamp = 86400 * days_before_year(rtc.year as u64) + + 86400 * days_before_month(rtc.year as u64, rtc.month as u64) + + 86400 * (rtc.day - 1) as u64 + + 3600 * rtc.hour as u64 + + 60 * rtc.minute as u64 + + rtc.second as u64; - t as f64 + let fract = kernel::time::time_between_ticks() + * (kernel::time::ticks() - kernel::time::last_rtc_update()) as f64; + + (timestamp as f64) + fract } fn days_before_year(year: u64) -> u64 { diff --git a/src/kernel/cmos.rs b/src/kernel/cmos.rs index bf57ba067..afa26765c 100644 --- a/src/kernel/cmos.rs +++ b/src/kernel/cmos.rs @@ -1,5 +1,6 @@ use crate::print; use x86_64::instructions::port::Port; +use x86_64::instructions::interrupts; #[repr(u8)] enum Register { @@ -9,7 +10,16 @@ enum Register { Day = 0x07, Month = 0x08, Year = 0x09, + A = 0x0A, B = 0x0B, + C = 0x0C, +} + +#[repr(u8)] +enum Interrupt { + Periodic = 1 << 6, + Alarm = 1 << 5, + Update = 1 << 4, } #[derive(Debug)] @@ -61,6 +71,55 @@ impl CMOS { RTC { year, month, day, hour, minute, second } } + pub fn enable_periodic_interrupt(&mut self) { + self.enable_interrupt(Interrupt::Periodic); + } + + pub fn enable_alarm_interrupt(&mut self) { + self.enable_interrupt(Interrupt::Alarm); + } + + pub fn enable_update_interrupt(&mut self) { + self.enable_interrupt(Interrupt::Update); + } + + /// Rate must be between 3 and 15 + /// Resulting in the following frequency: 32768 >> (rate - 1) + pub fn set_periodic_interrupt_rate(&mut self, rate: u8) { + interrupts::without_interrupts(|| { + self.disable_nmi(); + unsafe { + self.addr.write(Register::A as u8); + let prev = self.data.read(); + self.addr.write(Register::A as u8); + self.data.write((prev & 0xF0) | rate); + } + self.enable_nmi(); + self.notify_end_of_interrupt(); + }); + } + + fn enable_interrupt(&mut self, interrupt: Interrupt) { + interrupts::without_interrupts(|| { + self.disable_nmi(); + unsafe { + self.addr.write(Register::B as u8); + let prev = self.data.read(); + self.addr.write(Register::B as u8); + self.data.write(prev | interrupt as u8); + } + self.enable_nmi(); + self.notify_end_of_interrupt(); + }); + } + + pub fn notify_end_of_interrupt(&mut self) { + unsafe { + self.addr.write(Register::C as u8); + self.data.read(); + } + } + fn is_updating(&mut self) -> bool { unsafe { self.addr.write(0x0A as u8); @@ -74,4 +133,18 @@ impl CMOS { self.data.read() } } + + fn enable_nmi(&mut self) { + unsafe { + let prev = self.addr.read(); + self.addr.write(prev & 0x7F); + } + } + + fn disable_nmi(&mut self) { + unsafe { + let prev = self.addr.read(); + self.addr.write(prev | 0x80); + } + } } diff --git a/src/kernel/idt.rs b/src/kernel/idt.rs index 526cb5f89..15bd23017 100644 --- a/src/kernel/idt.rs +++ b/src/kernel/idt.rs @@ -3,6 +3,10 @@ use lazy_static::lazy_static; use spin::Mutex; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use x86_64::instructions::interrupts; +use x86_64::instructions::port::Port; + +const PIC1: u16 = 0x21; +const PIC2: u16 = 0xA1; // Translate IRQ into system interrupt fn interrupt_index(irq: u8) -> u8 { @@ -73,8 +77,6 @@ pub fn init() { IDT.load(); } -use x86_64::instructions::port::Port; - pub fn set_irq_handler(irq: u8, handler: fn()) { interrupts::without_interrupts(|| { let mut handlers = IRQ_HANDLERS.lock(); @@ -85,17 +87,17 @@ pub fn set_irq_handler(irq: u8, handler: fn()) { } pub fn set_irq_mask(irq: u8) { - let mut port: Port = Port::new(if irq < 8 { 0x21 } else { 0xA1 } ); + let mut port: Port = Port::new(if irq < 8 { PIC1 } else { PIC2 } ); unsafe { - let value = port.read() & (1 << irq); + let value = port.read() | (1 << (if irq < 8 { irq } else { irq - 8 })); port.write(value); } } pub fn clear_irq_mask(irq: u8) { - let mut port: Port = Port::new(if irq < 8 { 0x21 } else { 0xA1 } ); + let mut port: Port = Port::new(if irq < 8 { PIC1 } else { PIC2 } ); unsafe { - let value = port.read() & !(1 << irq); + let value = port.read() & !(1 << if irq < 8 { irq } else { irq - 8 }); port.write(value); } } diff --git a/src/kernel/time.rs b/src/kernel/time.rs index ead6187f6..c6ec4c95f 100644 --- a/src/kernel/time.rs +++ b/src/kernel/time.rs @@ -1,19 +1,35 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; +use crate::kernel::cmos::CMOS; use crate::kernel; -use lazy_static::lazy_static; -use spin::Mutex; +use x86_64::instructions::interrupts; +use x86_64::instructions::port::Port; -lazy_static! { - pub static ref TICKS: Mutex = Mutex::new(0); -} +// At boot the PIT starts with a frequency divider of 0 (equivalent to 65536) +// which will result in about 54.926 ms between ticks. +// During init we will change the divider to 1193 to have about 1.000 ms +// between ticks to improve time measurements accuracy. +const PIT_FREQUENCY: f64 = 3_579_545.0 / 3.0; // 1_193_181.666 Hz +const PIT_DIVIDER: usize = 1193; +const PIT_INTERVAL: f64 = (PIT_DIVIDER as f64) / PIT_FREQUENCY; + +static PIT_TICKS: AtomicUsize = AtomicUsize::new(0); +static LAST_RTC_UPDATE: AtomicUsize = AtomicUsize::new(0); pub fn ticks() -> usize { - *TICKS.lock() + PIT_TICKS.load(Ordering::Relaxed) +} + +pub fn time_between_ticks() -> f64 { + PIT_INTERVAL +} + +pub fn last_rtc_update() -> usize { + LAST_RTC_UPDATE.load(Ordering::Relaxed) } pub fn sleep(duration: f64) { - let interval = 1.0 / (1.193182 * 1000000.0 / 65536.0); let start = kernel::clock::uptime(); - while kernel::clock::uptime() - start < duration - interval { + while kernel::clock::uptime() - start < duration - time_between_ticks() { halt(); } } @@ -23,10 +39,35 @@ pub fn halt() { } pub fn init() { - kernel::idt::set_irq_handler(0, interrupt_handler); + // PIT timmer + let divider = if PIT_DIVIDER < 65536 { PIT_DIVIDER } else { 0 }; + set_pit_frequency_divider(divider as u16); + kernel::idt::set_irq_handler(0, pit_interrupt_handler); + + // RTC timmer + kernel::idt::set_irq_handler(8, rtc_interrupt_handler); + CMOS::new().enable_update_interrupt(); +} + +/// The frequency divider must be between 0 and 65535, with 0 acting as 65536 +fn set_pit_frequency_divider(divider: u16) { + interrupts::without_interrupts(|| { + let bytes = divider.to_le_bytes(); + let mut cmd: Port = Port::new(0x43); + let mut data: Port = Port::new(0x40); + unsafe { + cmd.write(0x36); + data.write(bytes[0]); + data.write(bytes[1]); + } + }); +} + +pub fn pit_interrupt_handler() { + PIT_TICKS.fetch_add(1, Ordering::Relaxed); } -pub fn interrupt_handler() { - let mut ticks = TICKS.lock(); - *ticks += 1; +pub fn rtc_interrupt_handler() { + LAST_RTC_UPDATE.store(ticks(), Ordering::Relaxed); + CMOS::new().notify_end_of_interrupt(); }