215 lines
6.9 KiB
C
215 lines
6.9 KiB
C
/*
|
||
* 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);
|
||
}
|