use spin::Lazy;
use spin::Mutex;
use x86_64::instructions::port::PortReadOnly;
use x86_64::registers::control::Cr2;
use x86_64::structures::idt::InterruptDescriptorTable;
use x86_64::structures::idt::InterruptStackFrame;
use x86_64::structures::idt::PageFaultErrorCode;
use x86_64::VirtAddr;
use super::gdt::DOUBLE_FAULT_IST_INDEX;
use crate::arch::apic::get_lapic_id;
use crate::task::scheduler::SCHEDULER;
const INTERRUPT_INDEX_OFFSET: u8 = 32;
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum InterruptIndex {
Timer = INTERRUPT_INDEX_OFFSET,
ApicError,
ApicSpurious,
Keyboard,
Mouse,
}
macro_rules! interrupt_handler {
($k: expr) => {{
extern "x86-interrupt" fn default(frame: InterruptStackFrame) {
IRQ_HANDLER.lock()($k as usize, frame);
}
default
}};
}
macro_rules! interrupt_handler10 {
($k: expr, $idt: expr) => {
$idt[$k * 10 + 0 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 1 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 2 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 3 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 4 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 5 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 6 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 7 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 8 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
$idt[$k * 10 + 9 + INTERRUPT_INDEX_OFFSET].set_handler_fn(interrupt_handler!($k * 10 + 0));
};
}
pub static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint);
idt.segment_not_present.set_handler_fn(segment_not_present);
idt.invalid_opcode.set_handler_fn(invalid_opcode);
idt.page_fault.set_handler_fn(page_fault);
idt.general_protection_fault
.set_handler_fn(general_protection_fault);
interrupt_handler10!(0,idt);
interrupt_handler10!(1,idt);
interrupt_handler10!(2,idt);
interrupt_handler10!(3,idt);
interrupt_handler10!(4,idt);
interrupt_handler10!(5,idt);
interrupt_handler10!(6,idt);
interrupt_handler10!(7,idt);
interrupt_handler10!(8,idt);
interrupt_handler10!(9,idt);
interrupt_handler10!(10,idt);
interrupt_handler10!(11,idt);
interrupt_handler10!(12,idt);
interrupt_handler10!(13,idt);
interrupt_handler10!(14,idt);
interrupt_handler10!(15,idt);
interrupt_handler10!(16,idt);
interrupt_handler10!(17,idt);
interrupt_handler10!(18,idt);
interrupt_handler10!(19,idt);
interrupt_handler10!(20,idt);
interrupt_handler10!(21,idt);
idt[InterruptIndex::Timer as u8].set_handler_fn(timer_interrupt);
idt[InterruptIndex::ApicError as u8].set_handler_fn(lapic_error);
idt[InterruptIndex::ApicSpurious as u8].set_handler_fn(spurious_interrupt);
idt[InterruptIndex::Keyboard as u8].set_handler_fn(keyboard_interrupt);
idt[InterruptIndex::Mouse as u8].set_handler_fn(mouse_interrupt);
unsafe {
idt.double_fault
.set_handler_fn(double_fault)
.set_stack_index(DOUBLE_FAULT_IST_INDEX as u16);
}
return idt;
});
#[naked]
extern "x86-interrupt" fn timer_interrupt(_frame: InterruptStackFrame) {
fn timer_handler(context: VirtAddr) -> VirtAddr {
super::apic::end_of_interrupt();
let mut scheduler = SCHEDULER.lock();
let address = scheduler.schedule(context);
address
}
unsafe {
core::arch::asm!(
"cli",
crate::push_context!(),
"mov rdi, rsp",
"call {timer_handler}",
"mov rsp, rax",
crate::pop_context!(),
"sti",
"iretq",
timer_handler = sym timer_handler,
options(noreturn)
);
}
}
extern "x86-interrupt" fn lapic_error(_frame: InterruptStackFrame) {
log::error!("Local APIC error!");
super::apic::end_of_interrupt();
}
extern "x86-interrupt" fn spurious_interrupt(_frame: InterruptStackFrame) {
log::debug!("Received spurious interrupt!");
super::apic::end_of_interrupt();
}
extern "x86-interrupt" fn segment_not_present(frame: InterruptStackFrame, error_code: u64) {
log::error!("Exception: Segment Not Present\n{:#?}", frame);
log::error!("Error Code: {:#x}", error_code);
panic!("Unrecoverable fault occured, halting!");
}
extern "x86-interrupt" fn general_protection_fault(frame: InterruptStackFrame, error_code: u64) {
log::error!("Exception: General Protection Fault\n{:#?}", frame);
log::error!("Error Code: {:#x}", error_code);
if (error_code & 0x1) != 0 {
log::error!("The exception occurred during delivery of an event external to the program,such as an interrupt or an earlier exception.");
}
if (error_code & 0x2) != 0 {
log::error!("Refers to a gate descriptor in the IDT")
} else {
if (error_code & 0x4) != 0 {
log::error!("Refers to a segment or gate descriptor in the LDT");
} else {
log::error!("Refers to a segment or gate descriptor in the LDT");
}
}
log::error!("Segment Selector Index: {}", error_code & 0xfff8);
x86_64::instructions::hlt();
}
extern "x86-interrupt" fn invalid_opcode(frame: InterruptStackFrame) {
log::error!("Exception: Invalid Opcode\n{:#?}", frame);
x86_64::instructions::hlt();
}
extern "x86-interrupt" fn breakpoint(frame: InterruptStackFrame) {
log::debug!("Exception: Breakpoint\n{:#?}", frame);
}
extern "x86-interrupt" fn double_fault(frame: InterruptStackFrame, error_code: u64) -> ! {
log::error!("Exception: Double Fault\n{:#?}", frame);
log::error!("Error Code: {:#x}", error_code);
panic!("Unrecoverable fault occured, halting!");
}
extern "x86-interrupt" fn keyboard_interrupt(_frame: InterruptStackFrame) {
let scancode: u8 = unsafe { PortReadOnly::new(0x60).read() };
crate::drivers::keyboard::add_scancode(scancode);
super::apic::end_of_interrupt();
}
extern "x86-interrupt" fn mouse_interrupt(_frame: InterruptStackFrame) {
let packet = unsafe { PortReadOnly::new(0x60).read() };
crate::drivers::mouse::MOUSE.lock().process_packet(packet);
super::apic::end_of_interrupt();
}
extern "x86-interrupt" fn page_fault(frame: InterruptStackFrame, error_code: PageFaultErrorCode) {
log::warn!("Processor: {}", get_lapic_id());
log::warn!("Exception: Page Fault\n{:#?}", frame);
log::warn!("Error Code: {:?}", error_code);
match Cr2::read() {
Ok(address) => {
log::warn!("Fault Address: {:#x}", address);
}
Err(error) => {
log::warn!("Invalid virtual address: {:?}", error);
}
}
x86_64::instructions::hlt();
}
pub type IrqHandler = fn(irq: usize, frame: InterruptStackFrame);
pub static IRQ_HANDLER: Mutex<IrqHandler> = Mutex::new(default_irq_handler);
pub fn default_irq_handler(_irq: usize, _frame: InterruptStackFrame) {
log::warn!("Unhandled IRQ!");
}
pub fn register_irq_handler(handler: IrqHandler) {
*IRQ_HANDLER.lock() = handler;
}