use alloc::sync::Arc; use arch::{ExtendedContext, InterruptContext}; use lazy_static::lazy_static; use crate::bindings::root::EINVAL; use crate::{driver::Port8, prelude::*}; use super::cpu::current_cpu; use super::mem::handle_page_fault; use super::syscall::handle_syscall32; use super::task::{ProcessList, Signal}; use super::timer::timer_interrupt; const PIC1_COMMAND: Port8 = Port8::new(0x20); const PIC1_DATA: Port8 = Port8::new(0x21); const PIC2_COMMAND: Port8 = Port8::new(0xA0); const PIC2_DATA: Port8 = Port8::new(0xA1); lazy_static! { static ref IRQ_HANDLERS: Spin<[Option>; 16]> = Spin::new([const { None }; 16]); } fn irq_handler(irqno: usize) { assert!(irqno < 16); let handler = IRQ_HANDLERS.lock()[irqno as usize].as_ref().cloned(); if let Some(handler) = handler { handler(); } PIC1_COMMAND.write(0x20); // EOI if irqno >= 8 { PIC2_COMMAND.write(0x20); // EOI } } fn fault_handler(int_stack: &mut InterruptContext) { match int_stack.int_no { // Invalid Op or Double Fault 14 => handle_page_fault(int_stack), 13 if int_stack.ss == 0 => ProcessList::kill_current(Signal::SIGILL), 6 | 8 if int_stack.ss == 0 => ProcessList::kill_current(Signal::SIGSEGV), _ => panic!("Unhandled fault: {}", int_stack.int_no), } } #[no_mangle] pub extern "C" fn interrupt_handler( int_stack: *mut InterruptContext, ext_ctx: *mut ExtendedContext, ) { let int_stack = unsafe { &mut *int_stack }; let ext_ctx = unsafe { &mut *ext_ctx }; match int_stack.int_no { // Fault 0..0x20 => fault_handler(int_stack), // Syscall 0x80 => handle_syscall32(int_stack.rax as usize, int_stack, ext_ctx), // Timer 0x40 => timer_interrupt(), // IRQ no => irq_handler(no as usize - 0x20), } } pub fn register_irq_handler(irqno: i32, handler: F) -> Result<(), u32> where F: Fn() + Send + Sync + 'static, { if irqno < 0 || irqno >= 16 { return Err(EINVAL); } let old = IRQ_HANDLERS.lock_irq()[irqno as usize].replace(Arc::new(handler)); assert!(old.is_none(), "IRQ handler already registered"); Ok(()) } pub fn init() -> KResult<()> { // Initialize PIC PIC1_COMMAND.write(0x11); // edge trigger mode PIC1_DATA.write(0x20); // IRQ 0-7 offset PIC1_DATA.write(0x04); // cascade with slave PIC PIC1_DATA.write(0x01); // no buffer mode PIC2_COMMAND.write(0x11); // edge trigger mode PIC2_DATA.write(0x28); // IRQ 8-15 offset PIC2_DATA.write(0x02); // cascade with master PIC PIC2_DATA.write(0x01); // no buffer mode // Allow all IRQs PIC1_DATA.write(0x0); PIC2_DATA.write(0x0); Ok(()) } pub fn end_of_interrupt() { // SAFETY: We only use this function in irq context, where preemption is disabled. unsafe { current_cpu() }.interrupt.end_of_interrupt(); }