From e9a813a1239198e16de62bc0a19589caa9ecc612 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Thu, 9 Apr 2026 22:12:29 +0800 Subject: [PATCH] Stage 3 --- Makefile | 17 +- clks/arch/x86_64/interrupt_stubs.S | 117 +++++++++++ clks/include/clks/interrupts.h | 9 + clks/kernel/interrupts.c | 313 +++++++++++++++++++++++++++++ clks/kernel/kmain.c | 6 +- docs/stage3.md | 34 ++++ 6 files changed, 493 insertions(+), 3 deletions(-) create mode 100644 clks/arch/x86_64/interrupt_stubs.S create mode 100644 clks/include/clks/interrupts.h create mode 100644 clks/kernel/interrupts.c create mode 100644 docs/stage3.md diff --git a/Makefile b/Makefile index e67df43..7995a3c 100644 --- a/Makefile +++ b/Makefile @@ -63,22 +63,29 @@ define log_step @printf '%b\n' "$(COLOR_STEP)[STEP]$(COLOR_RESET) $(1)" endef -SOURCES := \ +C_SOURCES := \ clks/kernel/kmain.c \ clks/kernel/log.c \ clks/kernel/limine_requests.c \ clks/kernel/tty.c \ clks/kernel/pmm.c \ clks/kernel/heap.c \ + clks/kernel/interrupts.c \ clks/lib/string.c \ clks/drivers/serial/serial.c \ clks/drivers/video/framebuffer.c \ clks/drivers/video/font8x8.c \ clks/arch/x86_64/boot.c -OBJECTS := $(patsubst %.c,$(OBJ_ROOT)/%.o,$(SOURCES)) +ASM_SOURCES := \ + clks/arch/x86_64/interrupt_stubs.S + +C_OBJECTS := $(patsubst %.c,$(OBJ_ROOT)/%.o,$(C_SOURCES)) +ASM_OBJECTS := $(patsubst %.S,$(OBJ_ROOT)/%.o,$(ASM_SOURCES)) +OBJECTS := $(C_OBJECTS) $(ASM_OBJECTS) CFLAGS_COMMON := -std=c11 -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Iclks/include +ASFLAGS_COMMON := -ffreestanding -Iclks/include LDFLAGS_COMMON := -nostdlib -z max-page-size=0x1000 .PHONY: all setup setup-tools setup-limine kernel ramdisk iso run debug clean clean-all help @@ -165,6 +172,12 @@ $(OBJ_ROOT)/%.o: %.c Makefile > @mkdir -p $(dir $@) > @$(CC) $(CFLAGS_COMMON) $(ARCH_CFLAGS) -c $< -o $@ +$(OBJ_ROOT)/%.o: %.S Makefile +> $(call log_step,assembling $<) +> @mkdir -p $(dir $@) +> @$(CC) $(ASFLAGS_COMMON) $(ARCH_CFLAGS) -c $< -o $@ + + $(RAMDISK_IMAGE): > $(call log_step,packing ramdisk -> $(RAMDISK_IMAGE)) > @mkdir -p $(dir $@) diff --git a/clks/arch/x86_64/interrupt_stubs.S b/clks/arch/x86_64/interrupt_stubs.S new file mode 100644 index 0000000..02f367b --- /dev/null +++ b/clks/arch/x86_64/interrupt_stubs.S @@ -0,0 +1,117 @@ +.intel_syntax noprefix + +.global clks_isr_stub_default +.extern clks_interrupt_dispatch + +.macro ISR_NOERR num +.global clks_isr_stub_\num +clks_isr_stub_\num: + push 0 + push \num + jmp clks_isr_common +.endm + +.macro ISR_ERR num +.global clks_isr_stub_\num +clks_isr_stub_\num: + push \num + jmp clks_isr_common +.endm + +clks_isr_stub_default: + push 0 + push 255 + jmp clks_isr_common + +ISR_NOERR 0 +ISR_NOERR 1 +ISR_NOERR 2 +ISR_NOERR 3 +ISR_NOERR 4 +ISR_NOERR 5 +ISR_NOERR 6 +ISR_NOERR 7 +ISR_ERR 8 +ISR_NOERR 9 +ISR_ERR 10 +ISR_ERR 11 +ISR_ERR 12 +ISR_ERR 13 +ISR_ERR 14 +ISR_NOERR 15 +ISR_NOERR 16 +ISR_ERR 17 +ISR_NOERR 18 +ISR_NOERR 19 +ISR_NOERR 20 +ISR_ERR 21 +ISR_NOERR 22 +ISR_NOERR 23 +ISR_NOERR 24 +ISR_NOERR 25 +ISR_NOERR 26 +ISR_NOERR 27 +ISR_NOERR 28 +ISR_ERR 29 +ISR_ERR 30 +ISR_NOERR 31 + +ISR_NOERR 32 +ISR_NOERR 33 +ISR_NOERR 34 +ISR_NOERR 35 +ISR_NOERR 36 +ISR_NOERR 37 +ISR_NOERR 38 +ISR_NOERR 39 +ISR_NOERR 40 +ISR_NOERR 41 +ISR_NOERR 42 +ISR_NOERR 43 +ISR_NOERR 44 +ISR_NOERR 45 +ISR_NOERR 46 +ISR_NOERR 47 + +clks_isr_common: + cld + + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rbp + push rdi + push rsi + push rdx + push rcx + push rbx + push rax + + mov rdi, rsp + sub rsp, 8 + call clks_interrupt_dispatch + add rsp, 8 + + pop rax + pop rbx + pop rcx + pop rdx + pop rsi + pop rdi + pop rbp + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + add rsp, 16 + iretq \ No newline at end of file diff --git a/clks/include/clks/interrupts.h b/clks/include/clks/interrupts.h new file mode 100644 index 0000000..17e8761 --- /dev/null +++ b/clks/include/clks/interrupts.h @@ -0,0 +1,9 @@ +#ifndef CLKS_INTERRUPTS_H +#define CLKS_INTERRUPTS_H + +#include + +void clks_interrupts_init(void); +u64 clks_interrupts_timer_ticks(void); + +#endif \ No newline at end of file diff --git a/clks/kernel/interrupts.c b/clks/kernel/interrupts.c new file mode 100644 index 0000000..4ebfc29 --- /dev/null +++ b/clks/kernel/interrupts.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include + +#define CLKS_IDT_ENTRY_COUNT 256U +#define CLKS_INTERRUPT_GATE 0x8EU + +#define CLKS_PIC1_CMD 0x20U +#define CLKS_PIC1_DATA 0x21U +#define CLKS_PIC2_CMD 0xA0U +#define CLKS_PIC2_DATA 0xA1U +#define CLKS_PIC_EOI 0x20U + +#define CLKS_IRQ_BASE 32U +#define CLKS_IRQ_TIMER 32U +#define CLKS_IRQ_LAST 47U + +struct clks_idt_entry { + u16 offset_low; + u16 selector; + u8 ist; + u8 type_attr; + u16 offset_mid; + u32 offset_high; + u32 zero; +} CLKS_PACKED; + +struct clks_idtr { + u16 limit; + u64 base; +} CLKS_PACKED; + +struct clks_interrupt_frame { + u64 rax; + u64 rbx; + u64 rcx; + u64 rdx; + u64 rsi; + u64 rdi; + u64 rbp; + u64 r8; + u64 r9; + u64 r10; + u64 r11; + u64 r12; + u64 r13; + u64 r14; + u64 r15; + u64 vector; + u64 error_code; + u64 rip; + u64 cs; + u64 rflags; + u64 rsp; + u64 ss; +}; + +extern void clks_isr_stub_default(void); +extern void clks_isr_stub_0(void); +extern void clks_isr_stub_1(void); +extern void clks_isr_stub_2(void); +extern void clks_isr_stub_3(void); +extern void clks_isr_stub_4(void); +extern void clks_isr_stub_5(void); +extern void clks_isr_stub_6(void); +extern void clks_isr_stub_7(void); +extern void clks_isr_stub_8(void); +extern void clks_isr_stub_9(void); +extern void clks_isr_stub_10(void); +extern void clks_isr_stub_11(void); +extern void clks_isr_stub_12(void); +extern void clks_isr_stub_13(void); +extern void clks_isr_stub_14(void); +extern void clks_isr_stub_15(void); +extern void clks_isr_stub_16(void); +extern void clks_isr_stub_17(void); +extern void clks_isr_stub_18(void); +extern void clks_isr_stub_19(void); +extern void clks_isr_stub_20(void); +extern void clks_isr_stub_21(void); +extern void clks_isr_stub_22(void); +extern void clks_isr_stub_23(void); +extern void clks_isr_stub_24(void); +extern void clks_isr_stub_25(void); +extern void clks_isr_stub_26(void); +extern void clks_isr_stub_27(void); +extern void clks_isr_stub_28(void); +extern void clks_isr_stub_29(void); +extern void clks_isr_stub_30(void); +extern void clks_isr_stub_31(void); +extern void clks_isr_stub_32(void); +extern void clks_isr_stub_33(void); +extern void clks_isr_stub_34(void); +extern void clks_isr_stub_35(void); +extern void clks_isr_stub_36(void); +extern void clks_isr_stub_37(void); +extern void clks_isr_stub_38(void); +extern void clks_isr_stub_39(void); +extern void clks_isr_stub_40(void); +extern void clks_isr_stub_41(void); +extern void clks_isr_stub_42(void); +extern void clks_isr_stub_43(void); +extern void clks_isr_stub_44(void); +extern void clks_isr_stub_45(void); +extern void clks_isr_stub_46(void); +extern void clks_isr_stub_47(void); + +static struct clks_idt_entry clks_idt[CLKS_IDT_ENTRY_COUNT]; +static u16 clks_idt_code_selector = 0x08U; +static u64 clks_timer_ticks = 0; + +static const char *clks_exception_names[32] = { + "DE DIVIDE ERROR", + "DB DEBUG", + "NMI", + "BP BREAKPOINT", + "OF OVERFLOW", + "BR BOUND RANGE", + "UD INVALID OPCODE", + "NM DEVICE NOT AVAILABLE", + "DF DOUBLE FAULT", + "COPROCESSOR SEGMENT", + "TS INVALID TSS", + "NP SEGMENT NOT PRESENT", + "SS STACK SEGMENT", + "GP GENERAL PROTECTION", + "PF PAGE FAULT", + "RESERVED", + "MF X87 FLOAT", + "AC ALIGNMENT CHECK", + "MC MACHINE CHECK", + "XF SIMD FLOAT", + "VE VIRT EXCEPTION", + "CP CONTROL PROTECTION", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "HV HYPERVISOR", + "VC VMM COMM", + "SX SECURITY", + "RESERVED" +}; + +static inline void clks_outb(u16 port, u8 value) { + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +static inline u8 clks_inb(u16 port) { + u8 value; + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void clks_io_wait(void) { + __asm__ volatile("outb %%al, $0x80" : : "a"(0)); +} + +static void clks_pic_remap_and_mask(void) { + u8 master_mask = clks_inb(CLKS_PIC1_DATA); + u8 slave_mask = clks_inb(CLKS_PIC2_DATA); + + clks_outb(CLKS_PIC1_CMD, 0x11); + clks_io_wait(); + clks_outb(CLKS_PIC2_CMD, 0x11); + clks_io_wait(); + + clks_outb(CLKS_PIC1_DATA, CLKS_IRQ_BASE); + clks_io_wait(); + clks_outb(CLKS_PIC2_DATA, CLKS_IRQ_BASE + 8U); + clks_io_wait(); + + clks_outb(CLKS_PIC1_DATA, 4U); + clks_io_wait(); + clks_outb(CLKS_PIC2_DATA, 2U); + clks_io_wait(); + + clks_outb(CLKS_PIC1_DATA, 0x01); + clks_io_wait(); + clks_outb(CLKS_PIC2_DATA, 0x01); + clks_io_wait(); + + (void)master_mask; + (void)slave_mask; + + clks_outb(CLKS_PIC1_DATA, 0xFEU); + clks_outb(CLKS_PIC2_DATA, 0xFFU); +} + +static void clks_pic_send_eoi(u64 vector) { + if (vector >= 40U) { + clks_outb(CLKS_PIC2_CMD, CLKS_PIC_EOI); + } + + clks_outb(CLKS_PIC1_CMD, CLKS_PIC_EOI); +} + +static void clks_idt_set_gate(u8 vector, void (*handler)(void), u8 flags) { + u64 addr = (u64)handler; + + clks_idt[vector].offset_low = (u16)(addr & 0xFFFFULL); + clks_idt[vector].selector = clks_idt_code_selector; + clks_idt[vector].ist = 0; + clks_idt[vector].type_attr = flags; + clks_idt[vector].offset_mid = (u16)((addr >> 16) & 0xFFFFULL); + clks_idt[vector].offset_high = (u32)((addr >> 32) & 0xFFFFFFFFULL); + clks_idt[vector].zero = 0; +} + +static void clks_load_idt(void) { + struct clks_idtr idtr; + + idtr.limit = (u16)(sizeof(clks_idt) - 1U); + idtr.base = (u64)&clks_idt[0]; + + __asm__ volatile("lidt %0" : : "m"(idtr)); +} + +static void clks_enable_interrupts(void) { + __asm__ volatile("sti"); +} + +void clks_interrupt_dispatch(struct clks_interrupt_frame *frame) { + u64 vector = frame->vector; + + if (vector < 32U) { + clks_log(CLKS_LOG_ERROR, "EXC", clks_exception_names[vector]); + clks_log_hex(CLKS_LOG_ERROR, "EXC", "VECTOR", vector); + clks_log_hex(CLKS_LOG_ERROR, "EXC", "ERROR", frame->error_code); + clks_log_hex(CLKS_LOG_ERROR, "EXC", "RIP", frame->rip); + clks_cpu_halt_forever(); + } + + if (vector == CLKS_IRQ_TIMER) { + clks_timer_ticks++; + } + + if (vector >= CLKS_IRQ_BASE && vector <= CLKS_IRQ_LAST) { + clks_pic_send_eoi(vector); + } +} + +void clks_interrupts_init(void) { + u32 i; + + __asm__ volatile("mov %%cs, %0" : "=r"(clks_idt_code_selector)); + + for (i = 0; i < CLKS_IDT_ENTRY_COUNT; i++) { + clks_idt_set_gate((u8)i, clks_isr_stub_default, CLKS_INTERRUPT_GATE); + } + + clks_idt_set_gate(0, clks_isr_stub_0, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(1, clks_isr_stub_1, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(2, clks_isr_stub_2, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(3, clks_isr_stub_3, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(4, clks_isr_stub_4, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(5, clks_isr_stub_5, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(6, clks_isr_stub_6, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(7, clks_isr_stub_7, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(8, clks_isr_stub_8, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(9, clks_isr_stub_9, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(10, clks_isr_stub_10, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(11, clks_isr_stub_11, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(12, clks_isr_stub_12, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(13, clks_isr_stub_13, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(14, clks_isr_stub_14, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(15, clks_isr_stub_15, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(16, clks_isr_stub_16, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(17, clks_isr_stub_17, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(18, clks_isr_stub_18, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(19, clks_isr_stub_19, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(20, clks_isr_stub_20, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(21, clks_isr_stub_21, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(22, clks_isr_stub_22, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(23, clks_isr_stub_23, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(24, clks_isr_stub_24, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(25, clks_isr_stub_25, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(26, clks_isr_stub_26, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(27, clks_isr_stub_27, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(28, clks_isr_stub_28, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(29, clks_isr_stub_29, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(30, clks_isr_stub_30, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(31, clks_isr_stub_31, CLKS_INTERRUPT_GATE); + + clks_idt_set_gate(32, clks_isr_stub_32, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(33, clks_isr_stub_33, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(34, clks_isr_stub_34, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(35, clks_isr_stub_35, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(36, clks_isr_stub_36, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(37, clks_isr_stub_37, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(38, clks_isr_stub_38, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(39, clks_isr_stub_39, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(40, clks_isr_stub_40, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(41, clks_isr_stub_41, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(42, clks_isr_stub_42, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(43, clks_isr_stub_43, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(44, clks_isr_stub_44, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(45, clks_isr_stub_45, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(46, clks_isr_stub_46, CLKS_INTERRUPT_GATE); + clks_idt_set_gate(47, clks_isr_stub_47, CLKS_INTERRUPT_GATE); + + clks_pic_remap_and_mask(); + clks_load_idt(); + clks_enable_interrupts(); +} + +u64 clks_interrupts_timer_ticks(void) { + return clks_timer_ticks; +} \ No newline at end of file diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 8addd11..9add0d4 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE2 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE3 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -77,6 +78,9 @@ void clks_kernel_main(void) { clks_kfree(heap_probe); } + clks_interrupts_init(); + clks_log(CLKS_LOG_INFO, "INT", "IDT + PIC INITIALIZED"); + clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY"); clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER"); diff --git a/docs/stage3.md b/docs/stage3.md new file mode 100644 index 0000000..dbf4076 --- /dev/null +++ b/docs/stage3.md @@ -0,0 +1,34 @@ +# CLeonOS Stage3 + +## Stage Goal +- Add interrupt and exception foundation for x86_64 CLKS. +- Build and load IDT with exception vectors (0-31) and PIC IRQ vectors (32-47). +- Remap PIC and enable timer IRQ. +- Provide unified ISR stub + C dispatcher. + +## Acceptance Criteria +- Kernel boots and prints `INT IDT + PIC INITIALIZED`. +- CPU exceptions are captured and logged with vector/error/RIP. +- Timer IRQ increments internal tick counter without panic. +- Kernel reaches idle loop with interrupts enabled. + +## Build Targets +- `make setup` +- `make iso` +- `make run` +- `make debug` + +## QEMU Command +- `qemu-system-x86_64 -M q35 -m 1024M -cdrom build/CLeonOS-x86_64.iso -serial stdio` + +## Common Bugs and Debugging +- Triple fault right after enabling interrupts: + - Check IDT entry selector/type and verify `lidt` loaded valid base/limit. +- Exception panic with wrong vector/error layout: + - Verify assembly stub push order matches `struct clks_interrupt_frame`. +- IRQ storm or hang: + - Ensure PIC EOI is sent for IRQ vectors and IRQ masks are correct. +- Link failure for ISR symbols: + - Confirm `interrupt_stubs.S` is included in Makefile and assembled to object file. +- Limine ELF panic on segment permissions: + - Keep linker sections page-aligned to avoid mixed permission pages. \ No newline at end of file