diff --git a/Makefile b/Makefile index 302ac37..219b452 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,9 @@ ASM_SOURCES := $(shell find $(SOURCE_DIR) -name '*.S') OBJECTS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.o) $(ASM_SOURCES:.S=.o)) DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.d)) -CFLAGS := -MMD -ffreestanding -O1 -Wall -Wextra -Werror -std=c2x -I$(SOURCE_DIR)/include -no-pie -fstack-protector-strong +CFLAGS := -MMD -ffreestanding -nostdlib -O1 -Wall -Wextra -Werror -std=c2x -I$(SOURCE_DIR)/include -no-pie -fstack-protector-strong -g3 +CFLAGS += -Wno-unused-function +CFLAGS += -Wno-unused-variable ASFLAGS := #$(info C_SOURCES is $(C_SOURCES)) @@ -34,7 +36,7 @@ ASFLAGS := #$(info DEPENDS is $(DEPENDS)) run: myos.iso - qemu-system-i386 -cdrom myos.iso + qemu-system-i386 -d int -no-reboot -cdrom myos.iso cross-compiler: cross-compiler-image/Dockerfile podman build cross-compiler-image -t cc-i686 diff --git a/interrupts.h b/interrupts.h deleted file mode 100644 index b14c454..0000000 --- a/interrupts.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame); diff --git a/src/boot.S b/src/boot.S index 275cba7..92fcff3 100644 --- a/src/boot.S +++ b/src/boot.S @@ -33,7 +33,7 @@ undefined behavior. .section .bss .align 16 stack_bottom: -.skip 16384 # 16 KiB +.skip 1024 * 16 # 16384, 16 KiB stack_top: /* diff --git a/src/include/libc.h b/src/include/libc.h index bb6a8fb..9e9b8e6 100644 --- a/src/include/libc.h +++ b/src/include/libc.h @@ -4,7 +4,8 @@ #include "str.h" #include "tty.h" -__attribute__((noreturn)) void panic(struct str s); +__attribute__((noreturn)) +void panic(struct str s); void* memmove(void *dest, const void *src, size_t n); void* memset(void *s, int c, size_t n); diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c index 1adddd6..402cad5 100644 --- a/src/kernel/gdt.c +++ b/src/kernel/gdt.c @@ -32,23 +32,23 @@ struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content) return entry; } -void gdt_set(uint16_t size, const struct gdt_table_entry base[], uint32_t offset) -{ - base += offset; - - /* the lgdt instruction requires a packed alignment */ - struct __attribute__((packed)) gdtr { - uint16_t size; - uint32_t base; - } gdt = { - .size = size, - .base = (uint32_t)base, - }; - - __asm__ volatile ( - "lgdt %[gdt]" - : - : [gdt] "m"(gdt) - ); -} +//void gdt_load(uint16_t size, const struct gdt_table_entry base[], uint32_t offset) +//{ +// base += offset; +// +// /* the lgdt instruction requires a packed alignment */ +// struct __attribute__((packed)) gdtr { +// uint16_t size; +// uint32_t base; +// } gdt = { +// .size = size, +// .base = (uint32_t)base, +// }; +// +// __asm__ volatile ( +// "lgdt %[gdt]" +// : +// : [gdt] "m"(gdt) +// ); +//} diff --git a/src/kernel/gdt.h b/src/kernel/gdt.h index c817b99..36fd607 100644 --- a/src/kernel/gdt.h +++ b/src/kernel/gdt.h @@ -1,5 +1,6 @@ #pragma once #include +#include "types.h" #define GDT_LIMIT_MAX 0xFFFFF @@ -46,23 +47,21 @@ enum gdt_access_flag : uint8_t { }; enum gdt_flags : uint8_t { - GDT_RESERVED = (1<<0), - GDT_LONG = (1<<1), - GDT_SIZE = (1<<2), - GDT_GRANULARITY = (1<<3), + GDT_RESERVED = (1<<0), + GDT_LONG = (1<<1), + GDT_32BIT = (1<<2), + GDT_GRANULARITY_PAGEWISE = (1<<3), }; -void gdt_set(uint16_t limit, const struct gdt_table_entry base[], uint32_t offset); - struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content); // only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro -static inline void gdt_reload_granular(uint32_t code, - uint32_t data, - uint32_t extra_segment, - uint32_t general_segment_1, - uint32_t general_segment_2, - uint32_t stack_segment) +static inline void gdt_flush_granular(uint32_t code, + uint32_t data, + uint32_t extra_segment, + uint32_t general_segment_1, + uint32_t general_segment_2, + uint32_t stack_segment) { __asm__ volatile ( // Far jump to reload the CS register @@ -88,10 +87,19 @@ static inline void gdt_reload_granular(uint32_t code, : "ax" // Clobbered register ); } - -static inline void gdt_reload(uint32_t data, uint32_t code) +static inline void gdt_load(struct gdt_table_entry base[], uint16_t gdt_size, segment_t data, segment_t code) { + /* the lgdt instruction requires a packed alignment */ + struct __attribute__((packed)) { + uint16_t size; + uint32_t base; + } gdt = { + .size = gdt_size, + .base = (uint32_t)base, + }; + __asm__ volatile ( + "lgdt %[gdt]\n" // Far jump to reload the CS register "jmp %[cs], $reload_CS\n" "reload_CS:\n" @@ -103,8 +111,9 @@ static inline void gdt_reload(uint32_t data, uint32_t code) "movw %%ax, %%gs\n" "movw %%ax, %%ss\n" : // No output operands - : [ds] "i"(data), - [cs] "i"(code) + : [gdt] "m"(gdt), + [ds] "i"(data), + [cs] "i"(code) : "ax" // Clobbered register ); } diff --git a/src/kernel/idt.c b/src/kernel/idt.c index b6894c8..d575a2e 100644 --- a/src/kernel/idt.c +++ b/src/kernel/idt.c @@ -3,17 +3,18 @@ #include "idt.h" -struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type) +struct idt_gate_descriptor idt_encode_descriptor(void* func, segment_t segment_selector, enum idt_dpl dpl, enum idt_flags type) { struct idt_gate_descriptor output = {0}; + uint32_t offset = (uint32_t)func; + output.offset_0 = offset & 0xFFFF; offset >>= 16; output.offset_1 = offset & 0xFFFF; output.segment_selector = segment_selector; - output.ist = ist; - output.flags = type | IDT_DPL_0 | IDT_PRESENT; + output.flags = type | dpl | IDT_PRESENT; return output; } diff --git a/src/kernel/idt.h b/src/kernel/idt.h index 7438dee..d133438 100644 --- a/src/kernel/idt.h +++ b/src/kernel/idt.h @@ -1,11 +1,13 @@ #pragma once #include +#include "types.h" struct __attribute__((packed)) idt_gate_descriptor { uint16_t offset_0; uint16_t segment_selector; - uint8_t ist; + + uint8_t ist; // unused in 32-bit /* `flags` ======= @@ -20,22 +22,26 @@ struct __attribute__((packed)) idt_gate_descriptor { uint16_t offset_1; }; +_Static_assert(sizeof(struct idt_gate_descriptor) == 8); + enum idt_flags : uint8_t { - IDT_GATE_TYPE_TASK_GATE = 0x5, - IDT_GATE_TYPE_INTERUPT_GATE_16 = 0x6, - IDT_GATE_TYPE_TRAP_GATE_16 = 0x7, - IDT_GATE_TYPE_INTERUPT_GATE_32 = 0xE, - IDT_GATE_TYPE_TRAP_GATE_32 = 0xF, + IDT_GATE_TYPE_TASK = 0x5, + IDT_GATE_TYPE_INTERRUPT16 = 0x6, + IDT_GATE_TYPE_TRAP16 = 0x7, + IDT_GATE_TYPE_INTERRUPT32 = 0xE, + IDT_GATE_TYPE_TRAP32 = 0xF, + IDT_PRESENT = 1 << 7, +}; + +enum idt_dpl : uint8_t { IDT_DPL_0 = 0 << 5, IDT_DPL_1 = 1 << 5, IDT_DPL_2 = 2 << 5, IDT_DPL_3 = 3 << 5, - - IDT_PRESENT = 1 << 7, }; -struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type); +struct idt_gate_descriptor idt_encode_descriptor(void* func, segment_t segment_selector, enum idt_dpl dpl, enum idt_flags type); void idt_load(struct idt_gate_descriptor table[], uint16_t size); diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 02c7e05..2f0126b 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -1,11 +1,10 @@ #include #include "libc.h" +#include "tty.h" +#include "interrupts.h" +#include "kernel_state.h" -#ifdef __x86_64__ -typedef unsigned long long int uword_t; -#else -typedef unsigned int uword_t; -#endif +#define EXCEPTION_DEPTH_MAX 5 struct __attribute__((packed)) interrupt_frame { uword_t ip; @@ -15,7 +14,131 @@ struct __attribute__((packed)) interrupt_frame { uword_t ss; }; -__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame) +static void print_interrupt_frame(struct interrupt_frame* f) { - (void)frame; + printf(str_attach( + "Interrupt frame:\n" + "================\n" + "ip: {x32}\n" + "cs: {x32}\n" + "flags: {x32}\n" + "sp: {x32}\n" + "ss: {x32}\n"), + f->ip, + f->cs, + f->flags, + f->sp, + f->ss); } + +/** + * Exceptions + * ========== + */ +__attribute__((interrupt, noreturn)) +void exception_handler_general_protection_fault(struct interrupt_frame* frame, int err) +{ + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + //terminal_clear(); + terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED); + printf(str_attach( + "general protection fault by segment selector {str} :(\n"), + gdt_segment_index_str[err/8]); + if (frame != NULL) { + print_interrupt_frame(frame); + } + __asm__ volatile("cli; hlt"); + __builtin_unreachable(); +} + +__attribute__((interrupt, noreturn)) +void exception_handler_double_fault(struct interrupt_frame* frame) +{ + (void)frame; + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + panic(str_attach("double fault :(")); +} + +__attribute__((interrupt, noreturn)) +void exception_handler_div_by_zero(struct interrupt_frame* frame) +{ + (void)frame; + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + panic(str_attach("div by zero occured :(")); +} + +__attribute__((interrupt, noreturn)) +void exception_handler_page_fault(struct interrupt_frame* frame, int err) +{ + (void)frame; + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED); + printf(str_attach( + "page fault :(, err: 0x{x32}\n"), + err, + gdt_segment_index_str[err/8]); + __asm__ volatile("cli; hlt"); + __builtin_unreachable(); +} + +__attribute__((interrupt, noreturn)) +void exception_default(struct interrupt_frame* frame) +{ + (void)frame; + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + panic(str_attach("non-implemented exception occured")); + + kernel.nested_exception_counter = 0; +} + +/** + * Interrupts + * ========== + */ +__attribute__((interrupt, noreturn)) +void interrupt_default(struct interrupt_frame* frame) +{ + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + (void)frame; + panic(str_attach("non-implemented interrupt invoked")); + + kernel.nested_exception_counter = 0; +} + +__attribute__((interrupt)) +void interrupt_handler_1(struct interrupt_frame* frame) +{ + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + (void)frame; + printf(str_attach("interrupt_handler_1 called!\n")); + + kernel.nested_exception_counter = 0; +} + +__attribute__((interrupt)) +void interrupt_handler_userspace_exit(struct interrupt_frame* frame) +{ + if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) { + panic(str_attach("fatal: too many nested exceptions\n")); + } + (void)frame; + printf(str_attach("interrupt_handler_userspace_exit called!\n")); + + kernel.nested_exception_counter = 0; +} + + diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h new file mode 100644 index 0000000..b788501 --- /dev/null +++ b/src/kernel/interrupts.h @@ -0,0 +1,33 @@ +#pragma once + +#include "types.h" + +struct __attribute__((packed)) interrupt_frame { + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +__attribute__((interrupt, noreturn)) +void exception_handler_div_by_zero(struct interrupt_frame* frame); + +__attribute__((interrupt, noreturn)) +void exception_handler_general_protection_fault(struct interrupt_frame* frame, int err); + +__attribute__((interrupt, noreturn)) +void exception_handler_double_fault(struct interrupt_frame* frame); + +__attribute__((interrupt, noreturn)) +void exception_default(struct interrupt_frame* frame); + +__attribute__((interrupt, noreturn)) +void interrupt_default(struct interrupt_frame* frame); + +__attribute__((interrupt)) +void interrupt_handler_1(struct interrupt_frame* frame); + +__attribute__((interrupt)) +void interrupt_handler_userspace_exit(struct interrupt_frame* frame); + diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 2efb971..683b4d0 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -5,52 +5,69 @@ #include "gdt.h" #include "tss.h" #include "idt.h" +#include "interrupts.h" +#include "types.h" +#include "kernel_state.h" // Future user-space #include "libc.h" #include "tty.h" +#include "str.h" -// TOOD: clean up this -typedef void(*func_t)(void*); - -static void ring3_mode(uint32_t, uint32_t, uint32_t, uint32_t, func_t); -static void user_mode_code(void*); - -void gdt_setup_flat_model() +static void pic_disable() { + __asm__ volatile ("outb %0, %1" + : + : "a"((uint8_t)0xff), "Nd"((uint16_t)0x21) + : "memory"); + __asm__ volatile ("outb %0, %1" + : + : "a"((uint8_t)0xff), "Nd"((uint16_t)0xA1) + : "memory"); } -/* copied and only slightly modified from an osdev wiki article with poor - taste, TODO: change - ----------------------------------------------*/ static void user_mode_code(void*) { - printf(str_attach("hello world :)\n")); + printf(str_attach("hello from user-space before interrupt :)\n")); + __asm__ volatile ("int $0x80"); + __asm__ volatile ("int $0x81"); + //while (1) /* busy loop */; + +#if 0 + printf(str_attach("hello from user-space before exception :)\n")); + /* test division by 0 exception */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdiv-by-zero" + volatile int a = 5/0; + (void)a; + #pragma GCC diagnostic pop + + // should not happen + printf(str_attach("hello from userspace after interrupt and exception!\n")); +#endif + } -static void ring3_mode(uint32_t udata_offset, uint32_t udata_rpl, uint32_t ucode_offset, uint32_t ucode_rpl, func_t callback) +static void ring3_mode(segment_t udata_segment, segment_t ucode_segment, func_t callback) { - const uint32_t udata = udata_offset | udata_rpl; - const uint32_t ucode = ucode_offset | ucode_rpl; - __asm__ volatile ( - "mov %[udata], %%ax\n" - "mov %%ax, %%ds\n" - "mov %%ax, %%es\n" - "mov %%ax, %%fs\n" - "mov %%ax, %%gs\n" - "push %%ax\n" + "mov %[udata], %%ax\n" + "mov %%ax, %%ds\n" + "mov %%ax, %%es\n" + "mov %%ax, %%fs\n" + "mov %%ax, %%gs\n" + "push %%ax\n" "mov %%esp, %%eax\n" - "push %%eax\n" // current esp + "push %%eax\n" // esp "pushf\n" // eflags "push %[ucode]\n" "push %[callback]\n" // instruction address to return to "iret" : - : [udata] "m"(udata), - [ucode] "m"(ucode), - [callback] "m"(callback) + : [udata] "i"(udata_segment), + [ucode] "i"(ucode_segment), + [callback] "i"(callback) : "eax" ); } @@ -65,25 +82,12 @@ void kernel_main(void) { __asm__ volatile("cli"); - static struct tss tss = {0}; + _Static_assert(sizeof(kernel.gdt) == 0x30); - enum segment_index : size_t { - null_segment = 0, - kernel_code_segment = 1, - kernel_data_segment = 2, - user_code_segment = 3, - user_data_segment = 4, - task_state_segment = 5, - segment_count, - }; - - static struct gdt_table_entry gdt[segment_count]; - _Static_assert(sizeof(gdt) == 0x30); - - gdt[null_segment] = gdt_encode_entry((struct gdt_entry_content){0}); + kernel.gdt[SEGMENT_NULL] = gdt_encode_entry((struct gdt_entry_content){0}); /* kernel */ - gdt[kernel_code_segment] = gdt_encode_entry((struct gdt_entry_content){ + kernel.gdt[SEGMENT_KERNEL_CODE] = gdt_encode_entry((struct gdt_entry_content){ .base = 0, .limit = GDT_LIMIT_MAX, .access_byte = GDT_ACCESS_RW @@ -91,21 +95,21 @@ void kernel_main(void) | GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DPL_0 | GDT_ACCESS_PRESENT, - .flags = GDT_SIZE - | GDT_GRANULARITY}); + .flags = GDT_32BIT + | GDT_GRANULARITY_PAGEWISE}); - gdt[kernel_data_segment] = gdt_encode_entry((struct gdt_entry_content){ + kernel.gdt[SEGMENT_KERNEL_DATA] = gdt_encode_entry((struct gdt_entry_content){ .base = 0, .limit = GDT_LIMIT_MAX, .access_byte = GDT_ACCESS_RW | GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DPL_0 | GDT_ACCESS_PRESENT, - .flags = GDT_SIZE - | GDT_GRANULARITY}); + .flags = GDT_32BIT + | GDT_GRANULARITY_PAGEWISE}); /* user */ - gdt[user_code_segment] = gdt_encode_entry((struct gdt_entry_content) { + kernel.gdt[SEGMENT_USER_CODE] = gdt_encode_entry((struct gdt_entry_content) { .base = 0, .limit = GDT_LIMIT_MAX, .access_byte = GDT_ACCESS_RW @@ -113,54 +117,111 @@ void kernel_main(void) | GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DPL_3 | GDT_ACCESS_PRESENT, - .flags = GDT_SIZE - | GDT_GRANULARITY}); + .flags = GDT_32BIT + | GDT_GRANULARITY_PAGEWISE}); - gdt[user_data_segment] = gdt_encode_entry((struct gdt_entry_content) { + kernel.gdt[SEGMENT_USER_DATA] = gdt_encode_entry((struct gdt_entry_content) { .base = 0, .limit = GDT_LIMIT_MAX, .access_byte = GDT_ACCESS_RW | GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DPL_3 | GDT_ACCESS_PRESENT, - .flags = GDT_SIZE - | GDT_GRANULARITY}); + .flags = GDT_32BIT + | GDT_GRANULARITY_PAGEWISE}); /* tss */ - gdt[task_state_segment] = gdt_encode_entry((struct gdt_entry_content) { - .base = (uint32_t)&tss, - .limit = sizeof(tss)-1, + kernel.gdt[SEGMENT_TASK_STATE] = gdt_encode_entry((struct gdt_entry_content) { + .base = (uint32_t)&kernel.tss, + .limit = sizeof(kernel.tss)-1, .access_byte = GDT_ACCESS_ACCESSED | GDT_ACCESS_EXEC | GDT_ACCESS_DPL_0 | GDT_ACCESS_PRESENT, .flags = 0}); - gdt_set(sizeof gdt, gdt, 0); - - gdt_reload(kernel_data_segment * sizeof (struct gdt_table_entry), - kernel_code_segment * sizeof (struct gdt_table_entry)); - /* Setup the TSS */ - tss.ss0 = kernel_data_segment * sizeof (struct gdt_table_entry); - tss.esp0 = 0; /* TODO: set kernel stack pointer */ + memset(&kernel.tss, 0, sizeof kernel.tss); + kernel.tss.ss0 = segment(SEGMENT_KERNEL_DATA, SEGMENT_GDT, 0); +#if 1 + static uint8_t kernel_stack[1024]; + kernel.tss.esp0 = (uint32_t)kernel_stack; +#else + kernel.tss.esp0 = 0; +#endif - tss_load(task_state_segment * sizeof (struct gdt_table_entry)); +#if 1 + /* Setup the IDT */ + for (size_t i = 0; i < 32; i++) { + kernel.idt[i] = idt_encode_descriptor( + exception_default, + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0), + IDT_DPL_3, + IDT_GATE_TYPE_TRAP32); + } + for (size_t i = 32; i < IDT_COUNT; i++) { + kernel.idt[i] = idt_encode_descriptor( + interrupt_default, + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0), + IDT_DPL_3, + IDT_GATE_TYPE_INTERRUPT32); + } - /* Setup the IDT */ - //static struct idt_gate_descriptor idt[256] = {0}; + kernel.idt[0x0] = idt_encode_descriptor( + exception_handler_div_by_zero, + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0), + IDT_DPL_3, + IDT_GATE_TYPE_TRAP32 + ); - //struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type); - //idt[80] = idt_encode_descriptor(interrupt_handler_1, kernel_code_segment, 0, IDT_GATE_TYPE_INTERUPT_GATE_32); + kernel.idt[0x8] = idt_encode_descriptor( + exception_handler_double_fault, + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0), + IDT_DPL_3, + IDT_GATE_TYPE_TRAP32 + ); - //idt_load(idt, sizeof idt); + kernel.idt[0xD] = idt_encode_descriptor( + exception_handler_general_protection_fault, + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0), + IDT_DPL_3, + IDT_GATE_TYPE_TRAP32 + ); - /* Finally go to ring 3 */ - ring3_mode(user_data_segment * sizeof (struct gdt_table_entry), 3, - user_code_segment * sizeof (struct gdt_table_entry), 3, - user_mode_code); + // Interrupts + kernel.idt[0x80] = idt_encode_descriptor( + interrupt_handler_1, + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0), + IDT_DPL_3, + IDT_GATE_TYPE_INTERRUPT32 + ); + kernel.idt[0x81] = idt_encode_descriptor( + interrupt_handler_userspace_exit, + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0), + IDT_DPL_3, + IDT_GATE_TYPE_INTERRUPT32 + ); +#endif + /* Flush the gdt and tss */ + gdt_load(kernel.gdt, + sizeof kernel.gdt, + segment(SEGMENT_KERNEL_DATA, SEGMENT_GDT, 0), + segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0)); + tss_load(segment(SEGMENT_TASK_STATE, SEGMENT_GDT, 0)); + idt_load(kernel.idt, sizeof kernel.idt); + + /* enable interrupts */ __asm__ volatile("sti"); + + /* Finally go to ring 3 */ + printf(str_attach("hello from kernel space!\n")); + + pic_disable(); // temporary duct-tape + + ring3_mode(segment(SEGMENT_USER_DATA, SEGMENT_GDT, 3), + segment(SEGMENT_USER_CODE, SEGMENT_GDT, 3), + user_mode_code); } diff --git a/src/kernel/kernel_state.c b/src/kernel/kernel_state.c new file mode 100644 index 0000000..4f7ecc7 --- /dev/null +++ b/src/kernel/kernel_state.c @@ -0,0 +1,18 @@ + +#include "str.h" + +#include "kernel_state.h" + +const struct str gdt_segment_index_str[SEGMENT_COUNT] = { + [SEGMENT_NULL ] = str_attach("SEGMENT_NULL"), + [SEGMENT_KERNEL_CODE] = str_attach("SEGMENT_KERNEL_CODE"), + [SEGMENT_KERNEL_DATA] = str_attach("SEGMENT_KERNEL_DATA"), + [SEGMENT_USER_CODE ] = str_attach("SEGMENT_USER_CODE"), + [SEGMENT_USER_DATA ] = str_attach("SEGMENT_USER_DATA"), + [SEGMENT_TASK_STATE ] = str_attach("SEGMENT_TASK_STATE"), +}; + +const struct str idt_index_str[IDT_COUNT] = { +}; + +struct kernel_state kernel = { 0 }; diff --git a/src/kernel/kernel_state.h b/src/kernel/kernel_state.h new file mode 100644 index 0000000..c5013ae --- /dev/null +++ b/src/kernel/kernel_state.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "str.h" +#include "tss.h" +#include "idt.h" +#include "gdt.h" + +enum gdt_segment_index : size_t { + SEGMENT_NULL, + SEGMENT_KERNEL_CODE, + SEGMENT_KERNEL_DATA, + SEGMENT_USER_CODE, + SEGMENT_USER_DATA, + SEGMENT_TASK_STATE, + SEGMENT_COUNT, +}; +extern const struct str gdt_segment_index_str[SEGMENT_COUNT]; // reverse lookup enum -> str + +enum idt_index : size_t { + IDT_COUNT = 256 +}; +extern const struct str idt_index_str[IDT_COUNT]; // reverse lookup enum -> str + +constexpr size_t BACKTRACE_MAX = 256; + +struct kernel_state { + struct tss tss; + struct gdt_table_entry gdt[SEGMENT_COUNT]; + struct idt_gate_descriptor idt[IDT_COUNT]; + int nested_exception_counter; +}; + +extern struct kernel_state kernel; diff --git a/src/kernel/types.h b/src/kernel/types.h new file mode 100644 index 0000000..3f01789 --- /dev/null +++ b/src/kernel/types.h @@ -0,0 +1,27 @@ + +#pragma once + + +typedef void(*func_t)(void*); + +/* define uword_t per the instruction set manual */ +#ifdef __x86_64__ +typedef unsigned long long int uword_t; +#else +typedef unsigned int uword_t; +#endif + +/* segment selectors identifies segments either in the gdt or the ldt */ +typedef uint16_t segment_t; + +enum segment_ti : uint16_t { + SEGMENT_GDT = 0, + SEGMENT_LDT = 1, +}; + +static inline segment_t segment(uint16_t index, enum segment_ti ti, uint16_t rpl) +{ + return (index << 3) | ((ti & 0xF) << 1) | (rpl & 0xFF); +} + + diff --git a/src/lib/libc.c b/src/lib/libc.c index 1232628..6e95f44 100644 --- a/src/lib/libc.c +++ b/src/lib/libc.c @@ -15,10 +15,10 @@ __attribute__((noreturn)) void panic(struct str s) { - terminal_clear(); + //terminal_clear(); terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED); terminal_write(s); - __asm__ volatile("hlt"); + __asm__ volatile("cli; hlt"); __builtin_unreachable(); } diff --git a/src/lib/printf.c b/src/lib/printf.c index e43fcdb..2a54101 100644 --- a/src/lib/printf.c +++ b/src/lib/printf.c @@ -31,12 +31,16 @@ static inline int ps_get(struct printf_state* s) return s->str.data[s->i++]; } -static int print_long(unsigned long n, struct str alphabet, bool is_signed) +static int print_long(unsigned long n, struct str alphabet, bool is_signed, unsigned int padding, char pad_char) { - constexpr size_t BUF_SZ = 21; + constexpr size_t BUF_SZ = 128; char buf[BUF_SZ]; size_t i = 0; + if (padding > BUF_SZ) { + return -1; + } + if (n == 0) { terminal_putchar('0'); return 1; @@ -59,37 +63,60 @@ static int print_long(unsigned long n, struct str alphabet, bool is_signed) buf[BUF_SZ-i] = alphabet.data[n % alphabet.len]; n /= alphabet.len; } + while (i < padding) { + i += 1; + buf[BUF_SZ-i] = pad_char; + } terminal_write((struct str){.data = &buf[BUF_SZ-i], .len = i}); return i; } -static int print_i32(struct printf_state* s) +static int print_i32(struct printf_state* s, int padding, char pad_char) { + padding = padding ? padding : 0; + pad_char = pad_char ? pad_char : ' '; long n = va_arg(s->ap, int32_t); - return print_long(n, str_attach("0123456789"), true); + return print_long(n, str_attach("0123456789"), true, padding, pad_char); } -static int print_u32(struct printf_state* s) +static int print_u32(struct printf_state* s, int padding, char pad_char) { + padding = padding ? padding : 0; + pad_char = pad_char ? pad_char : ' '; unsigned long n = va_arg(s->ap, uint32_t); - return print_long(n, str_attach("0123456789"), false); + return print_long(n, str_attach("0123456789"), false, padding, pad_char); } -static int print_x32(struct printf_state* s) +static int print_x32(struct printf_state* s, int padding, char pad_char) { + padding = padding ? padding : 0; + pad_char = pad_char ? pad_char : '0'; unsigned long n = va_arg(s->ap, uint32_t); - return print_long(n, str_attach("0123456789abcdefgh"), false); + struct str alphabet = str_attach("0123456789abcdef"); + return print_long(n, alphabet, false, padding, pad_char); } -static int print_str(struct printf_state* s) +static int print_b32(struct printf_state* s, int padding, char pad_char) { - const struct str str = va_arg(s->ap, struct str); - for (size_t i=0; i < str.len; i++) { - terminal_putchar(str.data[i]); - } - return str.len; + padding = padding ? padding : 32; + pad_char = pad_char ? pad_char : '0'; + unsigned long n = va_arg(s->ap, uint32_t); + struct str alphabet = str_attach("01"); + return print_long(n, alphabet, false, padding, pad_char); +} + +static int print_str(struct printf_state* s, int padding, char pad_char) +{ + // TODO: implement padding + (void)padding; + (void)pad_char; + const struct str str = va_arg(s->ap, struct str); + for (size_t i=0; i < str.len; i++) { + terminal_putchar(str.data[i]); + } + return str.len; } #pragma GCC diagnostic ignored "-Wmultichar" @@ -97,6 +124,8 @@ static int parse_format_cmd(struct printf_state* s) { int c; uint32_t cmd = 0; + int pad = 0; + char pad_char = '\0'; constexpr uint32_t CHAR_MASK = (1<