From 6f67926a75fbed691003b4be5acc0b729aa9aa5b Mon Sep 17 00:00:00 2001 From: Sainnhe Park Date: Tue, 11 Jan 2022 17:09:13 +0800 Subject: [PATCH] Hardware Interrupts --- Cargo.lock | 17 +++++++++++++ Cargo.toml | 2 ++ src/interrupts.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 12 +++++++-- src/main.rs | 11 ++------ src/serial.rs | 9 ++++++- src/vga_buffer.rs | 22 +++++++++++----- 7 files changed, 119 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ae52f6..af7486b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,8 @@ version = "0.1.0" dependencies = [ "bootloader", "lazy_static", + "pc-keyboard", + "pic8259", "spin", "uart_16550", "volatile 0.2.7", @@ -41,6 +43,21 @@ dependencies = [ "spin", ] +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ec21f514e2e16e94649f1d041ca4a7069b512c037ac156360652a775e6229d" +dependencies = [ + "x86_64", +] + [[package]] name = "spin" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 21935d6..f3e7049 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ volatile = "0.2.6" spin = "0.5.2" x86_64 = "0.14.7" 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 9918371..870d8d3 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,7 +1,9 @@ use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; -use crate::println; +use crate::{println, print}; use lazy_static::lazy_static; use crate::gdt; +use pic8259::ChainedPics; +use spin; lazy_static! { static ref IDT: InterruptDescriptorTable = { @@ -11,6 +13,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 }; } @@ -26,3 +32,60 @@ extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, _error_code: u64) -> ! { panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); } + +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) }); + +#[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()) + } +} + +extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { + print!("."); + 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()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 5c91a6b..9e58b6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! { serial_println!("[failed]\n"); serial_println!("Error: {}\n", info); exit_qemu(QemuExitCode::Failed); - loop {} + hlt_loop(); } #[cfg(test)] @@ -46,7 +46,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! { pub extern "C" fn _start() -> ! { init(); test_main(); - loop {} + hlt_loop(); } #[cfg(test)] @@ -79,7 +79,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(); } diff --git a/src/main.rs b/src/main.rs index 4be060b..693740f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,17 +20,10 @@ pub extern "C" fn _start() -> ! { x86_64::instructions::interrupts::int3(); println!("It didn't crash!"); - // 触发一个页错误 - // 由于我们没有设定页错误的处理函数 - // 所以会触发 double fault - unsafe { - *(0xdeadbeef as *mut u64) = 42; // 强制写入某个内存地址 - } - #[cfg(test)] test_main(); - loop {} + anos::hlt_loop(); } // 在非测试模式下,将 panic 信息打印到 VGA 缓冲区 @@ -40,7 +33,7 @@ pub extern "C" fn _start() -> ! { // 禁用标准库之后,这些东西就都没有了,我们需要把它重写一遍 fn panic(info: &PanicInfo) -> ! { println!("{}", info); - loop {} + anos::hlt_loop(); } #[cfg(test)] diff --git a/src/serial.rs b/src/serial.rs index 6c6d65f..353b13a 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -13,7 +13,14 @@ 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"); + }); } #[macro_export] diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 0d8403b..83df5b0 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -153,7 +153,11 @@ macro_rules! println { // println!() #[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] @@ -170,10 +174,16 @@ fn test_println_many() { // 测试多行 println! #[test_case] fn test_println_output() { // 测试字符是否真的打印到了屏幕上 + use core::fmt::Write; + use x86_64::instructions::interrupts; + 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); + } + }); }