printf spinlock + remove DEPRECATED stuff + begin separating x86 stuff

This commit is contained in:
2026-03-20 09:01:57 +01:00
parent 424b4c4632
commit 3607a7179c
12 changed files with 77 additions and 138 deletions
+315
View File
@@ -0,0 +1,315 @@
;
; @author xamidev <xamidev@riseup.net>
; @brief Stub for Interrupt Descriptor Table handlers
; @license GPL-3.0-only
;
bits 64
extern interrupt_dispatch
global interrupt_stub
global vector_0_handler
global vector_1_handler
global vector_2_handler
global vector_3_handler
global vector_4_handler
global vector_5_handler
global vector_6_handler
global vector_7_handler
global vector_8_handler
global vector_9_handler
global vector_10_handler
global vector_11_handler
global vector_12_handler
global vector_13_handler
global vector_14_handler
global vector_15_handler
global vector_16_handler
global vector_17_handler
global vector_18_handler
global vector_19_handler
global vector_20_handler
global vector_21_handler
interrupt_stub:
; We'll push all general-purpose registers to the stack,
; so they're intact and don't bother the code that was
; executed when the interrupt happened.
; (except rsp because it will already be saved in the iret frame)
push qword rax
push qword rbx
push qword rcx
push qword rdx
push qword rsi
push qword rdi
;push qword rsp
push qword rbp
push qword r8
push qword r9
push qword r10
push qword r11
push qword r12
push qword r13
push qword r14
push qword r15
; Put stack pointer as first argument of our function
mov rdi, rsp
call interrupt_dispatch
; What the function returns (new stack pointer) is saved in rbp
mov rsp, rax
pop qword r15
pop qword r14
pop qword r13
pop qword r12
pop qword r11
pop qword r10
pop qword r9
pop qword r8
pop qword rbp
;pop qword rsp
pop qword rdi
pop qword rsi
pop qword rdx
pop qword rcx
pop qword rbx
pop qword rax
; Removing the error code and vector number so stack doesn't
; get corrupted
add rsp, 16
; Restore ss, rsp, rflags, cs, rip of code that was executing
; before the interrupt
iretq
; Vector handlers will be 16-byte aligned so that we can loop over them
; like <vector_no> * 16 to get each one's address
; Divide Error
align 16
vector_0_handler:
; error code (nothing, so we push a dummy 0 quadword, 64bits/8bytes long)
push qword 0
; vector number (so our interrupt stub knows which one it is)
push qword 0
jmp interrupt_stub
; Debug Exception
align 16
vector_1_handler:
push qword 0
push qword 1
jmp interrupt_stub
; NMI
align 16
vector_2_handler:
push qword 0
push qword 2
jmp interrupt_stub
; Breakpoint
align 16
vector_3_handler:
push qword 0
push qword 3
jmp interrupt_stub
; Overflow
align 16
vector_4_handler:
push qword 0
push qword 4
jmp interrupt_stub
; BOUND Range exceeded
align 16
vector_5_handler:
push qword 0
push qword 5
jmp interrupt_stub
; Invalid Opcode
align 16
vector_6_handler:
push qword 0
push qword 6
jmp interrupt_stub
; Device Not Available
align 16
vector_7_handler:
push qword 0
push qword 7
jmp interrupt_stub
; Double Fault
align 16
vector_8_handler:
; No error code, we only push vector number
push qword 8
jmp interrupt_stub
; Coprocessor Segment Overrun
align 16
vector_9_handler:
push qword 0
push qword 9
jmp interrupt_stub
; Invalid TSS
align 16
vector_10_handler:
push qword 10
jmp interrupt_stub
; Segment Not Present
align 16
vector_11_handler:
push qword 11
jmp interrupt_stub
; Stack-Segment Fault
align 16
vector_12_handler:
push qword 12
jmp interrupt_stub
; General Protection
align 16
vector_13_handler:
push qword 13
jmp interrupt_stub
; Page Fault
align 16
vector_14_handler:
push qword 14
jmp interrupt_stub
; Intel reserved
align 16
vector_15_handler:
push qword 0
push qword 15
jmp interrupt_stub
; x87 FPU Floating-Point Error
align 16
vector_16_handler:
push qword 0
push qword 16
jmp interrupt_stub
; Alignment Check
align 16
vector_17_handler:
push qword 17
jmp interrupt_stub
; Machine Check
align 16
vector_18_handler:
push qword 0
push qword 18
jmp interrupt_stub
; SIMD Floating-Point Exception
align 16
vector_19_handler:
push qword 0
push qword 19
jmp interrupt_stub
; Virtualization Exception
align 16
vector_20_handler:
push qword 0
push qword 20
jmp interrupt_stub
; Control Protection Exception
align 16
vector_21_handler:
push qword 21
jmp interrupt_stub
; The others are reserved (22->31) or external (32->255) interrupts
align 16
vector_22_handler:
push qword 0
push qword 22
jmp interrupt_stub
align 16
vector_23_handler:
push qword 0
push qword 23
jmp interrupt_stub
align 16
vector_24_handler:
push qword 0
push qword 24
jmp interrupt_stub
align 16
vector_25_handler:
push qword 0
push qword 25
jmp interrupt_stub
align 16
vector_26_handler:
push qword 0
push qword 26
jmp interrupt_stub
align 16
vector_27_handler:
push qword 0
push qword 27
jmp interrupt_stub
align 16
vector_28_handler:
push qword 0
push qword 28
jmp interrupt_stub
align 16
vector_29_handler:
push qword 0
push qword 29
jmp interrupt_stub
align 16
vector_30_handler:
push qword 0
push qword 30
jmp interrupt_stub
align 16
vector_31_handler:
push qword 0
push qword 31
jmp interrupt_stub
; PIT timer
align 16
vector_32_handler:
push qword 0
push qword 32
jmp interrupt_stub
; PS/2 Keyboard
align 16
vector_33_handler:
push qword 0
push qword 33
jmp interrupt_stub
+271
View File
@@ -0,0 +1,271 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Interrupt Descriptor Table setup and dispatching
* @license GPL-3.0-only
*/
#include <arch/x86.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>
#include <sched/process.h>
struct interrupt_descriptor idt[256];
struct idtr idt_reg;
// Address to our first interrupt handler
extern char vector_0_handler[];
// Timer ticks
extern volatile uint64_t ticks;
/*
* idt_set_entry - Sets an Interrupt Descriptor Table entry
* @vector: Vector number in the IDT
* @handler: Pointer to the executable Interrupt Service Routine
* @dpl: Desired privilege level
*/
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;
}
/*
* idt_load - Loads the Interrupt Descriptor Table
* @idt_addr: Address to the IDT
*/
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));
}
/*
* idt_init - Initializes the Interrupt Descriptor Table
*
* Sets all IDT entries and their corresponding service routines,
* then loads it.
*/
void idt_init()
{
for (size_t i=0; i<=KERNEL_IDT_ENTRIES; 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");
}
/*
* read_cr2 - Reads the CR2 register
*
* This function is useful because it gets the address
* that the CPU tried to access in the case of a #PF.
*
* Return:
* %val - CR2 register value
*/
static inline uint64_t read_cr2(void)
{
uint64_t val;
asm volatile ("mov %%cr2, %0" : "=r"(val));
return val;
}
/*
* page_fault_handler - Handler for #PF
* @ctx: CPU context
*
* Shows detail about a #PF, especially what instruction (RIP)
* caused it, and what address access (CR2) caused it.
* Also displays an interpretation of the thrown error code.
* Then halts the system. We could implement demand paging later.
*/
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");
}
/*
* gp_fault_handler - Handler for #GP
* @ctx: CPU context
*
* Shows detail about a General Protection Fault,
* and what may have caused it. Halts the system.
*/
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");
}
// DEBUG
void kbdproc_main(void* arg)
{
printf("Key pressed/released.\r\n");
}
/*
* interrupt_dispatch - Interrupt dispatcher
* @context: CPU context
*
* This function is where all interrupt routines go, after they passed
* through their corresponding vector handler in the IDT assembly stub.
* It catches all exceptions.
*
* Return:
* <context> - CPU context after interrupt
*/
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
{
if (context == NULL) {
panic(NULL, "Interrupt dispatch recieved NULL context!");
}
switch(context->vector_number) {
case 0:
panic(context, "Divide Error");
break;
case 1:
panic(context, "Debug Exception");
break;
case 2:
panic(context, "NMI Interrupt");
break;
case 3:
panic(context, "Breakpoint Interrupt");
break;
case 4:
panic(context, "Overflow Trap");
break;
case 5:
panic(context, "BOUND Range Exceeded");
break;
case 6:
panic(context, "Invalid Opcode");
break;
case 7:
panic(context, "Device Not Available");
break;
case 8:
panic(context, "Double Fault");
break;
case 9:
panic(context, "Coprocessor Segment Overrun");
break;
case 10:
panic(context, "Invalid TSS");
break;
case 11:
panic(context, "Segment Not Present");
break;
case 12:
panic(context, "Stack-Segment Fault");
break;
case 13:
gp_fault_handler(context);
break;
case 14:
page_fault_handler(context);
break;
case 15:
panic(context, "Intel Reserved Interrupt (Achievement unlocked: How Did We Get Here?)");
break;
case 16:
panic(context, "x87 Floating-Point Error");
break;
case 17:
panic(context, "Alignment Check Fault");
break;
case 18:
panic(context, "Machine Check");
break;
case 19:
panic(context, "SIMD Floating-Point Exception");
break;
case 20:
panic(context, "Virtualization Exception");
break;
case 21:
panic(context, "Control Protection Exception");
break;
case 32: // Timer Interrupt
ticks++;
// Send an EOI so that we can continue having interrupts
outb(0x20, 0x20);
if (ticks % SCHEDULER_QUANTUM == 0) {
return scheduler_schedule(context);
}
break;
case 33: // Keyboard Interrupt
keyboard_handler();
process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG
outb(0x20, 0x20);
break;
default:
DEBUG("Unexpected Interrupt");
break;
}
return context;
}