diff --git a/moon/Cargo.toml b/moon/Cargo.toml index e315cd20..74594197 100644 --- a/moon/Cargo.toml +++ b/moon/Cargo.toml @@ -16,5 +16,8 @@ strip = "debuginfo" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +pc-keyboard = "0.6.1" +pic8259 = "0.10.2" spin = "0.9.4" -x86_64 = "0.14.10" \ No newline at end of file +volatile = "0.4.5" +x86_64 = "0.14.10" diff --git a/moon/src/arch/cpu.rs b/moon/src/arch/cpu.rs index 7f6a1368..97fc815c 100644 --- a/moon/src/arch/cpu.rs +++ b/moon/src/arch/cpu.rs @@ -1,2 +1,13 @@ -#![cfg(target_arch = "x86_64")] -pub use super::x86_64::cpu::*; \ No newline at end of file +#![allow(dead_code)] + +#[cfg(target_arch = "x86_64")] +pub use super::x86_64::cpu::*; + +use super::interrupts; + +pub fn idle_loop() -> ! { + interrupts::enable(); + loop { + wait_for_interrupt(); + } +} \ No newline at end of file diff --git a/moon/src/arch/mod.rs b/moon/src/arch/mod.rs index 394efc3e..a51daa75 100644 --- a/moon/src/arch/mod.rs +++ b/moon/src/arch/mod.rs @@ -1,6 +1,7 @@ pub mod cpu; pub mod io; pub mod interrupts; +pub mod timer; #[cfg(target_arch = "x86_64")] mod x86_64; \ No newline at end of file diff --git a/moon/src/arch/timer.rs b/moon/src/arch/timer.rs new file mode 100644 index 00000000..aff6fa73 --- /dev/null +++ b/moon/src/arch/timer.rs @@ -0,0 +1,2 @@ +#[cfg(target_arch = "x86_64")] +pub use super::x86_64::timer::*; \ No newline at end of file diff --git a/moon/src/arch/x86_64/cpu.rs b/moon/src/arch/x86_64/cpu.rs index 8d9f0242..ae93e135 100644 --- a/moon/src/arch/x86_64/cpu.rs +++ b/moon/src/arch/x86_64/cpu.rs @@ -2,7 +2,6 @@ #![allow(dead_code)] use core::arch::x86_64::{__cpuid as do_cpuid, CpuidResult}; -use core::arch::asm; use super::gdt; @@ -15,23 +14,24 @@ pub fn get_processor_id() -> u32 { return cpuid_result.ebx >> 24; } +use x86_64::instructions::hlt; +use x86_64::instructions::interrupts; + pub fn halt() -> ! { loop { - unsafe { - asm!("cli"); - asm!("hlt"); - } + interrupts::disable(); + wait_for_interrupt(); } } pub fn wait_for_interrupt() { - unsafe { asm!("hlt"); } + hlt(); } pub fn breakpoint() { - unsafe { asm!("int3"); } + interrupts::int3(); } pub fn platform_init() diff --git a/moon/src/arch/x86_64/interrupts.rs b/moon/src/arch/x86_64/interrupts.rs index d7d0c026..0bf418aa 100644 --- a/moon/src/arch/x86_64/interrupts.rs +++ b/moon/src/arch/x86_64/interrupts.rs @@ -1,10 +1,26 @@ +#![cfg(target_arch = "x86_64")] use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; -use crate::try_println; +use crate::{try_print, try_println}; use core::arch::asm; use spin::Mutex; +use crate::timer; +use pic8259::ChainedPics; + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +pub static PICS: Mutex = + Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); static IDT: Mutex = Mutex::new(InterruptDescriptorTable::new()); +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum IRQ { + Timer = PIC_1_OFFSET, + Keyboard = PIC_1_OFFSET + 1, +} + extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { try_println!("Breakpoint!\n{:#?}", stack_frame); @@ -19,16 +35,55 @@ extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, panic!("Double fault!\n{:#?}", stack_frame); } +extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame) +{ + timer::tick(); + unsafe { PICS.lock().notify_end_of_interrupt(IRQ::Timer as u8); } +} + +extern "x86-interrupt" fn keyboard_handler(_stack_frame: InterruptStackFrame) +{ + use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; + + use x86_64::instructions::port::Port; + static KEYBOARD: Mutex> = Mutex::new(Keyboard::new(HandleControl::Ignore)); + + let mut keyboard = KEYBOARD.lock(); + let mut port = Port::new(0x60); + + let scancode: u8 = unsafe { port.read() }; + if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { + if let Some(key) = keyboard.process_keyevent(key_event) { + match key { + DecodedKey::Unicode(character) => try_print!("{}", character), + DecodedKey::RawKey(key) => try_print!("{:?}", key), + } + } + } + + unsafe { + PICS.lock() + .notify_end_of_interrupt(IRQ::Keyboard as u8); + } +} + pub fn load() { let mut idt = IDT.lock(); idt.breakpoint.set_handler_fn(breakpoint_handler); idt.page_fault.set_handler_fn(page_fault_handler); idt.double_fault.set_handler_fn(double_fault_handler); + idt[IRQ::Timer as usize].set_handler_fn(timer_handler); + idt[IRQ::Keyboard as usize].set_handler_fn(keyboard_handler); unsafe { idt.load_unsafe(); } + + unsafe { + PICS.lock().initialize(); + PICS.lock().write_masks(0b11111100, 0b11111111) + } } pub fn enable() diff --git a/moon/src/arch/x86_64/mod.rs b/moon/src/arch/x86_64/mod.rs index 42037c1e..108319dd 100644 --- a/moon/src/arch/x86_64/mod.rs +++ b/moon/src/arch/x86_64/mod.rs @@ -2,4 +2,5 @@ pub mod cpu; pub mod io; pub mod interrupts; +pub mod timer; mod gdt; \ No newline at end of file diff --git a/moon/src/arch/x86_64/timer.rs b/moon/src/arch/x86_64/timer.rs new file mode 100644 index 00000000..2df675e2 --- /dev/null +++ b/moon/src/arch/x86_64/timer.rs @@ -0,0 +1,24 @@ +#![cfg(target_arch = "x86_64")] +pub const TIMER_FREQUENCY: u64 = 5000; +const BASE_FREQUENCY: u32 = 1193182; + +use x86_64::instructions::port::Port; +use crate::arch::io; + +pub fn platform_init() +{ + let mut divisor = (BASE_FREQUENCY / TIMER_FREQUENCY as u32) as u16; + if divisor < 100 { divisor = 100; } + + let mut port: Port = Port::new(0x40); + + unsafe { + port.write((divisor & 0xff) as u8); + io::io_delay(); + port.write(((divisor & 0xFF00) >> 8) as u8); + } +} + +pub fn frequency() -> u64 { + return TIMER_FREQUENCY; +} \ No newline at end of file diff --git a/moon/src/main.rs b/moon/src/main.rs index 9ec548b9..c5f2da2e 100644 --- a/moon/src/main.rs +++ b/moon/src/main.rs @@ -11,6 +11,7 @@ mod util; mod init; mod arch; mod log; +mod timer; use arch::cpu; use arch::interrupts; @@ -27,6 +28,8 @@ pub extern "C" fn _start() -> ! { cpu::platform_init(); + timer::init(timer::decode_boot_timestamp()); + interrupts::load(); video::clear(Color::White); @@ -35,10 +38,5 @@ pub extern "C" fn _start() -> ! { interrupts::enable(); - cpu::breakpoint(); - - println!("Still here!"); - - cpu::halt(); - + cpu::idle_loop(); } \ No newline at end of file diff --git a/moon/src/timer.rs b/moon/src/timer.rs new file mode 100644 index 00000000..8ca9a2e3 --- /dev/null +++ b/moon/src/timer.rs @@ -0,0 +1,112 @@ +#![allow(dead_code)] +use crate::println; + +use spin::RwLock; + +static mut TIMER: u64 = 0; // writes to aligned 64-bit integers are atomic, at least on x86_64 +static BOOT_TIMESTAMP: RwLock = RwLock::new(0); + +use crate::arch::timer; + +pub fn init(boot_timestamp: i64) +{ + *BOOT_TIMESTAMP.write() = boot_timestamp; + timer::platform_init(); +} + +pub fn tick() +{ + unsafe { TIMER += 1 } +} + +pub fn count() -> u64 { + unsafe { TIMER } +} + +const MS_PER_SECOND: u64 = 1000; +const US_PER_SECOND: u64 = 1000000; +const NS_PER_SECOND: u64 = 1000000000; + +pub fn second_count() -> u64 { + return count() / timer::frequency(); +} + +// FIXME: Propagate overflows. A system call may want to return EOVERFLOW instead of panicking. +pub fn millisecond_count() -> u64 { + return count().checked_mul(MS_PER_SECOND).unwrap() / timer::frequency(); +} + +pub fn microsecond_count() -> u64 { + return count().checked_mul(US_PER_SECOND).unwrap() / timer::frequency(); +} + +pub fn nanosecond_count() -> u64 { + return count().checked_mul(NS_PER_SECOND).unwrap() / timer::frequency(); +} + +pub fn boot_timestamp() -> i64 { + *BOOT_TIMESTAMP.read() +} + +// FIXME: These functions are verbose. +pub fn second_time() -> i64 { + i64::try_from(second_count()).unwrap().checked_add(boot_timestamp()).unwrap() +} + +pub fn millisecond_time() -> i64 { + i64::try_from(millisecond_count()).unwrap().checked_add(boot_timestamp().checked_mul(MS_PER_SECOND as i64).unwrap()).unwrap() +} + +pub fn microsecond_time() -> i64 { + i64::try_from(microsecond_count()).unwrap().checked_add(boot_timestamp().checked_mul(US_PER_SECOND as i64).unwrap()).unwrap() +} + +pub fn nanosecond_time() -> i64 { + i64::try_from(nanosecond_count()).unwrap().checked_add(boot_timestamp().checked_mul(NS_PER_SECOND as i64).unwrap()).unwrap() +} + +pub fn isleap(year: i32) -> bool +{ + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) +} + +fn make_yday(year: i32, month: i32) -> i32 { + let upto: [i16; 12usize] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + let mut yd: i16; + + yd = upto[(month - 1) as usize]; + if month > 2 && isleap(year) + { + yd += 1; + } + yd as i32 +} + +pub fn broken_down_to_unix(year: i64, month: i64, day: i64, hour: i64, min: i64, sec: i64) -> i64 { + return sec + min * 60 + hour * 3600 + (make_yday(year as i32, month as i32) + (day - 1) as i32) as i64 * 86400 + (year - 70) * 31536000 + ((year - 69) / 4) * 86400 - + ((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400; +} + +fn bcd_number_to_decimal(num: u8) -> i32 +{ + return (((num >> 4) * 10) + (num & 0xf)) as i32; +} + +pub fn decode_timestamp(datetime: [u8; 8]) -> i64 { + let year = (bcd_number_to_decimal(datetime[0]) * 100) + bcd_number_to_decimal(datetime[1]); + let month = bcd_number_to_decimal(datetime[2]); + let day = bcd_number_to_decimal(datetime[3]); + let hour = bcd_number_to_decimal(datetime[4]); + let min = bcd_number_to_decimal(datetime[5]); + let sec = bcd_number_to_decimal(datetime[6]); + println!("{}-{}-{} {}:{}:{}", year, month, day, hour, min, sec); + broken_down_to_unix((year - 1900) as i64, month as i64, day as i64, hour as i64, min as i64, sec as i64) +} + +use crate::bootboot::BOOTBOOT; +use crate::util::get_bootboot; + +pub fn decode_boot_timestamp() -> i64 { + let boot: &BOOTBOOT = get_bootboot(); + decode_timestamp(boot.datetime) +} \ No newline at end of file