From d093a8eea6bfb0ff7e621e9ba3307dae698322aa Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sat, 10 Jul 2021 16:37:45 +0200 Subject: separate the source code from object files; more modular Makefile --- .gitignore | 4 +- Makefile | 42 +++++++++-------- kernel/gdt.c | 121 ------------------------------------------------ kernel/gdt.h | 15 ------ kernel/idt.c | 75 ------------------------------ kernel/idt.h | 3 -- kernel/isr.c | 24 ---------- kernel/isr.h | 15 ------ kernel/main.c | 33 ------------- kernel/mem.c | 19 -------- kernel/mem.h | 12 ----- kernel/panic.h | 14 ------ kernel/proc.c | 19 -------- kernel/proc.h | 13 ------ kernel/tty.c | 38 --------------- kernel/tty.h | 8 ---- kernel/util.c | 9 ---- kernel/util.h | 4 -- platform/asm.h | 9 ---- platform/boot.s | 47 ------------------- platform/sysenter.s | 31 ------------- src/kernel/gdt.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ src/kernel/gdt.h | 15 ++++++ src/kernel/idt.c | 75 ++++++++++++++++++++++++++++++ src/kernel/idt.h | 3 ++ src/kernel/isr.c | 24 ++++++++++ src/kernel/isr.h | 15 ++++++ src/kernel/main.c | 33 +++++++++++++ src/kernel/mem.c | 19 ++++++++ src/kernel/mem.h | 12 +++++ src/kernel/panic.h | 14 ++++++ src/kernel/proc.c | 19 ++++++++ src/kernel/proc.h | 13 ++++++ src/kernel/tty.c | 38 +++++++++++++++ src/kernel/tty.h | 8 ++++ src/kernel/util.c | 9 ++++ src/kernel/util.h | 4 ++ src/platform/asm.h | 9 ++++ src/platform/boot.s | 47 +++++++++++++++++++ src/platform/sysenter.s | 31 +++++++++++++ 40 files changed, 532 insertions(+), 532 deletions(-) delete mode 100644 kernel/gdt.c delete mode 100644 kernel/gdt.h delete mode 100644 kernel/idt.c delete mode 100644 kernel/idt.h delete mode 100644 kernel/isr.c delete mode 100644 kernel/isr.h delete mode 100644 kernel/main.c delete mode 100644 kernel/mem.c delete mode 100644 kernel/mem.h delete mode 100644 kernel/panic.h delete mode 100644 kernel/proc.c delete mode 100644 kernel/proc.h delete mode 100644 kernel/tty.c delete mode 100644 kernel/tty.h delete mode 100644 kernel/util.c delete mode 100644 kernel/util.h delete mode 100644 platform/asm.h delete mode 100644 platform/boot.s delete mode 100644 platform/sysenter.s create mode 100644 src/kernel/gdt.c create mode 100644 src/kernel/gdt.h create mode 100644 src/kernel/idt.c create mode 100644 src/kernel/idt.h create mode 100644 src/kernel/isr.c create mode 100644 src/kernel/isr.h create mode 100644 src/kernel/main.c create mode 100644 src/kernel/mem.c create mode 100644 src/kernel/mem.h create mode 100644 src/kernel/panic.h create mode 100644 src/kernel/proc.c create mode 100644 src/kernel/proc.h create mode 100644 src/kernel/tty.c create mode 100644 src/kernel/tty.h create mode 100644 src/kernel/util.c create mode 100644 src/kernel/util.h create mode 100644 src/platform/asm.h create mode 100644 src/platform/boot.s create mode 100644 src/platform/sysenter.s diff --git a/.gitignore b/.gitignore index eb837cc..89f9ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ -*.bin -boot.iso -*.o +out/ diff --git a/Makefile b/Makefile index da06cb5..35bb882 100644 --- a/Makefile +++ b/Makefile @@ -2,41 +2,43 @@ AS = i686-elf-as CC = i686-elf-gcc CFLAGS = -std=gnu99 -ffreestanding -O2 -Wall -Wextra CFLAGS += -mgeneral-regs-only -CFLAGS += -I. +CFLAGS += -Isrc/ LFLAGS = -ffreestanding -O2 -nostdlib -lgcc QFLAGS = -no-reboot -d guest_errors,int,pcall,cpu_reset -OBJ = $(patsubst %.s,%.o,$(wildcard platform/*.s)) -OBJ += $(patsubst %.c,%.o,$(wildcard kernel/*.c)) +OBJ = $(patsubst src/%.s,out/obj/%.s.o,$(shell find src/ -type f -name '*.s')) +OBJ += $(patsubst src/%.c,out/obj/%.c.o,$(shell find src/ -type f -name '*.c')) .PHONY: boot debug clean -boot: kernel.bin - qemu-system-i386 -kernel kernel.bin $(QFLAGS) -no-shutdown +boot: out/fs/boot/kernel.bin + qemu-system-i386 -kernel $< $(QFLAGS) -no-shutdown debug: kernel.bin - qemu-system-i386 -kernel kernel.bin $(QFLAGS) -s -S & + qemu-system-i386 -kernel $< $(QFLAGS) -s -S & sleep 1 gdb clean: - rm -vf kernel.bin - rm -vf **/*.o - -boot.iso: kernel.bin grub.cfg - mkdir iso_tmp - mkdir -p iso_tmp/boot/grub - cp kernel.bin iso_tmp/boot - cp grub.cfg iso_tmp/boot/grub - grub-mkrescue -o $@ iso_tmp - rm -rv iso_tmp - -kernel.bin: $(OBJ) + rm -rv out/ + + +out/boot.iso: out/fs/boot/kernel.bin out/fs/boot/grub/grub.cfg + grub-mkrescue -o $@ out/fs/ + +out/fs/boot/grub/grub.cfg: grub.cfg + @mkdir -p $(@D) + cp $< $@ + +out/fs/boot/kernel.bin: $(OBJ) + @mkdir -p $(@D) $(CC) $(LFLAGS) -T linker.ld $^ -o $@ grub-file --is-x86-multiboot $@ -platform/%.o: platform/%.s +out/obj/%.s.o: src/%.s + @mkdir -p $(@D) $(AS) $^ -o $@ -%.o: %.c +out/obj/%.c.o: src/%.c + @mkdir -p $(@D) $(CC) $(CFLAGS) -c $^ -o $@ diff --git a/kernel/gdt.c b/kernel/gdt.c deleted file mode 100644 index b0d6320..0000000 --- a/kernel/gdt.c +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#include - -extern void stack_top; // platform/boot.s - -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 prev_tss; // unused - uint32_t esp0; // kernel mode stack pointer - uint32_t ss0; // kernel mode stack segment - // total size = 0x68 (?) - 3 * sizeof(uint32_t) = 5c - uint8_t _unused[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_prepare(); -static void gdt_load(); - - -static void gdt_prepare() { - GDT[SEG_null].present = 0; - - GDT[SEG_r0data].limit_low = 0xFFFF; - GDT[SEG_r0data].limit_high = 0xF; - GDT[SEG_r0data].gran = 1; // 4KB * 0xFFFFF = (almost) 4GB - GDT[SEG_r0data].base_low = 0; - GDT[SEG_r0data].base_high = 0; - GDT[SEG_r0data].accessed = 0; - GDT[SEG_r0data].rw = 1; - GDT[SEG_r0data].conforming = 0; - GDT[SEG_r0data].code = 0; - GDT[SEG_r0data].codeordata = 1; - GDT[SEG_r0data].ring = 0; - GDT[SEG_r0data].present = 1; - GDT[SEG_r0data].long_mode = 0; // ??? - GDT[SEG_r0data].available = 1; // ??? - GDT[SEG_r0data].x32 = 1; - - // copy to r0 code - GDT[SEG_r0code] = GDT[SEG_r0data]; - GDT[SEG_r0code].code = 1; - - GDT[SEG_r3data] = GDT[SEG_r0data]; - GDT[SEG_r3data].ring = 3; - GDT[SEG_r3code] = GDT[SEG_r0code]; - GDT[SEG_r3code].ring = 3; - - // tss - memset(&TSS, 0, sizeof(TSS)); - TSS.ss0 = SEG_r0data << 3; // kernel data segment - TSS.esp0 = (uint32_t) &stack_top; - - GDT[SEG_TSS].limit_low = sizeof(TSS); - GDT[SEG_TSS].base_low = (uint32_t) &TSS; - GDT[SEG_TSS].accessed = 1; // 1 for TSS - GDT[SEG_TSS].rw = 0; // 1 busy / 0 not busy - GDT[SEG_TSS].conforming = 0; // 0 for TSS - GDT[SEG_TSS].code = 1; // 32bit - GDT[SEG_TSS].codeordata = 0; // is a system entry - GDT[SEG_TSS].ring = 3; - GDT[SEG_TSS].present = 1; - GDT[SEG_TSS].limit_high = (sizeof(TSS) >> 16) & 0xf; - GDT[SEG_TSS].available = 0; // 0 for TSS - GDT[SEG_TSS].long_mode = 0; - GDT[SEG_TSS].x32 = 0; // idk - GDT[SEG_TSS].gran = 0; - GDT[SEG_TSS].base_high = (((uint32_t) &TSS) >> 24) & 0xff; -} - -void change_cs(int seg); // temporary - -static void gdt_load() { - lgdt_arg.limit = sizeof(GDT) - 1; - lgdt_arg.base = (uint32_t) &GDT; - asm("lgdt (%0)" - : : "r" (&lgdt_arg) : "memory"); - asm("ltr %%ax" - : : "a" (SEG_TSS << 3 | 3) : "memory"); - - // update all segment registers - change_cs(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/kernel/gdt.h b/kernel/gdt.h deleted file mode 100644 index 9e5a6a5..0000000 --- a/kernel/gdt.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -enum { - SEG_null, - // order dictated by SYSENTER - SEG_r0code, - SEG_r0data, - SEG_r3code, - SEG_r3data, - SEG_TSS, - - SEG_end -}; - -void gdt_init(); diff --git a/kernel/idt.c b/kernel/idt.c deleted file mode 100644 index 65f511f..0000000 --- a/kernel/idt.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include -#include -#include - -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/kernel/idt.h b/kernel/idt.h deleted file mode 100644 index 5627657..0000000 --- a/kernel/idt.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void idt_init(); diff --git a/kernel/isr.c b/kernel/isr.c deleted file mode 100644 index b715de0..0000000 --- a/kernel/isr.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include -#include - -bool isr_test_interrupt_called = false; - -__attribute__((interrupt)) -void isr_double_fault(struct interrupt_frame *frame) { - tty_const("#DF"); - panic(); -} - -__attribute__((interrupt)) -void isr_general_protection_fault(struct interrupt_frame *frame) { - tty_const("#GP"); - panic(); -} - -__attribute__((interrupt)) -void isr_test_interrupt(struct interrupt_frame *frame) { - isr_test_interrupt_called = true; -} diff --git a/kernel/isr.h b/kernel/isr.h deleted file mode 100644 index 150fc46..0000000 --- a/kernel/isr.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include - -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/kernel/main.c b/kernel/main.c deleted file mode 100644 index 0193514..0000000 --- a/kernel/main.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -void r3_test(); - -void kmain() -{ - tty_clear(); - tty_const("gdt..."); - gdt_init(); - tty_const("idt..."); - idt_init(); - tty_const("sysenter..."); - sysenter_setup(); - tty_const("mem..."); - mem_init(); - - tty_const("creating process..."); - struct process *proc = process_new(r3_test); - tty_const("switching..."); - process_switch(proc); -} - -void r3_test() { - tty_const("ok"); - asm("cli"); - panic(); -} diff --git a/kernel/mem.c b/kernel/mem.c deleted file mode 100644 index 08eb285..0000000 --- a/kernel/mem.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -extern void *_kernel_end; -static void *highest_page; - -void mem_init() { - highest_page = &_kernel_end; -} - -void *page_alloc(size_t pages) { - void *bottom = highest_page; - highest_page += pages * PAGE_SIZE; - return bottom; -} - -// frees `pages` consecutive pages starting from *first -void page_free(void *first, size_t pages) { - // not implemented -} diff --git a/kernel/mem.h b/kernel/mem.h deleted file mode 100644 index e8e6a4f..0000000 --- a/kernel/mem.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include - -#define PAGE_SIZE 4096 - -void mem_init(); - -// allocates `pages` consecutive pages -void *page_alloc(size_t pages); - -// frees `pages` consecutive pages starting from *first -void page_free(void *first, size_t pages); diff --git a/kernel/panic.h b/kernel/panic.h deleted file mode 100644 index 7d2ea5f..0000000 --- a/kernel/panic.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include - -// dumb c shit -#define panic_tostr2(x) #x -#define panic_tostr(x) panic_tostr2(x) - -#define panic() do { \ - tty_const(" PANIC! at the "); \ - tty_const(__func__); \ - tty_const(" (" __FILE__ ":" panic_tostr(__LINE__) ") "); \ - halt_cpu(); \ -} while (0) diff --git a/kernel/proc.c b/kernel/proc.c deleted file mode 100644 index 776238c..0000000 --- a/kernel/proc.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#include - -struct process *process_current; - -struct process *process_new(void *eip) { - struct process *proc; - proc = page_alloc(1); // TODO kmalloc - proc->stack_top = proc->esp = page_alloc(1) + 1 * PAGE_SIZE; - proc->eip = eip; - - return proc; -} - -void process_switch(struct process *proc) { - process_current = proc; - sysexit(proc->eip, proc->esp); -} diff --git a/kernel/proc.h b/kernel/proc.h deleted file mode 100644 index 976b275..0000000 --- a/kernel/proc.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -struct process { - void *stack_top; - void *esp; - - void *eip; -}; - -extern struct process *process_current; - -struct process *process_new(void *eip); -void process_switch(struct process *proc); diff --git a/kernel/tty.c b/kernel/tty.c deleted file mode 100644 index 5c4c47d..0000000 --- a/kernel/tty.c +++ /dev/null @@ -1,38 +0,0 @@ -/* will be moved to userspace later on */ - -#include - -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/kernel/tty.h b/kernel/tty.h deleted file mode 100644 index 3dc1525..0000000 --- a/kernel/tty.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include - -void tty_putchar(char c); -void tty_write(const char *buf, size_t len); -void tty_clear(); - -#define tty_const(str) tty_write(str, sizeof(str) - 1) diff --git a/kernel/util.c b/kernel/util.c deleted file mode 100644 index da3ac9d..0000000 --- a/kernel/util.c +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include - -void *memset(void *s, int c, size_t n) { - uint8_t *s2 = s; - for (size_t i = 0; i < n; n++) - s2[i] = c; - return s; -} diff --git a/kernel/util.h b/kernel/util.h deleted file mode 100644 index 688ac63..0000000 --- a/kernel/util.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -void *memset(void *s, int c, size_t n); diff --git a/platform/asm.h b/platform/asm.h deleted file mode 100644 index 6f72617..0000000 --- a/platform/asm.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// boot.s -__attribute__((noreturn)) -void halt_cpu(); - -// sysenter.c -void sysexit(void (*fun)(), void *stack_top); -void sysenter_setup(); diff --git a/platform/boot.s b/platform/boot.s deleted file mode 100644 index d5bfda5..0000000 --- a/platform/boot.s +++ /dev/null @@ -1,47 +0,0 @@ -.set MAGIC, 0x1BADB002 -.set FLAG_ALIGN, 1<<0 /* align modules on page boundaries */ -.set FLAG_MEMINFO, 1<<1 /* memory map */ -.set FLAGS, FLAG_ALIGN | FLAG_MEMINFO -.set CHECKSUM, -(MAGIC + FLAGS) - -.section .multiboot -.align 4 -.long MAGIC -.long FLAGS -.long CHECKSUM - -/* a lil stack */ -.section .bss -.global stack_top -.type stack_top, @object -.align 16 -stack_bottom: -.skip 16384 -stack_top: - - -.section .text -.global _start -.type _start, @function -_start: - mov $stack_top, %esp - call kmain - -.global halt_cpu -.type halt_cpu, @function -halt_cpu: - cli -1: hlt - jmp 1b - -// temporary, will be moved to another file soon -.global change_cs -.type change_cs, @function -change_cs: - /* 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 - -.size _start, . - _start diff --git a/platform/sysenter.s b/platform/sysenter.s deleted file mode 100644 index 270dc08..0000000 --- a/platform/sysenter.s +++ /dev/null @@ -1,31 +0,0 @@ -/* kernel/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/gdt.c b/src/kernel/gdt.c new file mode 100644 index 0000000..b0d6320 --- /dev/null +++ b/src/kernel/gdt.c @@ -0,0 +1,121 @@ +#include +#include +#include + +extern void stack_top; // platform/boot.s + +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 prev_tss; // unused + uint32_t esp0; // kernel mode stack pointer + uint32_t ss0; // kernel mode stack segment + // total size = 0x68 (?) - 3 * sizeof(uint32_t) = 5c + uint8_t _unused[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_prepare(); +static void gdt_load(); + + +static void gdt_prepare() { + GDT[SEG_null].present = 0; + + GDT[SEG_r0data].limit_low = 0xFFFF; + GDT[SEG_r0data].limit_high = 0xF; + GDT[SEG_r0data].gran = 1; // 4KB * 0xFFFFF = (almost) 4GB + GDT[SEG_r0data].base_low = 0; + GDT[SEG_r0data].base_high = 0; + GDT[SEG_r0data].accessed = 0; + GDT[SEG_r0data].rw = 1; + GDT[SEG_r0data].conforming = 0; + GDT[SEG_r0data].code = 0; + GDT[SEG_r0data].codeordata = 1; + GDT[SEG_r0data].ring = 0; + GDT[SEG_r0data].present = 1; + GDT[SEG_r0data].long_mode = 0; // ??? + GDT[SEG_r0data].available = 1; // ??? + GDT[SEG_r0data].x32 = 1; + + // copy to r0 code + GDT[SEG_r0code] = GDT[SEG_r0data]; + GDT[SEG_r0code].code = 1; + + GDT[SEG_r3data] = GDT[SEG_r0data]; + GDT[SEG_r3data].ring = 3; + GDT[SEG_r3code] = GDT[SEG_r0code]; + GDT[SEG_r3code].ring = 3; + + // tss + memset(&TSS, 0, sizeof(TSS)); + TSS.ss0 = SEG_r0data << 3; // kernel data segment + TSS.esp0 = (uint32_t) &stack_top; + + GDT[SEG_TSS].limit_low = sizeof(TSS); + GDT[SEG_TSS].base_low = (uint32_t) &TSS; + GDT[SEG_TSS].accessed = 1; // 1 for TSS + GDT[SEG_TSS].rw = 0; // 1 busy / 0 not busy + GDT[SEG_TSS].conforming = 0; // 0 for TSS + GDT[SEG_TSS].code = 1; // 32bit + GDT[SEG_TSS].codeordata = 0; // is a system entry + GDT[SEG_TSS].ring = 3; + GDT[SEG_TSS].present = 1; + GDT[SEG_TSS].limit_high = (sizeof(TSS) >> 16) & 0xf; + GDT[SEG_TSS].available = 0; // 0 for TSS + GDT[SEG_TSS].long_mode = 0; + GDT[SEG_TSS].x32 = 0; // idk + GDT[SEG_TSS].gran = 0; + GDT[SEG_TSS].base_high = (((uint32_t) &TSS) >> 24) & 0xff; +} + +void change_cs(int seg); // temporary + +static void gdt_load() { + lgdt_arg.limit = sizeof(GDT) - 1; + lgdt_arg.base = (uint32_t) &GDT; + asm("lgdt (%0)" + : : "r" (&lgdt_arg) : "memory"); + asm("ltr %%ax" + : : "a" (SEG_TSS << 3 | 3) : "memory"); + + // update all segment registers + change_cs(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/gdt.h b/src/kernel/gdt.h new file mode 100644 index 0000000..9e5a6a5 --- /dev/null +++ b/src/kernel/gdt.h @@ -0,0 +1,15 @@ +#pragma once + +enum { + SEG_null, + // order dictated by SYSENTER + SEG_r0code, + SEG_r0data, + SEG_r3code, + SEG_r3data, + SEG_TSS, + + SEG_end +}; + +void gdt_init(); diff --git a/src/kernel/idt.c b/src/kernel/idt.c new file mode 100644 index 0000000..65f511f --- /dev/null +++ b/src/kernel/idt.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include + +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/idt.h b/src/kernel/idt.h new file mode 100644 index 0000000..5627657 --- /dev/null +++ b/src/kernel/idt.h @@ -0,0 +1,3 @@ +#pragma once + +void idt_init(); diff --git a/src/kernel/isr.c b/src/kernel/isr.c new file mode 100644 index 0000000..b715de0 --- /dev/null +++ b/src/kernel/isr.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +bool isr_test_interrupt_called = false; + +__attribute__((interrupt)) +void isr_double_fault(struct interrupt_frame *frame) { + tty_const("#DF"); + panic(); +} + +__attribute__((interrupt)) +void isr_general_protection_fault(struct interrupt_frame *frame) { + tty_const("#GP"); + panic(); +} + +__attribute__((interrupt)) +void isr_test_interrupt(struct interrupt_frame *frame) { + isr_test_interrupt_called = true; +} diff --git a/src/kernel/isr.h b/src/kernel/isr.h new file mode 100644 index 0000000..150fc46 --- /dev/null +++ b/src/kernel/isr.h @@ -0,0 +1,15 @@ +#pragma once +#include + +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/main.c b/src/kernel/main.c new file mode 100644 index 0000000..0193514 --- /dev/null +++ b/src/kernel/main.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include +#include + +void r3_test(); + +void kmain() +{ + tty_clear(); + tty_const("gdt..."); + gdt_init(); + tty_const("idt..."); + idt_init(); + tty_const("sysenter..."); + sysenter_setup(); + tty_const("mem..."); + mem_init(); + + tty_const("creating process..."); + struct process *proc = process_new(r3_test); + tty_const("switching..."); + process_switch(proc); +} + +void r3_test() { + tty_const("ok"); + asm("cli"); + panic(); +} diff --git a/src/kernel/mem.c b/src/kernel/mem.c new file mode 100644 index 0000000..08eb285 --- /dev/null +++ b/src/kernel/mem.c @@ -0,0 +1,19 @@ +#include + +extern void *_kernel_end; +static void *highest_page; + +void mem_init() { + highest_page = &_kernel_end; +} + +void *page_alloc(size_t pages) { + void *bottom = highest_page; + highest_page += pages * PAGE_SIZE; + return bottom; +} + +// frees `pages` consecutive pages starting from *first +void page_free(void *first, size_t pages) { + // not implemented +} diff --git a/src/kernel/mem.h b/src/kernel/mem.h new file mode 100644 index 0000000..e8e6a4f --- /dev/null +++ b/src/kernel/mem.h @@ -0,0 +1,12 @@ +#pragma once +#include + +#define PAGE_SIZE 4096 + +void mem_init(); + +// allocates `pages` consecutive pages +void *page_alloc(size_t pages); + +// frees `pages` consecutive pages starting from *first +void page_free(void *first, size_t pages); diff --git a/src/kernel/panic.h b/src/kernel/panic.h new file mode 100644 index 0000000..7d2ea5f --- /dev/null +++ b/src/kernel/panic.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +// dumb c shit +#define panic_tostr2(x) #x +#define panic_tostr(x) panic_tostr2(x) + +#define panic() do { \ + tty_const(" PANIC! at the "); \ + tty_const(__func__); \ + tty_const(" (" __FILE__ ":" panic_tostr(__LINE__) ") "); \ + halt_cpu(); \ +} while (0) diff --git a/src/kernel/proc.c b/src/kernel/proc.c new file mode 100644 index 0000000..776238c --- /dev/null +++ b/src/kernel/proc.c @@ -0,0 +1,19 @@ +#include +#include +#include + +struct process *process_current; + +struct process *process_new(void *eip) { + struct process *proc; + proc = page_alloc(1); // TODO kmalloc + proc->stack_top = proc->esp = page_alloc(1) + 1 * PAGE_SIZE; + proc->eip = eip; + + return proc; +} + +void process_switch(struct process *proc) { + process_current = proc; + sysexit(proc->eip, proc->esp); +} diff --git a/src/kernel/proc.h b/src/kernel/proc.h new file mode 100644 index 0000000..976b275 --- /dev/null +++ b/src/kernel/proc.h @@ -0,0 +1,13 @@ +#pragma once + +struct process { + void *stack_top; + void *esp; + + void *eip; +}; + +extern struct process *process_current; + +struct process *process_new(void *eip); +void process_switch(struct process *proc); diff --git a/src/kernel/tty.c b/src/kernel/tty.c new file mode 100644 index 0000000..5c4c47d --- /dev/null +++ b/src/kernel/tty.c @@ -0,0 +1,38 @@ +/* will be moved to userspace later on */ + +#include + +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/tty.h b/src/kernel/tty.h new file mode 100644 index 0000000..3dc1525 --- /dev/null +++ b/src/kernel/tty.h @@ -0,0 +1,8 @@ +#pragma once +#include + +void tty_putchar(char c); +void tty_write(const char *buf, size_t len); +void tty_clear(); + +#define tty_const(str) tty_write(str, sizeof(str) - 1) diff --git a/src/kernel/util.c b/src/kernel/util.c new file mode 100644 index 0000000..da3ac9d --- /dev/null +++ b/src/kernel/util.c @@ -0,0 +1,9 @@ +#include +#include + +void *memset(void *s, int c, size_t n) { + uint8_t *s2 = s; + for (size_t i = 0; i < n; n++) + s2[i] = c; + return s; +} diff --git a/src/kernel/util.h b/src/kernel/util.h new file mode 100644 index 0000000..688ac63 --- /dev/null +++ b/src/kernel/util.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void *memset(void *s, int c, size_t n); diff --git a/src/platform/asm.h b/src/platform/asm.h new file mode 100644 index 0000000..6f72617 --- /dev/null +++ b/src/platform/asm.h @@ -0,0 +1,9 @@ +#pragma once + +// boot.s +__attribute__((noreturn)) +void halt_cpu(); + +// sysenter.c +void sysexit(void (*fun)(), void *stack_top); +void sysenter_setup(); diff --git a/src/platform/boot.s b/src/platform/boot.s new file mode 100644 index 0000000..d5bfda5 --- /dev/null +++ b/src/platform/boot.s @@ -0,0 +1,47 @@ +.set MAGIC, 0x1BADB002 +.set FLAG_ALIGN, 1<<0 /* align modules on page boundaries */ +.set FLAG_MEMINFO, 1<<1 /* memory map */ +.set FLAGS, FLAG_ALIGN | FLAG_MEMINFO +.set CHECKSUM, -(MAGIC + FLAGS) + +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +/* a lil stack */ +.section .bss +.global stack_top +.type stack_top, @object +.align 16 +stack_bottom: +.skip 16384 +stack_top: + + +.section .text +.global _start +.type _start, @function +_start: + mov $stack_top, %esp + call kmain + +.global halt_cpu +.type halt_cpu, @function +halt_cpu: + cli +1: hlt + jmp 1b + +// temporary, will be moved to another file soon +.global change_cs +.type change_cs, @function +change_cs: + /* 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 + +.size _start, . - _start diff --git a/src/platform/sysenter.s b/src/platform/sysenter.s new file mode 100644 index 0000000..270dc08 --- /dev/null +++ b/src/platform/sysenter.s @@ -0,0 +1,31 @@ +/* kernel/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 -- cgit v1.2.3