diff --git a/.vscode/settings.json b/.vscode/settings.json index cbeac9b..3e223b7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,7 @@ { "files.associations": { - "stdnoreturn.h": "c", - "serial.h": "c", - "io.h": "c", - "kprintf.h": "c", - "log.h": "c", - "cpu.h": "c" + "cstdio": "c", + "stdarg.h": "c", + "idt.h": "c" } } \ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile index dde581a..254b739 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -2,7 +2,7 @@ MAKEFLAGS += -rR .SUFFIXES: -QEMUFLAGS := -m 2G -serial stdio +QEMUFLAGS := -m 2G -serial stdio -display none IMAGE_NAME := release/emk HOST_CC := cc diff --git a/kernel/GNUmakefile b/kernel/GNUmakefile index 9450226..587e15c 100644 --- a/kernel/GNUmakefile +++ b/kernel/GNUmakefile @@ -12,15 +12,30 @@ CC := gcc AS := gcc NASM := nasm -CFLAGS := -g -O2 -pipe -Wall -Wextra -Werror -std=gnu11 -ffreestanding \ - -fno-stack-protector -fno-stack-check -fno-PIC \ - -ffunction-sections -fdata-sections -m64 -march=x86-64 \ - -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone \ - -mcmodel=kernel -Wno-unused-variable +# Build mode configuration +BUILD_MODE ?= dev +ifeq ($(BUILD_MODE),release) + CFLAGS := -Os -pipe -Wall -Wextra -Werror -std=gnu11 -ffreestanding \ + -fno-stack-protector -fno-stack-check -fno-PIC \ + -ffunction-sections -fdata-sections -m64 -march=x86-64 \ + -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone \ + -mcmodel=kernel -fno-unwind-tables -fno-asynchronous-unwind-tables \ + -s -Wno-unused-variable + NASMFLAGS := + LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -Wl,--gc-sections \ + -T linker.ld -Wl,-m,elf_x86_64 -Wl,--strip-all +else + CFLAGS := -g -O2 -pipe -Wall -Wextra -Werror -std=gnu11 -ffreestanding \ + -fno-stack-protector -fno-stack-check -fno-PIC \ + -ffunction-sections -fdata-sections -m64 -march=x86-64 \ + -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone \ + -mcmodel=kernel -Wno-unused-variable + NASMFLAGS := -F dwarf -g + LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -Wl,--gc-sections \ + -T linker.ld -Wl,-m,elf_x86_64 +endif + CPPFLAGS := -I../external -I$(SRCDIR) -MMD -MP -DLIMINE_API_REVISION=3 -NASMFLAGS := -F dwarf -g -LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -Wl,--gc-sections \ - -T linker.ld -Wl,-m,elf_x86_64 SRCS := $(shell find $(SRCDIR) -type f \( -name '*.c' -o -name '*.S' -o -name '*.asm' \)) OBJS := $(patsubst %.c,$(OBJDIR)/%.o,$(filter %.c,$(SRCS))) \ diff --git a/kernel/src/arch/gdt.c b/kernel/src/arch/gdt.c index 8ee226c..cde0000 100644 --- a/kernel/src/arch/gdt.c +++ b/kernel/src/arch/gdt.c @@ -1,3 +1,4 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ #include gdt_entry_t gdt[5]; diff --git a/kernel/src/arch/gdt.h b/kernel/src/arch/gdt.h index 3c68d77..efcf1f0 100644 --- a/kernel/src/arch/gdt.h +++ b/kernel/src/arch/gdt.h @@ -1,3 +1,4 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ #ifndef GDT_H #define GDT_H diff --git a/kernel/src/arch/idt-stub.S b/kernel/src/arch/idt-stub.S new file mode 100644 index 0000000..abce1a9 --- /dev/null +++ b/kernel/src/arch/idt-stub.S @@ -0,0 +1,94 @@ +.globl real_handlers +.extern real_handlers + +isr_handler_stub: + pushq %rax + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rbp + pushq %rdi + pushq %rsi + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + + movq %cr0, %rax + pushq %rax + movq %cr2, %rax + pushq %rax + movq %cr3, %rax + pushq %rax + movq %cr4, %rax + pushq %rax + + movq %ds, %rax + pushq %rax + movq %es, %rax + pushq %rax + + cld + + movq %rsp, %rdi + movq 168(%rsp), %rbx + shlq $3, %rbx + leaq real_handlers(%rip), %rax + addq %rbx, %rax + callq *(%rax) + + addq $48, %rsp + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rsi + popq %rdi + popq %rbp + popq %rdx + popq %rcx + popq %rbx + popq %rax + addq $16, %rsp + + iretq + +.macro ISR index +.global _isr\index +.type _isr\index, @function +_isr\index: +.if 0x\index != 8 && 0x\index != 10 && 0x\index != 11 && 0x\index != 12 && 0x\index != 13 && 0x\index != 14 && 0x\index != 17 && 0x\index != 30 + pushq $0 +.endif + pushq $0x\index + jmp isr_handler_stub +.endm + +.macro ISRADDR index + .quad _isr\index +.endm + +.irpc i, 0123456789abcdef +.irpc j, 0123456789abcdef + ISR \i\j +.endr +.endr + +.section .data + +.global stubs +.align 8 +stubs: +.irpc i, 0123456789abcdef +.irpc j, 0123456789abcdef + ISRADDR \i\j +.endr +.endr \ No newline at end of file diff --git a/kernel/src/arch/idt.c b/kernel/src/arch/idt.c new file mode 100644 index 0000000..fe9b4bf --- /dev/null +++ b/kernel/src/arch/idt.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +struct idt_entry __attribute__((aligned(16))) idt_descriptor[256] = {0}; +idt_intr_handler real_handlers[256] = {0}; +extern uint64_t stubs[]; + +struct __attribute__((packed)) idt_ptr +{ + uint16_t limit; + uint64_t base; +}; + +struct idt_ptr idt_ptr = {sizeof(idt_descriptor) - 1, (uint64_t)&idt_descriptor}; + +void idt_default_interrupt_handler(struct register_ctx *ctx) +{ + kpanic(ctx, NULL); +} + +#define SET_GATE(interrupt, base, flags) \ + do \ + { \ + idt_descriptor[(interrupt)].off_low = (base) & 0xFFFF; \ + idt_descriptor[(interrupt)].sel = 0x8; \ + idt_descriptor[(interrupt)].ist = 0; \ + idt_descriptor[(interrupt)].attr = (flags); \ + idt_descriptor[(interrupt)].off_mid = ((base) >> 16) & 0xFFFF; \ + idt_descriptor[(interrupt)].off_high = ((base) >> 32) & 0xFFFFFFFF; \ + idt_descriptor[(interrupt)].zero = 0; \ + } while (0) + +void idt_init() +{ + for (int i = 0; i < 32; i++) + { + SET_GATE(i, stubs[i], IDT_TRAP_GATE); + real_handlers[i] = idt_default_interrupt_handler; + } + + for (int i = 32; i < 256; i++) + { + SET_GATE(i, stubs[i], IDT_INTERRUPT_GATE); + } + + __asm__ volatile( + "lidt %0" + : : "m"(idt_ptr) : "memory"); +} + +int idt_register_handler(size_t vector, idt_intr_handler handler) +{ + if (real_handlers[vector] != idt_default_interrupt_handler) + { + real_handlers[vector] = handler; + return 0; + } + return 1; +} \ No newline at end of file diff --git a/kernel/src/arch/idt.h b/kernel/src/arch/idt.h new file mode 100644 index 0000000..a1758ab --- /dev/null +++ b/kernel/src/arch/idt.h @@ -0,0 +1,37 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#ifndef IDT_H +#define IDT_H + +#include +#include + +struct __attribute__((packed)) register_ctx +{ + uint64_t es, ds; + uint64_t cr4, cr3, cr2, cr0; + uint64_t r15, r14, r13, r12, r11, r10, r9, r8, rsi, rdi, rbp, rdx, rcx, rbx, rax; + uint64_t vector, err; + uint64_t rip, cs, rflags, rsp, ss; +}; + +struct __attribute__((packed)) idt_entry +{ + uint16_t off_low; + uint16_t sel; + uint8_t ist; + uint8_t attr; + uint16_t off_mid; + uint32_t off_high; + uint32_t zero; +}; + +typedef void (*idt_intr_handler)(struct register_ctx *ctx); +#define IDT_INTERRUPT_GATE (0x8E) +#define IDT_TRAP_GATE (0x8F) +#define IDT_IRQ_BASE (0x20) + +void idt_init(); +int idt_register_handler(size_t vector, idt_intr_handler handler); +void idt_default_interrupt_handler(struct register_ctx *ctx); + +#endif // IDT_H \ No newline at end of file diff --git a/kernel/src/emk.c b/kernel/src/emk.c index f78d808..8616ec0 100644 --- a/kernel/src/emk.c +++ b/kernel/src/emk.c @@ -6,6 +6,7 @@ #include #include #include +#include __attribute__((used, section(".limine_requests"))) static volatile LIMINE_BASE_REVISION(3); __attribute__((used, section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER; @@ -21,11 +22,15 @@ void emk_entry(void) if (!LIMINE_BASE_REVISION_SUPPORTED) { - early("ERROR: Limine base revision is not supported\n"); + log_early("ERROR: Limine base revision is not supported\n"); hcf(); } gdt_init(); - early("Initialized GDT"); + log_early("Initialized GDT"); + idt_init(); + log_early("Initialized IDT"); + __asm__ volatile("int $0x01"); + hlt(); } \ No newline at end of file diff --git a/kernel/src/sys/kpanic.c b/kernel/src/sys/kpanic.c new file mode 100644 index 0000000..e8b8e67 --- /dev/null +++ b/kernel/src/sys/kpanic.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include + +static const char *strings[32] = { + "Division by Zero", + "Debug", + "Non-Maskable-Interrupt", + "Breakpoint", + "Overflow", + "Bound Range Exceeded", + "Invalid opcode", + "Device (FPU) not available", + "Double Fault", + "RESERVED VECTOR", + "Invalid TSS", + "Segment not present", + "Stack Segment Fault", + "General Protection Fault", + "Page Fault", + "RESERVED VECTOR", + "x87 FP Exception", + "Alignment Check", + "Machine Check (Internal Error)", + "SIMD FP Exception", + "Virtualization Exception", + "Control Protection Exception", + "RESERVED VECTOR", + "RESERVED VECTOR", + "RESERVED VECTOR", + "RESERVED VECTOR", + "RESERVED VECTOR", + "RESERVED VECTOR", + "Hypervisor Injection Exception", + "VMM Communication Exception", + "Security Exception", + "RESERVED VECTOR"}; + +static void capture_regs(struct register_ctx *context) +{ + __asm__ volatile( + "movq %%rax, %0\n\t" + "movq %%rbx, %1\n\t" + "movq %%rcx, %2\n\t" + "movq %%rdx, %3\n\t" + "movq %%rsi, %4\n\t" + "movq %%rdi, %5\n\t" + "movq %%rbp, %6\n\t" + "movq %%r8, %7\n\t" + "movq %%r9, %8\n\t" + "movq %%r10, %9\n\t" + "movq %%r11, %10\n\t" + "movq %%r12, %11\n\t" + "movq %%r13, %12\n\t" + "movq %%r14, %13\n\t" + "movq %%r15, %14\n\t" + : "=m"(context->rax), "=m"(context->rbx), "=m"(context->rcx), "=m"(context->rdx), + "=m"(context->rsi), "=m"(context->rdi), "=m"(context->rbp), "=m"(context->r9), + "=m"(context->r9), "=m"(context->r10), "=m"(context->r11), "=m"(context->r12), + "=m"(context->r13), "=m"(context->r14), "=m"(context->r15) + : + : "memory"); + + __asm__ volatile( + "movq %%cs, %0\n\t" + "movq %%ss, %1\n\t" + "movq %%es, %2\n\t" + "movq %%ds, %3\n\t" + "movq %%cr0, %4\n\t" + "movq %%cr2, %5\n\t" + "movq %%cr3, %6\n\t" + "movq %%cr4, %7\n\t" + : "=r"(context->cs), "=r"(context->ss), "=r"(context->es), "=r"(context->ds), + "=r"(context->cr0), "=r"(context->cr2), "=r"(context->cr3), "=r"(context->cr4) + : + : "memory"); + + __asm__ volatile( + "movq %%rsp, %0\n\t" + "pushfq\n\t" + "popq %1\n\t" + : "=r"(context->rsp), "=r"(context->rflags) + : + : "memory"); + + context->rip = (uint64_t)__builtin_return_address(0); +} + +void kpanic(struct register_ctx *ctx, const char *fmt, ...) +{ + struct register_ctx regs; + + if (ctx == NULL) + { + capture_regs(®s); + regs.err = 0xDEADBEEF; + regs.vector = 0x0; + } + else + { + memcpy(®s, ctx, sizeof(struct register_ctx)); + } + + char buf[1024]; + + if (fmt) + { + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + } + else + { + if (regs.vector >= sizeof(strings) / sizeof(strings[0])) + { + snprintf(buf, sizeof(buf), "Unknown panic vector: %d", regs.vector); + } + else + { + snprintf(buf, sizeof(buf), "%s", strings[regs.vector]); + } + } + + log_panic("=== Kernel panic: '%s' @ 0x%.16llx ===", buf, regs.rip); + log_panic("Registers:"); + log_panic(" rax: 0x%.16llx rbx: 0x%.16llx rcx: 0x%.16llx rdx: 0x%.16llx", regs.rax, regs.rbx, regs.rcx, regs.rdx); + log_panic(" rsi: 0x%.16llx rdi: 0x%.16llx rbp: 0x%.16llx rsp: 0x%.16llx", regs.rsi, regs.rdi, regs.rbp, regs.rsp); + log_panic(" r8 : 0x%.16llx r9 : 0x%.16llx r10: 0x%.16llx r11: 0x%.16llx", regs.r8, regs.r9, regs.r10, regs.r11); + log_panic(" r12: 0x%.16llx r13: 0x%.16llx r14: 0x%.16llx r15: 0x%.16llx", regs.r12, regs.r13, regs.r14, regs.r15); + log_panic(" rip: 0x%.16llx rflags: 0x%.16llx", regs.rip, regs.rflags); + log_panic(" cs : 0x%.16llx ss: 0x%.16llx ds: 0x%.16llx es: 0x%.16llx", regs.cs, regs.ss, regs.ds, regs.es); + log_panic(" cr0: 0x%.16llx cr2: 0x%.16llx cr3: 0x%.16llx cr4: 0x%.16llx", regs.cr0, regs.cr2, regs.cr3, regs.cr4); + log_panic(" err: 0x%.16llx vector: 0x%.16llx", regs.err, regs.vector); + + hcf(); +} \ No newline at end of file diff --git a/kernel/src/sys/kpanic.h b/kernel/src/sys/kpanic.h new file mode 100644 index 0000000..4f7bae6 --- /dev/null +++ b/kernel/src/sys/kpanic.h @@ -0,0 +1,8 @@ +#ifndef KPANIC_H +#define KPANIC_H + +#include + +void kpanic(struct register_ctx *ctx, const char *fmt, ...); + +#endif // KPANIC_H \ No newline at end of file diff --git a/kernel/src/util/kprintf.c b/kernel/src/util/kprintf.c index 2e6e34e..d15c989 100644 --- a/kernel/src/util/kprintf.c +++ b/kernel/src/util/kprintf.c @@ -1,3 +1,4 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ #include #include @@ -24,5 +25,26 @@ int kprintf(const char *fmt, ...) } va_end(args); + return length; +} + +int snprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + int length = vsnprintf(buf, size, fmt, args); + va_end(args); + return length; +} + +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + int length = npf_vsnprintf(buf, size, fmt, args); + + if (length >= (int)size) + { + buf[size - 1] = '\0'; + } + return length; } \ No newline at end of file diff --git a/kernel/src/util/kprintf.h b/kernel/src/util/kprintf.h index 75f0541..f809a8c 100644 --- a/kernel/src/util/kprintf.h +++ b/kernel/src/util/kprintf.h @@ -1,7 +1,15 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ #ifndef KPRINTF_H #define KPRINTF_H +#include +#include + /* Minimal kprintf using nanoprintf, see external/nanoprintf.h */ int kprintf(const char *fmt, ...); +/* Careful with these */ +int snprintf(char *buf, size_t size, const char *fmt, ...); +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); + #endif // KPRINTF_H \ No newline at end of file diff --git a/kernel/src/util/log.h b/kernel/src/util/log.h index 4e33c28..378156b 100644 --- a/kernel/src/util/log.h +++ b/kernel/src/util/log.h @@ -1,8 +1,10 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ #ifndef LOG_H #define LOG_H #include -#define early(fmt, ...) kprintf("early: " fmt "\n", ##__VA_ARGS__) +#define log_early(fmt, ...) kprintf("early: " fmt "\n", ##__VA_ARGS__) +#define log_panic(fmt, ...) kprintf("panic: " fmt "\n", ##__VA_ARGS__) #endif // LOG_H \ No newline at end of file diff --git a/limine.conf b/limine.conf index 2ec1904..c493652 100644 --- a/limine.conf +++ b/limine.conf @@ -1,4 +1,4 @@ -timeout: 3 +timeout: 0 /EMK protocol: limine