/* * idt.c – Interrupt Descriptor Table setup and exception handling. * * On initialisation the kernel copies the firmware's IDT (preserving * its hardware IRQ handlers for vectors 32-47), then overrides CPU * exception vectors 0-31 with our own stubs from isr.S. * * Hardware IRQs receive an EOI and return. CPU exceptions print * diagnostic information (including CR2 for page faults) and halt. */ #include "idt.h" /* ================================================================ * Constants and internal types * ================================================================ */ #define IDT_SIZE 256 #define IDT_TYPE_INTERRUPT 0x8E /* P=1, DPL=0, 64-bit interrupt gate */ /* Single 16-byte IDT entry (x86-64 long mode). */ typedef struct { UINT16 offset_low; /* bits 0-15 of handler address */ UINT16 selector; /* code segment selector */ UINT8 ist; /* interrupt stack table index */ UINT8 type_attr; /* type and attributes */ UINT16 offset_mid; /* bits 16-31 of handler address */ UINT32 offset_high; /* bits 32-63 of handler address */ UINT32 zero; /* reserved, must be zero */ } __attribute__((packed)) IdtEntry; /* IDTR register layout for the LIDT instruction. */ typedef struct { UINT16 limit; UINT64 base; } __attribute__((packed)) IdtPtr; /* ================================================================ * Module state * ================================================================ */ static IdtEntry idt[IDT_SIZE]; static BootInfo *gBoot = NULL; /* Defined in isr.S – one stub function per vector (0-255). */ extern void (*isr_stub_table[])(void); /* ================================================================ * IDT gate helpers * ================================================================ */ /* Install a single IDT gate pointing at `handler`. */ static void idt_set_gate(UINTN index, void (*handler)(void)) { UINT64 addr = (UINT64)(UINTN)handler; UINT16 selector = 0; __asm__ __volatile__("mov %%cs, %0" : "=r"(selector)); idt[index].offset_low = (UINT16)(addr & 0xFFFF); idt[index].selector = selector; idt[index].ist = 0; idt[index].type_attr = IDT_TYPE_INTERRUPT; idt[index].offset_mid = (UINT16)((addr >> 16) & 0xFFFF); idt[index].offset_high = (UINT32)((addr >> 32) & 0xFFFFFFFF); idt[index].zero = 0; } /* Load a new IDT register value. */ static void lidt(const IdtPtr *idtr) { __asm__ __volatile__("lidt (%0)" :: "r"(idtr)); } /* ================================================================ * Exception name lookup * ================================================================ */ /* Return a human-readable name for CPU exception vectors 0-31. */ static const CHAR16 *exception_name(UINTN vector) { switch (vector) { case 0: return L"Divide Error"; case 1: return L"Debug"; case 2: return L"Non-Maskable Interrupt"; case 3: return L"Breakpoint"; case 4: return L"Overflow"; case 5: return L"Bound Range Exceeded"; case 6: return L"Invalid Opcode"; case 7: return L"Device Not Available"; case 8: return L"Double Fault"; case 9: return L"Coprocessor Segment Overrun"; case 10: return L"Invalid TSS"; case 11: return L"Segment Not Present"; case 12: return L"Stack-Segment Fault"; case 13: return L"General Protection Fault"; case 14: return L"Page Fault"; case 15: return L"Reserved"; case 16: return L"x87 Floating-Point"; case 17: return L"Alignment Check"; case 18: return L"Machine Check"; case 19: return L"SIMD Floating-Point"; case 20: return L"Virtualization"; case 21: return L"Control Protection"; case 22: return L"Reserved"; case 23: return L"Reserved"; case 24: return L"Reserved"; case 25: return L"Reserved"; case 26: return L"Reserved"; case 27: return L"Reserved"; case 28: return L"Hypervisor Injection"; case 29: return L"VMM Communication"; case 30: return L"Security"; case 31: return L"Reserved"; default: return L"Unknown"; } } /* ================================================================ * PIC and low-level helpers * ================================================================ */ /* Write a byte to an I/O port. */ static inline void outb(UINT16 port, UINT8 value) { __asm__ __volatile__("outb %0, %1" :: "a"(value), "Nd"(port)); } /* Send End-Of-Interrupt to the 8259 PIC(s). */ static void pic_eoi(UINTN vector) { if (vector >= 40) { outb(0xA0, 0x20); /* EOI to slave PIC */ } outb(0x20, 0x20); /* EOI to master PIC */ } /* Disable interrupts and halt forever (unrecoverable fault). */ static void halt_forever(void) { for (;;) { __asm__ __volatile__("cli; hlt"); } } /* ================================================================ * ISR dispatcher (called from isr_common in isr.S) * ================================================================ */ void isr_handler(ISRFrame *frame) { UINT64 cr2 = 0; /* Hardware IRQs (vectors 32-47): send EOI and return */ if (frame->vector >= 32 && frame->vector <= 47) { pic_eoi(frame->vector); return; } /* CPU exceptions (vectors 0-31): print diagnostics and halt */ if (gBoot != NULL && gBoot->print != NULL) { gBoot->print(L"\n\rEXCEPTION: %d (%s)\n\r", frame->vector, exception_name(frame->vector)); gBoot->print(L" Error Code: 0x%lx\n\r", frame->error_code); gBoot->print(L" RIP: 0x%lx CS: 0x%lx RFLAGS: 0x%lx\n\r", frame->rip, frame->cs, frame->rflags); } if (frame->vector == 14) { __asm__ __volatile__("mov %%cr2, %0" : "=r"(cr2)); if (gBoot != NULL && gBoot->print != NULL) { gBoot->print(L" CR2: 0x%lx\n\r", cr2); } } halt_forever(); } /* ================================================================ * IDT initialisation * ================================================================ */ void idt_init(BootInfo *Boot) { IdtPtr old_idtr; IdtPtr idtr; IdtEntry *old_idt = NULL; UINTN i = 0; gBoot = Boot; /* Read the firmware's existing IDT so we can preserve its entries */ __asm__ __volatile__("sidt %0" : "=m"(old_idtr)); old_idt = (IdtEntry *)(UINTN)old_idtr.base; /* Copy the entire existing IDT first (preserves firmware IRQ handlers) */ for (i = 0; i < IDT_SIZE; i++) { if (old_idt != NULL && (i * sizeof(IdtEntry)) < (UINTN)(old_idtr.limit + 1)) { idt[i] = old_idt[i]; } else { idt_set_gate(i, isr_stub_table[i]); } } /* Override only CPU exception vectors (0-31) with our handlers */ for (i = 0; i < 32; i++) { idt_set_gate(i, isr_stub_table[i]); } idtr.limit = (UINT16)(sizeof(idt) - 1); idtr.base = (UINT64)(UINTN)idt; lidt(&idtr); }