diff --git a/kernel/include/interrupts.h b/kernel/include/interrupts.h new file mode 100644 index 0000000..d5b075e --- /dev/null +++ b/kernel/include/interrupts.h @@ -0,0 +1,26 @@ +/* Calcite, kernel/include/interrupts.h + * Copyright 2025 Benji Dial + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#pragma once + +#include + +//switches to kernel gdt and idt, maps and unmasks irqs, enables interrupts +void enable_interrupts(); + +//the handler should have normal C linkage +void set_irq_handler(uint8_t irq_line, void (*handler)()); diff --git a/kernel/include/panic.h b/kernel/include/panic.h new file mode 100644 index 0000000..8555ab3 --- /dev/null +++ b/kernel/include/panic.h @@ -0,0 +1,31 @@ +/* Calcite, kernel/include/panic.h + * Copyright 2025 Benji Dial + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +[[noreturn]] void panic_core( + const char *file, const char *function, int line, const char *message); + +#define panic(message) panic_core(__FILE__, __func__, __LINE__, message); + +#ifdef NDEBUG +#define assert(condition) ((void)0) +#else +#define assert(condition) \ + { \ + if (!(condition)) \ + panic("assertion failed: " #condition) \ + } +#endif diff --git a/kernel/src/entry.c b/kernel/src/entry.c index 6a8771b..724c0fa 100644 --- a/kernel/src/entry.c +++ b/kernel/src/entry.c @@ -15,10 +15,12 @@ * this program. If not, see . */ +#include #include #include #include #include +#include LIMINE_BASE_REVISION(3) @@ -177,30 +179,12 @@ static uint64_t initfs_length; [[noreturn]] static void with_kernel_page_tables() { - //test initfs + //further initialization set_initfs(initfs_start, initfs_length); + enable_interrupts(); - const uint8_t *hello_start; - uint64_t hello_length; - look_up_initfs_file("resx/hello.txt", &hello_start, &hello_length); - - if (hello_length != 7) - die(); - - for (int i = 0; i < 7; ++i) - if (hello_start[i] != "Hello!\n"[i]) - die(); - - //test framebuffer - - for (int y = 0; y < fb_height; ++y) - for (int x = 0; x < fb_width; ++x) { - fb_base[y * fb_pitch + x * 4] = y * 256 / fb_height; - fb_base[y * fb_pitch + x * 4 + 1] = x * 256 / fb_width; - fb_base[y * fb_pitch + x * 4 + 2] = 0; - } - - die(); + while (1) + __asm__ ("hlt"); } diff --git a/kernel/src/interrupts.asm b/kernel/src/interrupts.asm new file mode 100644 index 0000000..4740556 --- /dev/null +++ b/kernel/src/interrupts.asm @@ -0,0 +1,153 @@ + ; Calcite, kernel/src/interrupts.asm + ; Copyright 2025 Benji Dial + ; + ; This program is free software: you can redistribute it and/or modify + ; it under the terms of the GNU General Public License as published by + ; the Free Software Foundation, either version 3 of the License, or (at + ; your option) any later version. + ; + ; This program is distributed in the hope that it will be useful, but + ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + ; more details. + ; + ; You should have received a copy of the GNU General Public License along with + ; this program. If not, see . + + +bits 64 + +;both defined in interrupts.c +extern isr_exception_c +extern isr_irq_c + +section .rodata + +;referenced in interrupts.c +global isrs +isrs: +%assign n 0 +%rep 48 + dq isr_ %+ n +%assign n n + 1 +%endrep + +section .text + +%assign n 0 + +%rep 32 +isr_ %+ n: +%if n <= 0x07 || n = 0x09 || (n >= 0x0f && n <= 0x1c && n <> 0x11 && n <> 0x15) || n = 0x1f + push qword 0 +%endif + push qword n + jmp isr_exception_common +%assign n n + 1 +%endrep + +%rep 16 +isr_ %+ n: + push rdi + mov rdi, n - 32 + jmp isr_irq_common +%assign n n + 1 +%endrep + +isr_exception_common: + push rax + push rbx + push rcx + push rdx + push rdi + push rsi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + mov rdi, rsp + jmp isr_exception_c + +isr_irq_common: + push rax + push rcx + push rdx + push rsi + push r8 + push r9 + push r10 + push r11 + + test dil, 0x08 + jnz .high + + call isr_irq_c + mov al, 0x20 + out 0x20, al + jmp .common_end + +.high: + call isr_irq_c + mov al, 0x20 + out 0xa0, al + out 0x20, al + +.common_end: + pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdx + pop rcx + pop rax + pop rdi + + iretq + +;referenced in interrupts.c +;rdi is pointer to gdt descriptor +;rsi is pointer to idt descriptor +;dx is offset of tss into gdt +global enable_interrupts_asm +enable_interrupts_asm: + + ;load tables + + lgdt [rdi] + ltr dx + lidt [rsi] + + ;initialize interrupt controllers + + mov al, 0x11 + out 0x20, al + mov al, 0x20 + out 0x21, al + mov al, 0x04 + out 0x21, al + mov al, 0x01 + out 0x21, al + mov al, 0x00 + out 0x21, al + + mov al, 0x11 + out 0xa0, al + mov al, 0x28 + out 0xa1, al + mov al, 0x02 + out 0xa1, al + mov al, 0x01 + out 0xa1, al + mov al, 0x00 + out 0xa1, al + + ;enable interrupts and return + + sti + ret diff --git a/kernel/src/interrupts.c b/kernel/src/interrupts.c new file mode 100644 index 0000000..28483e3 --- /dev/null +++ b/kernel/src/interrupts.c @@ -0,0 +1,240 @@ +/* Calcite, kernel/src/interrupts.c + * Copyright 2025 Benji Dial + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +//relevant sources: +// amd64 architecture programmer's manual, volume 2: system programming +// https://osdev.wiki/wiki/Global_Descriptor_Table +// https://osdev.wiki/wiki/Interrupt_Descriptor_Table +// https://osdev.wiki/wiki/Task_State_Segment + +#include +#include + +struct [[gnu::packed]] exception_parameter { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + uint64_t exception_number; + //0 if exception pushes no error code + uint64_t error_code; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +}; + +//referenced in interrupts.asm +void isr_exception_c(struct exception_parameter *parameter) { + (void)parameter; + panic("TODO"); +} + +void (*irq_handlers[16])() = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +//referenced in interrupts.asm +void isr_irq_c(uint64_t irq_number) { + if (irq_handlers[irq_number]) + (*irq_handlers[irq_number])(); +} + +void set_irq_handler(uint8_t irq_line, void (*handler)(void)) { + assert(irq_line < 16); + irq_handlers[irq_line] = handler; +} + +enum { + + GDT_AB_CODE_DATA_ACCESSED = 0x01, + GDT_AB_DATA_WRITABLE = 0x02, + + GDT_AB_TYPE_TSS = 0x09, + GDT_AB_TYPE_DATA = 0x10, + GDT_AB_TYPE_CODE = 0x18, + + GDT_AB_DPL_ZERO = 0x00, + GDT_AB_DPL_THREE = 0x60, + + GDT_AB_PRESENT = 0x80, + + GDT_FLAG_LONG = 0x20 + +}; + +struct [[gnu::packed]] gdt_entry { + uint32_t zero; + uint8_t zero_; + uint8_t access_byte; + uint8_t flags; + uint8_t zero__; +}; + +struct [[gnu::packed]] gdt_entry_system { + uint16_t limit_0; + uint16_t base_0; + uint8_t base_16; + uint8_t access_byte; + uint8_t flags; + uint8_t base_24; + uint32_t base_32; + uint32_t zero; +}; + +static struct gdt_entry the_gdt[7]; + +struct [[gnu::packed]] gdt_descriptor { + uint16_t limit; + void *base; +}; + +static struct gdt_descriptor the_gdt_descriptor = { + .limit = 7 * sizeof(struct gdt_entry) - 1, + .base = the_gdt +}; + +struct [[gnu::packed]] tss { + uint32_t zero; + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint64_t zero_; + uint64_t ist1; + uint64_t ist2; + uint64_t ist3; + uint64_t ist4; + uint64_t ist5; + uint64_t ist6; + uint64_t ist7; + uint64_t zero__; + uint16_t zero___; + uint16_t io_permission_map_offset; +}; + +static struct tss the_tss; + +//maybe these should be allocated dynamically with guard pages? +#define INTERRUPT_STACK_SIZE (16 << 20) +static uint8_t interrupt_stack_one[INTERRUPT_STACK_SIZE]; +static uint8_t interrupt_stack_two[INTERRUPT_STACK_SIZE]; + +enum { + + //"interrupt" here just means disable interrupts on entry. + //in our case, we use this for both irq's and exceptions. + IDT_TYPE_INTERRUPT = 0x0e, + + IDT_TYPE_DPL_ZERO = 0x00, + IDT_TYPE_DPL_THREE = 0x60, + + IDT_TYPE_PRESENT = 0x80 + +}; + +struct [[gnu::packed]] idt_entry { + uint16_t offset_0; + uint16_t segment_selector; + uint8_t interrupt_stack_index; + uint8_t type; + uint16_t offset_16; + uint32_t offset_32; + uint32_t zero; +}; + +static struct idt_entry the_idt[256]; + +struct [[gnu::packed]] idt_descriptor { + uint16_t limit; + void *base; +}; + +static struct idt_descriptor the_idt_descriptor = { + .limit = 256 * sizeof(struct idt_entry) - 1, + .base = the_idt +}; + +//defined in interrupts.asm +extern void *isrs[48]; + +//defined in interrupts.asm +void enable_interrupts_asm( + struct gdt_descriptor *gdtr, + struct idt_descriptor *idtr, + uint16_t tssr); + +void enable_interrupts() { + + the_tss.ist1 = (uint64_t)&interrupt_stack_one; + the_tss.ist2 = (uint64_t)&interrupt_stack_two; + + //having the table past the end of the tss will lead to a gpf when the + //cpu tries to access it, which is the same effect as a disabled port. + the_tss.io_permission_map_offset = 256; + + uint64_t tss_base = (uint64_t)&the_tss; + uint16_t tss_limit = sizeof(struct tss) - 1; + + struct gdt_entry_system *tss_entry = (struct gdt_entry_system *)&the_gdt[1]; + tss_entry->limit_0 = tss_limit; + tss_entry->base_0 = tss_base & 0xffff; + tss_entry->base_16 = (tss_base >> 16) & 0xff; + tss_entry->access_byte = + GDT_AB_TYPE_TSS | GDT_AB_DPL_ZERO | GDT_AB_PRESENT; + tss_entry->flags = GDT_FLAG_LONG; + tss_entry->base_24 = (tss_base >> 24) & 0xff; + tss_entry->base_32 = tss_base >> 32; + + struct gdt_entry *kernel_code_entry = &the_gdt[5]; + kernel_code_entry->access_byte = + GDT_AB_CODE_DATA_ACCESSED | GDT_AB_TYPE_CODE | + GDT_AB_DPL_ZERO | GDT_AB_PRESENT; + kernel_code_entry->flags = GDT_FLAG_LONG; + + struct gdt_entry *kernel_data_entry = &the_gdt[6]; + kernel_data_entry->access_byte = + GDT_AB_CODE_DATA_ACCESSED | GDT_AB_DATA_WRITABLE | + GDT_AB_TYPE_DATA | GDT_AB_DPL_ZERO | GDT_AB_PRESENT; + kernel_data_entry->flags = GDT_FLAG_LONG; + + for (int i = 0; i < 48; ++i) { + uint64_t offset = (uint64_t)isrs[i]; + the_idt[i].offset_0 = offset & 0xffff; + the_idt[i].segment_selector = 0x0028; + the_idt[i].interrupt_stack_index = i < 32 ? 0x02 : 0x01; + the_idt[i].type = + IDT_TYPE_INTERRUPT | IDT_TYPE_DPL_ZERO | IDT_TYPE_PRESENT; + the_idt[i].offset_16 = (offset >> 16) & 0xffff; + the_idt[i].offset_32 = offset >> 32; + } + + enable_interrupts_asm(&the_gdt_descriptor, &the_idt_descriptor, 0x08); + +} diff --git a/kernel/src/paging.c b/kernel/src/paging.c index 8450e11..f03bff4 100644 --- a/kernel/src/paging.c +++ b/kernel/src/paging.c @@ -16,6 +16,7 @@ */ #include +#include #define MAX_PHYSICAL_GB 64ULL @@ -102,16 +103,10 @@ void map_in_kernel_page_table( int writable, int executable) { uint64_t virtual_base_u64 = (uint64_t)virtual_base; - - //should probably die in this case - if (virtual_base_u64 < 0xffffffffc0000000) - return; + assert(virtual_base_u64 >= 0xffffffffc0000000); uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12; - - //should probably die in this case too - if (kernel_p1s[p1s_index] != 0) - return; + assert(kernel_p1s[p1s_index] == 0); kernel_p1s[p1s_index] = physical_base | (writable ? 0x3 : 0x1) | diff --git a/kernel/src/panic.c b/kernel/src/panic.c new file mode 100644 index 0000000..ad9f0e6 --- /dev/null +++ b/kernel/src/panic.c @@ -0,0 +1,30 @@ +/* Calcite, kernel/src/panic.c + * Copyright 2025 Benji Dial + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +[[noreturn]] void panic_core( + const char *file, const char *function, int line, const char *message) { + + //TODO + + (void)file; + (void)function; + (void)line; + (void)message; + while (1) + __asm__ ("hlt"); + +}