diff --git a/DOOM.txt b/DOOM.txt new file mode 100644 index 0000000..4f1aa83 --- /dev/null +++ b/DOOM.txt @@ -0,0 +1,24 @@ +up to doom: + +- Return from pedicel_main() normally (to idle) + +** Checkpoint: ring0 process working + +- VFS layer (open/read/write/...) with USTar filesystem (for initrd) + +** Checkpoint: files not linked to but accessible by the kernel + +- Ring3 memory mappings +- Ring3 privilege switch + +** Checkpoint: welcome to userland + +- Syscall interface +- Implement syscalls needed for doom + +** Checkpoint: can run simple programs, ring 3, loaded from filesystem + +- Properly handle the keyboard interrupt (keyboard buffer) +- Port DOOMgeneric (few functions with Framebuffer/ticks/etc.) + +** Achievement: It runs doom! diff --git a/Makefile b/Makefile index ce177d1..13cd618 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ build: x86_64-elf-gcc -g -c -Isrc $(SOURCES) $(PROBLEMATIC_FLAGS) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o nasm -f elf64 src/idt/idt.S -o idt_stub.o + nasm -f elf64 src/entry.S -o entry.o x86_64-elf-ld -o pepperk -T linker.ld *.o limine/limine: @@ -32,11 +33,11 @@ build-iso: limine/limine build ./limine/limine bios-install pepper.iso debug: - qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown & + /usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown & gdb pepperk --command=debug.gdb run: build-iso - qemu-system-x86_64 -cdrom pepper.iso -serial stdio + /usr/bin/qemu-system-x86_64 -cdrom pepper.iso -serial stdio clean: rm -rf *.o pepperk iso_root pepper.iso limine diff --git a/linker.ld b/linker.ld index 1b34478..5e3e8a6 100644 --- a/linker.ld +++ b/linker.ld @@ -1,6 +1,6 @@ OUTPUT_FORMAT(elf64-x86-64) -ENTRY(kmain) +ENTRY(_start) PHDRS { diff --git a/src/config.h b/src/config.h index 1fb3b16..632a1f5 100644 --- a/src/config.h +++ b/src/config.h @@ -27,6 +27,7 @@ #define KERNEL_BASE 0xFFFFFFFF80000000ULL // 2 MB should be enough (as of now, the whole kernel ELF is around 75kb) #define KERNEL_SIZE 0x200000 +#define KERNEL_STACK_SIZE 65536 /* heap */ #define KHEAP_SIZE (16*1024*1024) diff --git a/src/entry.S b/src/entry.S new file mode 100644 index 0000000..73b6de0 --- /dev/null +++ b/src/entry.S @@ -0,0 +1,23 @@ +bits 64 +global _start + +extern kmain +extern kernel_stack + +KERNEL_STACK_SIZE equ 65536 + +section .text + +_start: + cli + + ; load kernel stack + lea rsp, [kernel_stack+KERNEL_STACK_SIZE] + + ; rbp=0 so last frame in stack trace + xor rbp, rbp + + ; 16 byte align + and rsp, -16 + + call kmain \ No newline at end of file diff --git a/src/idt/idt.c b/src/idt/idt.c index 025dedd..61e402c 100644 --- a/src/idt/idt.c +++ b/src/idt/idt.c @@ -13,6 +13,7 @@ #include #include "sched/scheduler.h" #include "config.h" +#include "sched/process.h" struct interrupt_descriptor idt[256]; struct idtr idt_reg; @@ -118,6 +119,11 @@ static void gp_fault_handler(struct cpu_status_t* ctx) struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) { + if (context == NULL) + { + panic(NULL, "Interrupt dispatch recieved NULL context!"); + } + switch(context->vector_number) { case 0: @@ -140,6 +146,7 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) break; case 6: DEBUG("Invalid Opcode!"); + panic(context, "Invalid Opcode!"); break; case 7: DEBUG("Device Not Available!"); @@ -194,7 +201,8 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) if (ticks % SCHEDULER_QUANTUM == 0) { CLEAR_INTERRUPTS; - scheduler_schedule(); + struct cpu_status_t* current_ctx = scheduler_schedule(context); + process_switch(current_ctx->iret_rsp, current_ctx->iret_rip); SET_INTERRUPTS; } diff --git a/src/kernel.h b/src/kernel.h index e8fcae1..a6834c1 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -30,6 +30,7 @@ enum ErrorCodes void panic(struct cpu_status_t* ctx, const char* str); void hcf(); +void idle(); #define assert(check) do { if(!(check)) hcf(); } while(0) struct boot_context diff --git a/src/kmain.c b/src/kmain.c index 922e7cb..856d336 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -37,6 +37,8 @@ void hcf() // Doing nothing (can be interrupted) void idle() {for(;;)asm("hlt");} +uint8_t kernel_stack[KERNEL_STACK_SIZE] __attribute__((aligned(16))); + void panic(struct cpu_status_t* ctx, const char* str) { CLEAR_INTERRUPTS; @@ -68,17 +70,13 @@ extern struct process_t* current_process; void pedicel_main(void* arg) { - panic(NULL, "we did it!"); + printf("Hello, world from a KERNEL PROCESS!"); + //panic(NULL, "WE DID IT! Hello, world from a PROCESS!!!!"); } -void two_main(void* arg) +void idle_main(void* arg) { - -} - -void three_main(void* arg) -{ - + for(;;)asm("hlt"); } // This is our entry point @@ -113,8 +111,7 @@ void kmain() vmm_init(); struct process_t* pedicel = process_create("pedicel", (void*)pedicel_main, 0); - //struct process_t* two = process_create("two", (void*)two_main, 0); - //struct process_t* three = process_create("three", (void*)three_main, 0); + process_display_list(processes_list); scheduler_init(); diff --git a/src/mem/heap/kheap.c b/src/mem/heap/kheap.c index 220471a..d3918f2 100644 --- a/src/mem/heap/kheap.c +++ b/src/mem/heap/kheap.c @@ -142,6 +142,6 @@ void kfree(void* ptr) // Should return a pointer to top of the stack (as stack grows DOWNWARDS) void* kalloc_stack() { - uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); + uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space return ptr ? ptr+PROCESS_STACK_SIZE : NULL; } \ No newline at end of file diff --git a/src/mem/paging/paging.c b/src/mem/paging/paging.c index 0597943..c208e53 100644 --- a/src/mem/paging/paging.c +++ b/src/mem/paging/paging.c @@ -24,7 +24,7 @@ If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages 4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages */ -static inline void load_cr3(uint64_t value) { +void load_cr3(uint64_t value) { asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory"); } diff --git a/src/mem/paging/paging.h b/src/mem/paging/paging.h index 34afeaa..7aa32ba 100644 --- a/src/mem/paging/paging.h +++ b/src/mem/paging/paging.h @@ -16,6 +16,9 @@ void paging_init(); void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags); +// To swap root page tables +void load_cr3(uint64_t value); + extern uint64_t hhdm_off; #define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off)) diff --git a/src/sched/process.c b/src/sched/process.c index 9161a37..7566bbf 100644 --- a/src/sched/process.c +++ b/src/sched/process.c @@ -11,10 +11,13 @@ #include "string/string.h" #include "mem/gdt/gdt.h" #include "config.h" +#include "io/serial/serial.h" struct process_t* processes_list; struct process_t* current_process; +extern uint64_t *kernel_pml4; + size_t next_free_pid = 0; void process_init() @@ -52,14 +55,22 @@ struct process_t* process_create(char* name, void(*function)(void*), void* arg) proc->pid = next_free_pid++; proc->status = READY; + uint64_t* stack_top = (uint64_t*)kalloc_stack(); + // push return address to the stack so when "ret" hits we jmp to exit instead of idk what + // stack grows DOWNWARDS!! + *(--stack_top) = (uint64_t)process_exit; + proc->context = ctx; proc->context->iret_ss = KERNEL_DATA_SEGMENT; // process will live in kernel mode - proc->context->iret_rsp = (uint64_t)kalloc_stack(); + proc->context->iret_rsp = (uint64_t)stack_top; proc->context->iret_flags = 0x202; //bit 2 and 9 set (Interrupt Flag) proc->context->iret_cs = KERNEL_CODE_SEGMENT; proc->context->iret_rip = (uint64_t)function; // beginning of executable code proc->context->rdi = (uint64_t)arg; // 1st arg is in rdi (as per x64 calling convention) - proc->context->rbp = 0; + proc->context->rbp = 0; + + // Kernel PML4 as it already maps code/stack (when switching to userland we'll have to change that) + proc->root_page_table = kernel_pml4; proc->next = 0; @@ -126,4 +137,38 @@ struct process_t* process_get_next(struct process_t* process) { if (!process) return NULL; return process->next; +} + +// (from gdt) This will switch tasks ONLY in ring 0 +// KERNEL CS = 0x08 +// KERNEL SS = 0x10 +__attribute__((naked, noreturn)) +void process_switch(uint64_t stack_addr, uint64_t code_addr) +{ + asm volatile(" \ + push $0x10 \n\ + push %0 \n\ + push $0x202 \n\ + push $0x08 \n\ + push %1 \n\ + iretq \n\ + " :: "r"(stack_addr), "r"(code_addr)); +} + + +// Will be used to clean up resources (if any, when we implement it) +// Just mark as DEAD then idle. Scheduler will delete process at next timer interrupt % quantum. +void process_exit() +{ + CLEAR_INTERRUPTS; + if (current_process) + { + current_process->status = DEAD; + } + SET_INTERRUPTS; + + for (;;) + { + asm("hlt"); + } } \ No newline at end of file diff --git a/src/sched/process.h b/src/sched/process.h index 9b8e572..f245728 100644 --- a/src/sched/process.h +++ b/src/sched/process.h @@ -9,6 +9,7 @@ #include #include "config.h" +#include typedef enum { @@ -33,6 +34,8 @@ struct process_t* process_create(char* name, void(*function)(void*), void* arg); void process_add(struct process_t** processes_list, struct process_t* process); void process_delete(struct process_t** processes_list, struct process_t* process); struct process_t* process_get_next(struct process_t* process); +void process_switch(uint64_t stack_addr, uint64_t code_addr); +void process_exit(); void process_display_list(struct process_t* processes_list); diff --git a/src/sched/scheduler.c b/src/sched/scheduler.c index 0299ce8..645d845 100644 --- a/src/sched/scheduler.c +++ b/src/sched/scheduler.c @@ -6,6 +6,8 @@ #include "kernel.h" #include "process.h" +#include "mem/paging/paging.h" +#include extern struct process_t* processes_list; extern struct process_t* current_process; @@ -18,7 +20,7 @@ void scheduler_init() struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context) { - current_process->context = context; + //current_process->context = context; current_process->status = READY; for (;;) { @@ -41,6 +43,13 @@ struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context) } } - DEBUG("current_process={pid=%u name='%s'}", current_process->pid, current_process->name); + + // Current_process gets wrong context?? (iret_rip points to other stuff than process function; like putpixel() for example) + DEBUG("current_process={pid=%u, name='%s', root_page_table[virt]=%p}", current_process->pid, current_process->name, current_process->root_page_table); + + load_cr3(VIRT_TO_PHYS((uint64_t)current_process->root_page_table)); + DEBUG("loaded process pml4 into cr3"); + /* process_switch(current_process->context->rbp, current_process->context->iret_rip); + DEBUG("switched to process rip!"); */ return current_process->context; } \ No newline at end of file diff --git a/src/sched/scheduler.h b/src/sched/scheduler.h index b4d7516..9ec712e 100644 --- a/src/sched/scheduler.h +++ b/src/sched/scheduler.h @@ -7,7 +7,7 @@ #ifndef SCHEDULER_H #define SCHEDULER_H -void scheduler_schedule(); +struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context); void scheduler_init(); #endif \ No newline at end of file