Files
Operator-system/idt.c

215 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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);
}