//! Implementation of CPU interruptions use core::ops::IndexMut; use core::sync::atomic::Ordering; use pc_keyboard::{DecodedKey, KeyCode, KeyState}; use spin::Lazy; use x86_64::instructions::port::Port; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use super::cmos::CMOS; use super::keyboard::{get_scancode, send_csi, ALT_PRESSED, CTRL_PRESSED, KEYBOARD, SHIFT_PRESSED}; use super::pics::{PIC1_OFFSET, PIC2_OFFSET, PICS}; use super::pit::{self, LAST_RTC_UPDATE, TICKS}; use crate::encoding::ascii::Char; use crate::kernel::interrupt::keyboard::send_key; use crate::println; /// Index of the interrupt numbers (IRQ) #[repr(u8)] #[derive(Debug, Clone, Copy)] pub(super) enum InterruptIndex { /// Timer interrupt Timer = PIC1_OFFSET, /// Keyboards interrupt Keyboard, /// Real-time controller interrupt Rtc = PIC2_OFFSET, } impl From<InterruptIndex> for u8 { fn from(value: InterruptIndex) -> Self { value as Self } } impl From<InterruptIndex> for usize { fn from(value: InterruptIndex) -> Self { u8::from(value).into() } } /// Global Interruption Descriptor Table (IDT) pub(super) static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| { let mut idt = InterruptDescriptorTable::new(); idt.breakpoint.set_handler_fn(breakpoint_handler); idt.double_fault.set_handler_fn(double_fault_handler); set_handler_fn(&mut idt, InterruptIndex::Timer, timer_interrupt_handler); set_handler_fn(&mut idt, InterruptIndex::Keyboard, keyboard_interrupt_handler); set_handler_fn(&mut idt, InterruptIndex::Rtc, rtc_interrupt_handler); idt }); /// Set a given `handler` for the given `idt` at a given `pin` fn set_handler_fn( idt: &mut InterruptDescriptorTable, pin: InterruptIndex, handler: extern "x86-interrupt" fn(InterruptStackFrame), ) { idt.index_mut(pin.into()).set_handler_fn(handler); let index: u8 = Into::<u8>::into(pin) - PIC1_OFFSET; let (interrupt_line, port_number) = if index < 8 { (index, 0x21) } else { (index - 8, 0xA1) }; let mut port = Port::<u8>::new(port_number); // SAFETY: no memory violation is made here let byte = unsafe { port.read() }; // SAFETY: and neither here unsafe { port.write(byte & !(1 << interrupt_line)); }; } /// Custom interruption handler to print useful informations on the error extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { println!("Exception: breakpoint\n{:#?}", stack_frame); } /// Same purpose as [`breakpoint_handler`] but for double faults extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) -> ! { panic!("Double fault code {}\n{:#?}", error_code, stack_frame); } /// Same purpose as [`breakpoint_handler`] but for time interruptions extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { TICKS.fetch_add(1, Ordering::Relaxed); // SAFETY: Timer index is the same everywhere it should unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.into()); }; } /// Same purpose as [`breakpoint_handler`] but for real-time controller interruptions extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFrame) { LAST_RTC_UPDATE.store(pit::ticks(), Ordering::Relaxed); CMOS.notify_end_of_interrupt(); // SAFETY: Timer index is the same everywhere it should unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::Rtc.into()); }; } /// Same purpose as [`breakpoint_handler`] but for real-time controller interruptions #[allow(clippy::wildcard_enum_match_arm)] // To let the code be readable extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { /// Tabulation char const TAB: char = Char::Tab.into(); /// Delete char const DEL: char = Char::DEL.into(); let mut binding = KEYBOARD.lock(); let keyboard = binding.get_mut().expect("Tried to get the keyboard before its initialization"); let scancode = get_scancode(); if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { match key_event.code { KeyCode::LAlt | KeyCode::RAltGr => ALT_PRESSED.store(key_event.state == KeyState::Down, Ordering::Relaxed), KeyCode::LShift | KeyCode::RShift => SHIFT_PRESSED.store(key_event.state == KeyState::Down, Ordering::Relaxed), KeyCode::LControl | KeyCode::RControl => SHIFT_PRESSED.store(key_event.state == KeyState::Down, Ordering::Relaxed), _ => {}, }; let alt_pressed = ALT_PRESSED.load(Ordering::Relaxed); let ctrl_pressed = CTRL_PRESSED.load(Ordering::Relaxed); let shift_pressed = SHIFT_PRESSED.load(Ordering::Relaxed); if let Some(key) = keyboard.process_keyevent(key_event) { match key { DecodedKey::RawKey(KeyCode::ArrowUp) => send_csi("1A"), DecodedKey::RawKey(KeyCode::ArrowDown) => send_csi("1B"), DecodedKey::RawKey(KeyCode::ArrowRight) => send_csi("1C"), DecodedKey::RawKey(KeyCode::ArrowLeft) => send_csi("1D"), DecodedKey::Unicode(TAB) if shift_pressed => send_csi("2"), DecodedKey::Unicode(DEL) if alt_pressed && ctrl_pressed => println!("TODO: implement reboot"), DecodedKey::Unicode(key) => send_key(key), _ => {}, } }; }; // SAFETY: Timer index is the same everywhere it should unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::Rtc.into()); }; } #[cfg(test)] mod test { use x86_64::instructions::interrupts; #[test_case] fn interruption() { interrupts::int3(); } }