diff options
author | dzwdz | 2021-07-18 14:16:24 +0200 |
---|---|---|
committer | dzwdz | 2021-07-18 14:16:24 +0200 |
commit | ed12953042ec38244f3ab93d67f4c2dea6f1fee3 (patch) | |
tree | 7984f01c156535fb57104db9cbcf0f8a9112b7ed /src/kernel | |
parent | e333a43fb7c1e39418cfcc1be660d5006903c42a (diff) |
move all kernel sources to src/kernel/
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/arch/generic.h | 17 | ||||
-rw-r--r-- | src/kernel/arch/i386/boot.c | 33 | ||||
-rw-r--r-- | src/kernel/arch/i386/boot.s | 16 | ||||
-rw-r--r-- | src/kernel/arch/i386/gdt.h | 16 | ||||
-rw-r--r-- | src/kernel/arch/i386/gdt/farjump.s | 9 | ||||
-rw-r--r-- | src/kernel/arch/i386/gdt/gdt.c | 127 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/idt.c | 75 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/idt.h | 3 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/isr.c | 24 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/isr.h | 15 | ||||
-rw-r--r-- | src/kernel/arch/i386/log.c | 6 | ||||
-rw-r--r-- | src/kernel/arch/i386/multiboot.h | 31 | ||||
-rw-r--r-- | src/kernel/arch/i386/multiboot.s | 19 | ||||
-rw-r--r-- | src/kernel/arch/i386/sysenter.h | 2 | ||||
-rw-r--r-- | src/kernel/arch/i386/sysenter.s | 31 | ||||
-rw-r--r-- | src/kernel/arch/i386/tty.c | 36 | ||||
-rw-r--r-- | src/kernel/arch/i386/tty.h | 6 | ||||
-rw-r--r-- | src/kernel/arch/log.h | 28 | ||||
-rw-r--r-- | src/kernel/main.c | 2 | ||||
-rw-r--r-- | src/kernel/mem.c | 2 | ||||
-rw-r--r-- | src/kernel/panic.h | 2 | ||||
-rw-r--r-- | src/kernel/proc.c | 2 |
22 files changed, 498 insertions, 4 deletions
diff --git a/src/kernel/arch/generic.h b/src/kernel/arch/generic.h new file mode 100644 index 0000000..17980fd --- /dev/null +++ b/src/kernel/arch/generic.h @@ -0,0 +1,17 @@ +#pragma once + +#include <kernel/arch/log.h> + +// i have no idea where else to put it +// some code assumes that it's a power of 2 +#define PAGE_SIZE 4096 + +// src/arch/i386/boot.s +extern void stack_top; + +__attribute__((noreturn)) +void halt_cpu(); + +// src/arch/i386/sysenter.s +void sysexit(void (*fun)(), void *stack_top); +void sysenter_setup(); diff --git a/src/kernel/arch/i386/boot.c b/src/kernel/arch/i386/boot.c new file mode 100644 index 0000000..2bb18be --- /dev/null +++ b/src/kernel/arch/i386/boot.c @@ -0,0 +1,33 @@ +#include <kernel/arch/generic.h> +#include <kernel/arch/i386/gdt.h> +#include <kernel/arch/i386/interrupts/idt.h> +#include <kernel/arch/i386/multiboot.h> +#include <kernel/arch/i386/sysenter.h> +#include <kernel/arch/i386/tty.h> +#include <kernel/main.h> +#include <kernel/panic.h> + +void kmain_early(struct multiboot_info *multiboot) { + struct kmain_info info; + + // setup some basic stuff + tty_clear(); + log_const("gdt..."); + gdt_init(); + log_const("idt..."); + idt_init(); + log_const("sysenter..."); + sysenter_setup(); + + { // find the init module + struct multiboot_mod *module = &multiboot->mods[0]; + if (multiboot->mods_count < 1) { + log_const("can't find init! "); + panic(); + } + info.init.at = module->start; + info.init.size = module->end - module->start; + } + + kmain(info); +} diff --git a/src/kernel/arch/i386/boot.s b/src/kernel/arch/i386/boot.s new file mode 100644 index 0000000..74de9b7 --- /dev/null +++ b/src/kernel/arch/i386/boot.s @@ -0,0 +1,16 @@ +.section .text +.global _start +.type _start, @function +_start: + mov $stack_top, %esp + push %ebx // address of the Multiboot struct + call kmain_early + +.global halt_cpu +.type halt_cpu, @function +halt_cpu: + cli +1: hlt + jmp 1b + +.size _start, . - _start diff --git a/src/kernel/arch/i386/gdt.h b/src/kernel/arch/i386/gdt.h new file mode 100644 index 0000000..bcb9870 --- /dev/null +++ b/src/kernel/arch/i386/gdt.h @@ -0,0 +1,16 @@ +#pragma once + +enum { + SEG_null, + // order dictated by SYSENTER + SEG_r0code, + SEG_r0data, + SEG_r3code, + SEG_r3data, + SEG_TSS, + + SEG_end +}; + +void gdt_init(); +void gdt_farjump(int segment); diff --git a/src/kernel/arch/i386/gdt/farjump.s b/src/kernel/arch/i386/gdt/farjump.s new file mode 100644 index 0000000..85d8ba5 --- /dev/null +++ b/src/kernel/arch/i386/gdt/farjump.s @@ -0,0 +1,9 @@ +.section .text +.global gdt_farjump +.type gdt_farjump, @function +gdt_farjump: + /* retf pops off the return address and code segment off the stack. + * it turns out that in the i386 cdecl calling convention they're in + * the correct place already. + */ + retf diff --git a/src/kernel/arch/i386/gdt/gdt.c b/src/kernel/arch/i386/gdt/gdt.c new file mode 100644 index 0000000..2935e6e --- /dev/null +++ b/src/kernel/arch/i386/gdt/gdt.c @@ -0,0 +1,127 @@ +#include <kernel/arch/generic.h> +#include <kernel/arch/i386/gdt.h> +#include <kernel/util.h> +#include <stdbool.h> +#include <stdint.h> + + +struct gdt_entry { + uint64_t limit_low : 16; + uint64_t base_low : 24; + uint64_t accessed : 1; // set by the processor + // CODE | DATA + uint64_t rw : 1; // readable? | writeable? + uint64_t conforming : 1; // conforming? | expands down? + uint64_t code : 1; // 1 | 0 + + uint64_t codeordata : 1; // 1 for everything other than TSS and LDT + uint64_t ring : 2; + uint64_t present : 1; // always 1 + uint64_t limit_high : 4; + uint64_t available : 1; // ??? + uint64_t long_mode : 1; + uint64_t x32 : 1; + uint64_t gran : 1; // 1 - 4kb, 0 - 1b + uint64_t base_high : 8; +} __attribute__((packed)); + +struct tss_entry { + uint32_t _unused0; + uint32_t esp0; // kernel mode stack pointer + uint32_t ss0; // kernel mode stack segment + uint8_t _unused1[0x5c]; +} __attribute__((packed)); + +struct lgdt_arg { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +static struct gdt_entry GDT[SEG_end]; +static struct tss_entry TSS; +static struct lgdt_arg lgdt_arg; // probably doesn't need to be global + +static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code); +static void gdt_prepare(); +static void gdt_load(); + + +static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code) { + *entry = (struct gdt_entry) { + // set up the identity mapping + .limit_low = 0xFFFF, + .limit_high = 0xF, + .gran = 1, // 4KB * 0xFFFFF = (almost) 4GB + .base_low = 0, + .base_high = 0, + + .ring = ring, + .code = code, + + .accessed = 0, + .rw = 1, + .conforming = 0, + .codeordata = 1, + .present = 1, + .long_mode = 0, // ??? + .available = 1, // ??? + .x32 = 1, + }; +} + +static void gdt_prepare() { + GDT[SEG_null].present = 0; + + gdt_fillout(&GDT[SEG_r0code], 0, true); + gdt_fillout(&GDT[SEG_r0data], 0, false); + gdt_fillout(&GDT[SEG_r3code], 3, true); + gdt_fillout(&GDT[SEG_r3data], 3, false); + + // tss + memset(&TSS, 0, sizeof(TSS)); + TSS.ss0 = SEG_r0data << 3; // kernel data segment + TSS.esp0 = (uintptr_t) &stack_top; + + GDT[SEG_TSS] = (struct gdt_entry) { + .limit_low = sizeof(TSS), + .limit_high = sizeof(TSS) >> 16, + .gran = 0, + .base_low = (uintptr_t) &TSS, + .base_high = ((uintptr_t) &TSS) >> 24, + + .accessed = 1, // 1 for TSS + .rw = 0, // 1 busy / 0 not busy + .conforming = 0, // 0 for TSS + .code = 1, // 32bit + .codeordata = 0, // is a system entry + .ring = 3, + .present = 1, + .available = 0, // 0 for TSS + .long_mode = 0, + .x32 = 0, // idk + }; +} + +static void gdt_load() { + lgdt_arg.limit = sizeof(GDT) - 1; + lgdt_arg.base = (uintptr_t) &GDT; + asm("lgdt (%0)" + : : "r" (&lgdt_arg) : "memory"); + + asm("ltr %%ax" + : : "a" (SEG_TSS << 3 | 3) : "memory"); + + // update all segment registers + gdt_farjump(SEG_r0code << 3); + asm("mov %0, %%ds;" + "mov %0, %%ss;" + "mov %0, %%es;" + "mov %0, %%fs;" + "mov %0, %%gs;" + : : "r" (SEG_r0data << 3) : "memory"); +} + +void gdt_init() { + gdt_prepare(); + gdt_load(); +} diff --git a/src/kernel/arch/i386/interrupts/idt.c b/src/kernel/arch/i386/interrupts/idt.c new file mode 100644 index 0000000..5573928 --- /dev/null +++ b/src/kernel/arch/i386/interrupts/idt.c @@ -0,0 +1,75 @@ +#include <kernel/arch/i386/gdt.h> +#include <kernel/arch/i386/interrupts/idt.h> +#include <kernel/arch/i386/interrupts/isr.h> +#include <kernel/panic.h> +#include <stdbool.h> +#include <stdint.h> + +struct idt_entry { + uint16_t offset_low ; + uint16_t code_seg ; + uint8_t zero ; // unused, has to be 0 + uint8_t type : 4; // 16/32 bit, task/interrupt/task gate + uint8_t storage : 1; // 0 for interrupt/trap gates + uint8_t ring : 2; + uint8_t present : 1; + uint16_t offset_high ; +} __attribute__((packed)); + +// is exactly the same as lgdt_arg, i should combine them into a single struct +// later +struct lidt_arg { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + + +static struct idt_entry IDT[256]; +static struct lidt_arg lidt_arg; + +static inline void idt_add(uint8_t num, bool user, void (*isr)); +static void idt_prepare(); +static void idt_load(); +static void idt_test(); + + +static inline void idt_add(uint8_t num, bool user, void (*isr)) { + uintptr_t offset = (uintptr_t) isr; + + IDT[num] = (struct idt_entry) { + .offset_low = offset, + .offset_high = offset >> 16, + .code_seg = SEG_r0code << 3, + .zero = 0, + .present = 1, + .ring = user ? 3 : 0, + .storage = 0, + .type = 0xE, // 32-bit interrupt gate + }; +} + +static void idt_prepare() { + for (int i = 0; i < 256; i++) + IDT[i].present = 0; + + idt_add(0x08, false, isr_double_fault); + idt_add(0x0d, false, isr_general_protection_fault); + idt_add(0x34, false, isr_test_interrupt); +} + +static void idt_load() { + lidt_arg.limit = sizeof(IDT) - 1; + lidt_arg.base = (uintptr_t) &IDT; + asm("lidt (%0)" : : "r" (&lidt_arg) : "memory"); +} + +static void idt_test() { + asm("int $0x34" : : : "memory"); + if (!isr_test_interrupt_called) panic(); +} + +void idt_init() { + idt_prepare(); + idt_load(); + idt_test(); +} diff --git a/src/kernel/arch/i386/interrupts/idt.h b/src/kernel/arch/i386/interrupts/idt.h new file mode 100644 index 0000000..5627657 --- /dev/null +++ b/src/kernel/arch/i386/interrupts/idt.h @@ -0,0 +1,3 @@ +#pragma once + +void idt_init(); diff --git a/src/kernel/arch/i386/interrupts/isr.c b/src/kernel/arch/i386/interrupts/isr.c new file mode 100644 index 0000000..d2e3d0f --- /dev/null +++ b/src/kernel/arch/i386/interrupts/isr.c @@ -0,0 +1,24 @@ +#include <kernel/arch/i386/interrupts/isr.h> +#include <kernel/arch/log.h> +#include <kernel/panic.h> +#include <stdbool.h> +#include <stdint.h> + +bool isr_test_interrupt_called = false; + +__attribute__((interrupt)) +void isr_double_fault(struct interrupt_frame *frame) { + log_const("#DF"); + panic(); +} + +__attribute__((interrupt)) +void isr_general_protection_fault(struct interrupt_frame *frame) { + log_const("#GP"); + panic(); +} + +__attribute__((interrupt)) +void isr_test_interrupt(struct interrupt_frame *frame) { + isr_test_interrupt_called = true; +} diff --git a/src/kernel/arch/i386/interrupts/isr.h b/src/kernel/arch/i386/interrupts/isr.h new file mode 100644 index 0000000..150fc46 --- /dev/null +++ b/src/kernel/arch/i386/interrupts/isr.h @@ -0,0 +1,15 @@ +#pragma once +#include <stdbool.h> + +struct interrupt_frame; + +extern bool isr_test_interrupt_called; // used in the self-test in idt.c + +__attribute__((interrupt)) +void isr_double_fault(struct interrupt_frame *frame); + +__attribute__((interrupt)) +void isr_general_protection_fault(struct interrupt_frame *frame); + +__attribute__((interrupt)) +void isr_test_interrupt(struct interrupt_frame *frame); diff --git a/src/kernel/arch/i386/log.c b/src/kernel/arch/i386/log.c new file mode 100644 index 0000000..84f8b04 --- /dev/null +++ b/src/kernel/arch/i386/log.c @@ -0,0 +1,6 @@ +#include <kernel/arch/i386/tty.h> +#include <kernel/arch/log.h> + +void log_write(const char *buf, size_t len) { + tty_write(buf, len); +} diff --git a/src/kernel/arch/i386/multiboot.h b/src/kernel/arch/i386/multiboot.h new file mode 100644 index 0000000..f030247 --- /dev/null +++ b/src/kernel/arch/i386/multiboot.h @@ -0,0 +1,31 @@ +#pragma once +#include <stdint.h> + +// TODO assert that pointers have 4 bytes. + +struct multiboot_mod { + void *start; + void *end; + const char *str; + uint32_t _reserved; +} __attribute__((packed)); + +struct multiboot_info { + uint32_t flag_mem : 1; + uint32_t flag_boot_device : 1; + uint32_t flag_cmdline : 1; + uint32_t flag_mods : 1; + uint32_t _flag_other : 28; // unimplemented + + uint32_t mem_lower; + uint32_t mem_upper; + + uint32_t boot_device; + + const char *cmdline; + + uint32_t mods_count; + struct multiboot_mod *mods; + + // [...] +} __attribute__((packed)); diff --git a/src/kernel/arch/i386/multiboot.s b/src/kernel/arch/i386/multiboot.s new file mode 100644 index 0000000..db502cc --- /dev/null +++ b/src/kernel/arch/i386/multiboot.s @@ -0,0 +1,19 @@ +.set MAGIC, 0x1BADB002 +/* TODO set bss_end_addr, so the init module doesn't get overriden by the stack */ + +/* 1<<0 - align modules on page boundaries. + 1<<16 - enable manual addressing */ +.set FLAGS, 1<<0 | 1<<16 +.set CHECKSUM, -(MAGIC + FLAGS) + +.section .multiboot +.align 4 +multiboot_header: + .long MAGIC + .long FLAGS + .long CHECKSUM + .long multiboot_header // header_addr + .long multiboot_header // load_addr + .long _data_end // load_end_addr + .long _bss_end // bss_end_addr + .long _start // entry_addr diff --git a/src/kernel/arch/i386/sysenter.h b/src/kernel/arch/i386/sysenter.h new file mode 100644 index 0000000..b531fe8 --- /dev/null +++ b/src/kernel/arch/i386/sysenter.h @@ -0,0 +1,2 @@ +#pragma once +void sysenter_setup(); diff --git a/src/kernel/arch/i386/sysenter.s b/src/kernel/arch/i386/sysenter.s new file mode 100644 index 0000000..666c0f0 --- /dev/null +++ b/src/kernel/arch/i386/sysenter.s @@ -0,0 +1,31 @@ +/* arch/i386/gdt.c */ +.set SEG_r0code, 1 +.set SEG_r3code, 3 +.set SEG_r3data, 4 + +.set IA32_SYSENTER_CS, 0x174 + +.section .text +.global sysexit +.type sysexit, @function +sysexit: + pop %ecx + pop %edx + + mov $(SEG_r3data << 3 | 3), %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + sysexit + + +.global sysenter_setup +.type sysenter_setup, @function +sysenter_setup: + xor %edx, %edx + mov $(SEG_r0code << 3), %eax + mov $IA32_SYSENTER_CS, %ecx + wrmsr + ret diff --git a/src/kernel/arch/i386/tty.c b/src/kernel/arch/i386/tty.c new file mode 100644 index 0000000..b9ebdb6 --- /dev/null +++ b/src/kernel/arch/i386/tty.c @@ -0,0 +1,36 @@ +#include <kernel/arch/i386/tty.h> + +struct vga_cell { + unsigned char c; + unsigned char style; +} __attribute__((__packed__)); + +static const size_t vga_len = 80 * 25; +static struct vga_cell *vga = (void*) 0xB8000; +static size_t vga_pos = 0; + +static void tty_scroll() { + for (int i = 0; i < vga_len - 80; i++) { + vga[i] = vga[i + 80]; + } + vga_pos -= 80; +} + +void tty_putchar(char c) +{ + if (vga_pos >= vga_len - 80) tty_scroll(); + vga[vga_pos++].c = c; +} + +void tty_write(const char *buf, size_t len) +{ + for (size_t i = 0; i < len; i++) { + tty_putchar(buf[i]); + } +} + +void tty_clear() { + for (size_t i = 0; i < vga_len; i++) + vga[i].c = ' '; + vga_pos = 0; +} diff --git a/src/kernel/arch/i386/tty.h b/src/kernel/arch/i386/tty.h new file mode 100644 index 0000000..cbb8efc --- /dev/null +++ b/src/kernel/arch/i386/tty.h @@ -0,0 +1,6 @@ +#pragma once +#include <stddef.h> + +void tty_putchar(char c); +void tty_write(const char *buf, size_t len); +void tty_clear(); diff --git a/src/kernel/arch/log.h b/src/kernel/arch/log.h new file mode 100644 index 0000000..0a5d5a0 --- /dev/null +++ b/src/kernel/arch/log.h @@ -0,0 +1,28 @@ +#pragma once +#include <stddef.h> + +void log_write(const char *buf, size_t len); + +inline void log_hex(const char *buf, size_t len) { + char hex[2]; + for (size_t i = 0; i < len; i++) { + hex[0] = (buf[i] & 0xF0) >> 4; + hex[0] += '0'; + if (hex[0] > '9') + hex[0] += 'a' - '9' - 1; + + hex[1] = buf[i] & 0xF; + hex[1] += '0'; + if (hex[1] > '9') + hex[1] += 'a' - '9' - 1; + + log_write(hex, 2); + } +} + +// used for static strings +#define log_const(str) log_write(str, sizeof(str) - 1) + +// very hacky, shouldn't be actually used - only for debugging +// prints backwards +#define log_var_dont_use(var) log_hex((void*)&var, sizeof(var)) diff --git a/src/kernel/main.c b/src/kernel/main.c index 761e499..677faaf 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -1,4 +1,4 @@ -#include <arch/generic.h> +#include <kernel/arch/generic.h> #include <kernel/main.h> #include <kernel/mem.h> #include <kernel/panic.h> diff --git a/src/kernel/mem.c b/src/kernel/mem.c index b455b25..ba5b504 100644 --- a/src/kernel/mem.c +++ b/src/kernel/mem.c @@ -1,4 +1,4 @@ -#include <arch/generic.h> +#include <kernel/arch/generic.h> #include <kernel/mem.h> #include <stdint.h> diff --git a/src/kernel/panic.h b/src/kernel/panic.h index 12d26fd..ea2cef5 100644 --- a/src/kernel/panic.h +++ b/src/kernel/panic.h @@ -1,5 +1,5 @@ #pragma once -#include <arch/generic.h> +#include <kernel/arch/generic.h> // dumb c shit #define panic_tostr2(x) #x diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 7d42bce..4a931a5 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -1,4 +1,4 @@ -#include <arch/generic.h> +#include <kernel/arch/generic.h> #include <kernel/mem.h> #include <kernel/proc.h> |