//! Interrupt Descriptor Table (IDT) and Exception Handling use crate::kprintln; use crate::sched::SCHEDULER; use crate::drivers::timer::send_eoi; use core::mem::size_of; #[repr(C, packed)] #[derive(Clone, Copy)] pub struct IdtEntry { offset_low: u16, selector: u16, ist: u8, type_attr: u8, offset_mid: u16, offset_high: u32, _reserved: u32, } impl IdtEntry { pub const fn missing() -> Self { Self { offset_low: 0, selector: 0, ist: 0, type_attr: 0, offset_mid: 0, offset_high: 0, _reserved: 0, } } pub fn set_handler(&mut self, handler: u64, ist: u8, dpl: u8) { self.offset_low = (handler & 0xffff) as u16; self.selector = 0x08; // Kernel code segment self.ist = ist & 0x7; self.type_attr = 0x80 | ((dpl & 0x3) << 5) | 0x0E; // 64-bit Interrupt Gate Present self.offset_mid = ((handler >> 16) & 0xffff) as u16; self.offset_high = ((handler >> 32) & 0xffffffff) as u32; self._reserved = 0; } } #[repr(C, packed)] struct IdtDescriptor { limit: u16, base: u64, } static mut IDT: [IdtEntry; 256] = [IdtEntry::missing(); 256]; extern "C" { fn default_isr_stub(); fn timer_isr_entry(); fn keyboard_isr_entry(); fn mouse_isr_entry(); } // Global assembly ISR stub with vector ID pushed as separate stubs (0-31) core::arch::global_asm!( // With error code: CPU already pushed error_code, we push vector on top ".macro ISR_ERR_CODE num", "isr_stub_\\num:", "push \\num", // push vector number (error_code already on stack below) "jmp default_isr_common", ".endm", // Without error code: push dummy 0, then vector ".macro ISR_NO_ERR num", "isr_stub_\\num:", "push 0", // dummy error_code "push \\num", // push vector number "jmp default_isr_common", ".endm", "ISR_NO_ERR 0", "ISR_NO_ERR 1", "ISR_NO_ERR 2", "ISR_NO_ERR 3", "ISR_NO_ERR 4", "ISR_NO_ERR 5", "ISR_NO_ERR 6", "ISR_NO_ERR 7", "ISR_ERR_CODE 8","ISR_NO_ERR 9", "ISR_ERR_CODE 10","ISR_ERR_CODE 11", "ISR_ERR_CODE 12","ISR_ERR_CODE 13","ISR_ERR_CODE 14","ISR_NO_ERR 15", "ISR_NO_ERR 16","ISR_ERR_CODE 17","ISR_NO_ERR 18","ISR_NO_ERR 19", "ISR_NO_ERR 20","ISR_ERR_CODE 21","ISR_NO_ERR 22","ISR_NO_ERR 23", "ISR_NO_ERR 24","ISR_NO_ERR 25","ISR_NO_ERR 26","ISR_NO_ERR 27", "ISR_NO_ERR 28","ISR_NO_ERR 29","ISR_ERR_CODE 30","ISR_NO_ERR 31", ".global default_isr_stub", "default_isr_stub:", "push 0", // dummy error code "push 255", // dummy vector "jmp default_isr_common", ".global default_isr_common", "default_isr_common:", // Stack at this point (top to bottom): // RSP+0: vector // RSP+8: error_code // RSP+16: RIP (pushed by CPU) // RSP+24: CS // RSP+32: RFLAGS // RSP+40: RSP (if ring change) // RSP+48: SS (if ring change) "push rax", "push rcx", "push rdx", "push rsi", "push rdi", "push r8", "push r9", "push r10", "push r11", "mov rdi, rsp", "call raw_exception_handler", "pop r11", "pop r10", "pop r9", "pop r8", "pop rdi", "pop rsi", "pop rdx", "pop rcx", "pop rax", // skip vector + error_code from stack "add rsp, 16", "iretq" ); pub unsafe fn init() { let idt_ptr = core::ptr::addr_of_mut!(IDT) as *mut IdtEntry; // Install per-vector stubs for exceptions 0-31 extern "C" { fn isr_stub_0(); fn isr_stub_1(); fn isr_stub_2(); fn isr_stub_3(); fn isr_stub_4(); fn isr_stub_5(); fn isr_stub_6(); fn isr_stub_7(); fn isr_stub_8(); fn isr_stub_9(); fn isr_stub_10(); fn isr_stub_11(); fn isr_stub_12(); fn isr_stub_13(); fn isr_stub_14(); fn isr_stub_15(); fn isr_stub_16(); fn isr_stub_17(); fn isr_stub_18(); fn isr_stub_19(); fn isr_stub_20(); fn isr_stub_21(); fn isr_stub_22(); fn isr_stub_23(); fn isr_stub_24(); fn isr_stub_25(); fn isr_stub_26(); fn isr_stub_27(); fn isr_stub_28(); fn isr_stub_29(); fn isr_stub_30(); fn isr_stub_31(); } let exception_stubs: [u64; 32] = [ isr_stub_0 as u64, isr_stub_1 as u64, isr_stub_2 as u64, isr_stub_3 as u64, isr_stub_4 as u64, isr_stub_5 as u64, isr_stub_6 as u64, isr_stub_7 as u64, isr_stub_8 as u64, isr_stub_9 as u64, isr_stub_10 as u64, isr_stub_11 as u64, isr_stub_12 as u64, isr_stub_13 as u64, isr_stub_14 as u64, isr_stub_15 as u64, isr_stub_16 as u64, isr_stub_17 as u64, isr_stub_18 as u64, isr_stub_19 as u64, isr_stub_20 as u64, isr_stub_21 as u64, isr_stub_22 as u64, isr_stub_23 as u64, isr_stub_24 as u64, isr_stub_25 as u64, isr_stub_26 as u64, isr_stub_27 as u64, isr_stub_28 as u64, isr_stub_29 as u64, isr_stub_30 as u64, isr_stub_31 as u64, ]; for (i, &stub_addr) in exception_stubs.iter().enumerate() { (*idt_ptr.add(i)).set_handler(stub_addr, 0, 0); } // Fill 32..255 with default stub let stub_addr = default_isr_stub as *const () as usize as u64; for i in 32..256usize { (*idt_ptr.add(i)).set_handler(stub_addr, 0, 0); } // Set Timer Interrupt (Vector 32 / 0x20) let timer_addr = timer_isr_entry as *const () as usize as u64; (*idt_ptr.add(32)).set_handler(timer_addr, 0, 0); // Set Keyboard Interrupt (Vector 33 / 0x21) let kbd_addr = keyboard_isr_entry as *const () as usize as u64; (*idt_ptr.add(33)).set_handler(kbd_addr, 0, 0); // Set Mouse Interrupt (Vector 44 / 0x2C - Slave PIC IRQ 12) let mouse_addr = mouse_isr_entry as *const () as usize as u64; (*idt_ptr.add(44)).set_handler(mouse_addr, 0, 0); let descriptor = IdtDescriptor { limit: (size_of::<[IdtEntry; 256]>() - 1) as u16, base: idt_ptr as u64, }; core::arch::asm!("lidt [{0}]", in(reg) &descriptor, options(preserves_flags)); } #[repr(C)] pub struct ExceptionFrame { // Stack layout when raw_exception_handler is called (low addr = top of stack): // Pushed by default_isr_common: r11,r10,r9,r8,rdi,rsi,rdx,rcx,rax (in that push order) pub r11: u64, pub r10: u64, pub r9: u64, pub r8: u64, pub rdi: u64, pub rsi: u64, pub rdx: u64, pub rcx: u64, pub rax: u64, // Pushed by per-vector stub (in push order: vector first on top) pub vector: u64, pub error_code: u64, // Pushed by CPU hardware: pub rip: u64, pub cs: u64, pub rflags: u64, pub user_rsp: u64, // only present on ring transition pub ss: u64, } #[no_mangle] pub extern "C" fn raw_exception_handler(frame: *mut ExceptionFrame) { unsafe { let v = (*frame).vector; let ec = (*frame).error_code; let rip = (*frame).rip; let cs = (*frame).cs; let rflags = (*frame).rflags; let is_user = (cs & 3) == 3; let names = [ "#DE", "#DB", "NMI", "#BP", "#OF", "#BR", "#UD", "#NM", "#DF", "CSO", "#TS", "#NP", "#SS", "#GP", "#PF", "RES", "#MF", "#AC", "#MC", "#XM", "#VE", "#CP", "??" , "??" , "??" , "??" , "??" , "??" , "??" , "??" , "#SX", "??" , ]; let name = if v < 32 { names[v as usize] } else { "IRQ" }; if is_user { let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER); let cur_tid = sched.current_tid; let cur_name = sched.threads[cur_tid].name; let (sig_num, sig_name) = match v { 0 | 4 | 5 => (8, "SIGFPE"), // Divide by zero or overflow 6 => (4, "SIGILL"), // Invalid opcode 11 | 12 | 13 | 14 => (11, "SIGSEGV"), // Page fault or General protection _ => (7, "SIGBUS"), }; kprintln!("\n[SIGNAL] Process [TID {}] '{}' crashed with Signal {} ({}) at RIP={:#018x} ErrCode={:#x}", cur_tid, cur_name, sig_num, sig_name, rip, ec); if v == 14 { let cr2: u64; core::arch::asm!("mov {}, cr2", out(reg) cr2, options(nostack, preserves_flags)); kprintln!(" [SIGNAL] Faulting memory address (CR2): {:#018x}", cr2); } // Terminate only the crashed Ring 3 process sched.threads[cur_tid].state = crate::sched::thread::ThreadState::Dead; // Switch CPU to the next ready process so the OS and desktop stay alive! sched.schedule(); return; } kprintln!("\n[EXCEPTION] Kernel Panic! Vec={} ({}) ErrCode={:#x} RIP={:#018x} CS={:#x} RFLAGS={:#x}", v, name, ec, rip, cs, rflags); if v == 14 { let cr2: u64; core::arch::asm!("mov {}, cr2", out(reg) cr2, options(nostack, preserves_flags)); kprintln!(" #PF CR2 (faulting address): {:#018x}", cr2); } if v < 32 { kprintln!(" [HALT] Unrecoverable kernel exception - CPU halted."); loop { core::arch::asm!("cli; hlt", options(nostack, preserves_flags)); } } } } #[no_mangle] pub extern "C" fn timer_interrupt_handler() { unsafe { send_eoi(); let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER); sched.preempt_tick(); } }