From e5f746acadf22f14c719f4a018d12115c36f5bad Mon Sep 17 00:00:00 2001 From: ami-chuu Date: Mon, 30 Mar 2026 18:02:04 +0300 Subject: [PATCH] Added Hardware Interrupts --- src/interrupts.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 12 ++++++-- src/main.rs | 4 +-- src/serial.rs | 12 +++++--- src/vga_buffer.rs | 22 +++++++++++---- 5 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/interrupts.rs b/src/interrupts.rs index 6f660c7..956b7d0 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,8 +1,16 @@ -use crate::gdt; -use crate::println; +use crate::{gdt, print, println}; use lazy_static::lazy_static; +use pic8259::ChainedPics; +use spin; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum InterruptIndex { + Timer = PIC_1_OFFSET, + Keyboard, +} + lazy_static! { static ref IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); @@ -12,6 +20,8 @@ lazy_static! { .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 }; @@ -32,8 +42,66 @@ extern "x86-interrupt" fn double_fault_handler( panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); } +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( + ScancodeSet1::new(), + layouts::Us104Key, + 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()); + } +} + #[test_case] fn test_breakpoint_exception() { // invoke a breakpoint exception x86_64::instructions::interrupts::int3(); } + +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) }); + +impl InterruptIndex { + fn as_u8(self) -> u8 { + self as u8 + } + + fn as_usize(self) -> usize { + usize::from(self.as_u8()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 6f6f9c1..2d803be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,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 test` @@ -48,7 +48,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! { pub extern "C" fn _start() -> ! { init(); test_main(); - loop {} + hlt_loop(); } #[cfg(test)] @@ -76,4 +76,12 @@ pub fn exit_qemu(exit_code: QemuExitCode) { pub fn init() { gdt::init(); interrupts::init_idt(); + unsafe { interrupts::PICS.lock().initialize() }; + x86_64::instructions::interrupts::enable(); +} + +pub fn hlt_loop() -> ! { + loop { + x86_64::instructions::hlt(); + } } diff --git a/src/main.rs b/src/main.rs index 0b41888..ae33235 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ pub extern "C" fn _start() -> ! { test_main(); println!("It did not crash!"); - loop {} + amix::hlt_loop(); } // This function is called on panic. @@ -25,7 +25,7 @@ pub extern "C" fn _start() -> ! { #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); - loop {} + amix::hlt_loop(); } #[cfg(test)] diff --git a/src/serial.rs b/src/serial.rs index d0aa59b..ac5bf6f 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -13,10 +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"); + }); } // Prints to the host through the serial interface. diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 12392f1..0366321 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -140,7 +140,11 @@ macro_rules! 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] @@ -157,10 +161,16 @@ fn test_println_many() { #[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); + } + }); }