//! Implementation of CPU interruptions use core::ops::IndexMut; use core::sync::atomic::Ordering; use spin::Lazy; use x86_64::instructions::port::Port; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use super::cmos::CMOS; use super::pics::{PIC1_OFFSET, PIC2_OFFSET, PICS}; use super::pit::{self, LAST_RTC_UPDATE, TICKS}; use crate::kernel::interrupt::keyboard::KeyQueue; /// 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, /// Standard ATA primary interrupt (0xE above base offset) AtaPrimary = PIC1_OFFSET + 0x0E, /// Standard ATA primary interrupt (0xF above base offset) AtaSecondary = PIC1_OFFSET + 0x0F, } 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); set_handler_fn(&mut idt, InterruptIndex::AtaPrimary, primary_ata_handler); set_handler_fn(&mut idt, InterruptIndex::AtaSecondary, secondary_ata_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 #[cfg(not(test))] extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { use crate::println; println!("Exception: breakpoint\n{:#?}", stack_frame); } #[cfg(test)] const extern "x86-interrupt" fn breakpoint_handler(_stack_frame: InterruptStackFrame) {} /// 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: RTC 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 extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { KeyQueue::handle_interrupt(); // SAFETY: Keyboard index is the same everywhere it should unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::Keyboard.into()); }; } /// Primary ATA interrupt handler extern "x86-interrupt" fn primary_ata_handler(_stack_frame: InterruptStackFrame) { // SAFETY: ATA primary index is the standard one unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaPrimary.into()); }; } /// Secondary ATA interrupt handler extern "x86-interrupt" fn secondary_ata_handler(_stack_frame: InterruptStackFrame) { // SAFETY: ATA secondary index is the standard one unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaSecondary.into()); }; } #[cfg(test)] mod test { use x86_64::instructions::interrupts; #[test_case] fn interruption() { interrupts::int3(); } }