diff --git a/Cargo.lock b/Cargo.lock index cd70fe0..5ff9bfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,12 +41,29 @@ version = "0.1.0" dependencies = [ "bootloader", "lazy_static", + "pc-keyboard", + "pic8259", "spin", "uart_16550", "volatile 0.2.7", "x86_64", ] +[[package]] +name = "pc-keyboard" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac" + +[[package]] +name = "pic8259" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb844b5b01db1e0b17938685738f113bfc903846f18932b378bc0eabfa40e194" +dependencies = [ + "x86_64", +] + [[package]] name = "rustversion" version = "1.0.14" diff --git a/Cargo.toml b/Cargo.toml index 37e0633..647dd34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,8 @@ volatile = "0.2.6" spin = "0.5.2" x86_64 = "0.14.2" uart_16550 = "0.2.0" +pic8259 = "0.10.1" +pc-keyboard = "0.5.0" [dependencies.lazy_static] version = "1.0" diff --git a/src/interrupts.rs b/src/interrupts.rs index 7266767..5100ae1 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,7 +1,17 @@ use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use crate::println; +use crate::print; use crate::gdt; use lazy_static::lazy_static; +use pic8259::ChainedPics; +use spin; + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum InterruptIndex { + Timer = PIC_1_OFFSET, + Keyboard, +} lazy_static! { static ref IDT: InterruptDescriptorTable = { @@ -11,6 +21,8 @@ lazy_static! { idt.double_fault.set_handler_fn(double_fault_handler) .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); } + idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler); + idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler); idt }; } @@ -27,6 +39,59 @@ extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); } +extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { + unsafe { + PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); + } +} + +extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { + use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; + use spin::Mutex; + use x86_64::instructions::port::Port; + + lazy_static! { + static ref KEYBOARD: Mutex> = + Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1, + 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) => print!("{}", character), + DecodedKey::RawKey(key) => print!("{:?}", key), + } + } + } + + unsafe { + PICS.lock() + .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8()); + } +} + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +impl InterruptIndex { + fn as_u8(self) -> u8 { + self as u8 + } + + fn as_usize(self) -> usize { + usize::from(self.as_u8()) + } +} + +pub static PICS: spin::Mutex = + spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); + #[test_case] fn test_breakpoint_exceptions() { x86_64::instructions::interrupts::int3(); diff --git a/src/lib.rs b/src/lib.rs index f4f3a5b..41880b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ pub mod gdt; pub fn init() { gdt::init(); interrupts::init_idt(); + unsafe { interrupts::PICS.lock().initialize() }; + x86_64::instructions::interrupts::enable(); } pub trait Testable { @@ -44,7 +46,13 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! { serial_println!("[failed]\n"); serial_println!("Error: {}\n", info); exit_qemu(QemuExitCode::Failed); - loop {} + hlt_loop(); +} + +pub fn hlt_loop() -> ! { + loop { + x86_64::instructions::hlt(); + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -69,7 +77,7 @@ pub fn exit_qemu(exit_code: QemuExitCode) { pub extern "C" fn _start() -> ! { init(); test_main(); - loop {} + hlt_loop(); } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index 555e1cf..431a81d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ pub extern "C" fn _start() -> ! { #[cfg(test)] test_main(); - loop {} + os::hlt_loop(); } // This function is called on panic in non-test mode. @@ -32,7 +32,7 @@ pub extern "C" fn _start() -> ! { #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); - loop {} + os::hlt_loop(); } #[cfg(test)] diff --git a/src/serial.rs b/src/serial.rs index e8073d9..bf24450 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -13,7 +13,13 @@ lazy_static! { #[doc(hidden)] pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; - SERIAL1.lock().write_fmt(args).expect("Printing to serial failed"); + use x86_64::instructions::interrupts; + interrupts::without_interrupts(|| { + SERIAL1 + .lock() + .write_fmt(args) + .expect("Printing to serial failed"); + }); } /// Prints to the host through the serial interface. diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index e1340cf..43856d2 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -141,10 +141,14 @@ macro_rules! println { ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } +// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. #[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; - WRITER.lock().write_fmt(args).unwrap(); + use x86_64::instructions::interrupts; + interrupts::without_interrupts(|| { + WRITER.lock().write_fmt(args).unwrap(); + }); } #[test_case]