diff --git a/Cargo.lock b/Cargo.lock index 913d9ef..bbfcaea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,8 @@ version = "0.1.0" dependencies = [ "bootloader 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pc-keyboard 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pic8259_simple 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "uart_16550 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -45,6 +47,11 @@ name = "cc" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cpuio" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lazy_static" version = "1.4.0" @@ -58,6 +65,19 @@ name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pc-keyboard" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pic8259_simple" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cpuio 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "raw-cpuid" version = "6.1.0" @@ -145,8 +165,11 @@ dependencies = [ "checksum bootloader 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "45dd858bd74a742ec0fe887722952c263abd0825aa8d33a3704917a97d7bd41e" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" +"checksum cpuio 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum pc-keyboard 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fff50ab09ba31bcebc0669f4e64c0952fae1acdca9e6e0587e68e4e8443808ac" +"checksum pic8259_simple 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc64b2fd10828da8521b6cdabe0679385d7d2a3a6d4c336b819d1fa31ba35c72" "checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" diff --git a/Cargo.toml b/Cargo.toml index 204f961..de8bc4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,8 @@ edition = "2018" [dependencies] bootloader = "0.8.1" +pc-keyboard = "0.3.1" +pic8259_simple = "0.1.1" spin = "0.5.2" uart_16550 = "0.2.0" volatile = "0.2.6" diff --git a/src/interrupts.rs b/src/interrupts.rs index 3adacc5..241ae77 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,8 +1,11 @@ use lazy_static::lazy_static; +use pic8259_simple::ChainedPics; +use spin; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use crate::gdt; -use crate::println; +use crate::hlt_loop; +use crate::{print, println}; #[cfg(test)] use crate::{serial_print, serial_println}; @@ -15,6 +18,10 @@ 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 }; @@ -33,7 +40,71 @@ extern "x86-interrupt" fn breakpoint_handler( extern "x86-interrupt" fn double_fault_handler( stack_frame: &mut InterruptStackFrame, _error_code: u64 ) { - panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); + println!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); + hlt_loop(); +} + +extern "x86-interrupt" fn timer_interrupt_handler( + _stack_frame: &mut InterruptStackFrame +) { + print!("."); + + unsafe { + PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); + } +} + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +pub static PICS: spin::Mutex = spin::Mutex::new(unsafe { + ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) +}); + +extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) { + use x86_64::instructions::port::Port; + use pc_keyboard::{Keyboard, ScancodeSet1, DecodedKey, layouts}; + use spin::Mutex; + + lazy_static!{ + static ref KEYBOARD: Mutex> = + Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1)); + } + + let mut keyboard = KEYBOARD.lock(); + let mut port = Port::new(0x60); // PS/2 port + + 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()); + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum InterruptIndex { + Timer = PIC_1_OFFSET, + Keyboard, +} + +impl InterruptIndex { + fn as_u8(self) -> u8 { + self as u8 + } + + fn as_usize(self) -> usize { + usize::from(self.as_u8()) + } } #[test_case] diff --git a/src/lib.rs b/src/lib.rs index f65c260..98d1dd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! { serial_println!("[failed]\n"); serial_println!("Error: {}\n", info); exit_qemu(QemuExitCode::Failed); - loop {} + hlt_loop(); } /// Entry point for `cargo xtest` @@ -34,7 +34,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! { pub extern "C" fn _start() -> ! { init(); test_main(); - loop {} + hlt_loop(); } #[cfg(test)] @@ -59,7 +59,15 @@ pub fn exit_qemu(exit_code: QemuExitCode) { } } +pub fn hlt_loop() -> ! { + loop { + x86_64::instructions::hlt(); + } +} + pub fn init() { gdt::init(); interrupts::init_idt(); + unsafe { interrupts::PICS.lock().initialize() }; + x86_64::instructions::interrupts::enable(); } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index aa31d6b..038b33a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use blog_os::println; #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); - loop {} + blog_os::hlt_loop(); } #[cfg(test)] @@ -32,5 +32,5 @@ pub extern "C" fn _start() -> ! { test_main(); println!("It did not crash!"); - loop {} + blog_os::hlt_loop(); } diff --git a/src/serial.rs b/src/serial.rs index 2da8766..41dcaf3 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -13,8 +13,12 @@ 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"); + }); } diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 80f91e3..84cae0f 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -134,7 +134,11 @@ lazy_static! { #[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] @@ -155,14 +159,20 @@ fn test_println_many() { #[test_case] fn test_println_output() { + use core::fmt::Write; + use x86_64::instructions::interrupts; + serial_print!("test_println_output... "); let s = "Some test string that fits on a single line"; - println!("{}", s); - for (i, c) in s.chars().enumerate() { - let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read(); - assert_eq!(char::from(screen_char.ascii_character), c); - } + interrupts::without_interrupts(|| { + let mut writer = WRITER.lock(); + writeln!(writer, "\n{}", s).expect("writeln failed"); + for (i, c) in s.chars().enumerate() { + let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read(); + assert_eq!(char::from(screen_char.ascii_character), c); + } + }); serial_println!("[ok]"); }