Files
pepperOS/src/idt/idt.c
2026-02-07 02:18:15 +01:00

217 lines
6.1 KiB
C

/*
* @author xamidev <xamidev@riseup.net>
* @brief Interrupt Descriptor Table setup and dispatching
* @license GPL-3.0-only
*/
#include "idt.h"
#include <stdint.h>
#include <stddef.h>
#include "io/serial/serial.h"
#include "io/kbd/ps2.h"
#include <kernel.h>
#include <stdbool.h>
#include "sched/scheduler.h"
#include "config.h"
struct interrupt_descriptor idt[256];
struct idtr idt_reg;
// Address to our first interrupt handler
extern char vector_0_handler[];
// Timer ticks
extern uint64_t ticks;
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
{
uint64_t handler_addr = (uint64_t)handler;
struct interrupt_descriptor* entry = &idt[vector];
// Address is split in three parts so we right-shift progressively to get it all
entry->address_low = handler_addr & 0xFFFF;
entry->address_mid = (handler_addr >> 16) & 0xFFFF;
entry->address_high = handler_addr >> 32;
// Kernel code selector (as set in GDT)
entry->selector = 0x8;
// Interrupt gate, present, DPL (having: max DPL = 3)
entry->flags = 0b1110 | ((dpl & 0b11) << 5) | (1 << 7);
// We won't use IST for now
entry->ist = 0;
}
void idt_load(void* idt_addr)
{
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
idt_reg.limit = 0xFFF;
idt_reg.base = (uint64_t)idt_addr;
asm volatile("lidt %0" :: "m"(idt_reg));
}
void idt_init()
{
// We set 256 entries, but we have only the first few stubs.
// Undefined behavior?
for (size_t i=0; i<256; i++)
{
// Each vector handler is 16-byte aligned, so <vector_no>*16 = address of that handler
idt_set_entry(i, vector_0_handler + (i*16), 0);
}
idt_load(&idt);
DEBUG("IDT initialized");
}
static inline uint64_t read_cr2(void)
{
uint64_t val;
asm volatile ("mov %%cr2, %0" : "=r"(val));
return val;
}
static void page_fault_handler(struct cpu_status_t* ctx)
{
// It could be used to remap pages etc. to fix the fault, but right now what I'm more
// interested in is getting more info out of those numbers cause i'm lost each time i have
// to read all this mess
uint64_t cr2 = read_cr2();
DEBUG("\x1b[38;5;231mPage Fault at rip=0x%p, err=%u (%s%s%s%s%s%s%s%s) when accessing addr=0x%p\x1b[0m", ctx->iret_rip, ctx->error_code,
CHECK_BIT(ctx->error_code, 0) ? "PAGE_PROTECTION_VIOLATION " : "PAGE_NOT_PRESENT ",
CHECK_BIT(ctx->error_code, 1) ? "ON_WRITE " : "ON_READ ",
CHECK_BIT(ctx->error_code, 2) ? "IN_USER_MODE" : "IN_KERNEL_MODE",
CHECK_BIT(ctx->error_code, 3) ? " WAS_RESERVED" : "",
CHECK_BIT(ctx->error_code, 4) ? " ON_INSTRUCTION_FETCH" : "",
CHECK_BIT(ctx->error_code, 5) ? " PK_VIOLATION" : "",
CHECK_BIT(ctx->error_code, 6) ? " ON_SHADOWSTACK_ACCESS" : "",
CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "",
cr2);
panic(ctx, "page fault");
}
static void gp_fault_handler(struct cpu_status_t* ctx)
{
DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m",
ctx->iret_rip,
ctx->error_code,
(ctx->error_code == 0) ? "NOT_SEGMENT_RELATED" : "SEGMENT_RELATED");
// Segment-related
if (ctx->error_code != 0)
{
bool is_external = CHECK_BIT(ctx->error_code, 0);
// is it IDT, GDT, LDT?
uint8_t table = ctx->error_code & 0x6; // 0b110 (isolate table)
uint16_t index = ctx->error_code & 0xFFF8; // 13*1 1111111111111 + 000 = 1111111111111000
char* table_names[4] = {"GDT", "IDT", "LDT", "IDT"};
DEBUG("\x1b[38;5;231m%s in %s index %u\x1b[0m",
is_external ? "EXTERNAL" : "INTERNAL",
table_names[table],
index);
}
panic(ctx, "gp fault");
}
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
{
switch(context->vector_number)
{
case 0:
DEBUG("Divide Error!");
break;
case 1:
DEBUG("Debug Exception!");
break;
case 2:
DEBUG("NMI Interrupt!");
break;
case 3:
DEBUG("Breakpoint Interrupt!");
break;
case 4:
DEBUG("Overflow Trap!");
break;
case 5:
DEBUG("BOUND Range Exceeded!");
break;
case 6:
DEBUG("Invalid Opcode!");
break;
case 7:
DEBUG("Device Not Available!");
break;
case 8:
DEBUG("Double Fault!");
break;
case 9:
DEBUG("Coprocessor Segment Overrun!");
break;
case 10:
DEBUG("Invalid TSS!");
break;
case 11:
DEBUG("Segment Not Present!");
break;
case 12:
DEBUG("Stack-Segment Fault!");
break;
case 13:
gp_fault_handler(context);
break;
case 14:
// Better debugging for page faults...
page_fault_handler(context);
break;
case 15:
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
break;
case 16:
DEBUG("x87 Floating-Point Error!");
break;
case 17:
DEBUG("Alignment Check Fault!");
break;
case 18:
DEBUG("Machine Check!");
break;
case 19:
DEBUG("SIMD Floating-Point Exception!");
break;
case 20:
DEBUG("Virtualization Exception!");
break;
case 21:
DEBUG("Control Protection Exception!");
break;
case 32: // Timer Interrupt
ticks++;
if (ticks % SCHEDULER_QUANTUM == 0)
{
CLEAR_INTERRUPTS;
scheduler_schedule();
SET_INTERRUPTS;
}
// Send an EOI so that we can continue having interrupts
outb(0x20, 0x20);
break;
case 33:
DEBUG("Keyboard Interrupt");
keyboard_handler();
break;
default:
DEBUG("Unexpected interrupt");
break;
}
return context;
}