diff --git a/include/sys/amd64/cpu.h b/include/sys/amd64/cpu.h index 3195311..65f8eae 100644 --- a/include/sys/amd64/cpu.h +++ b/include/sys/amd64/cpu.h @@ -9,9 +9,8 @@ struct cpu { // TODO: somehow export offsets to asm struct cpu *self; // 0x00 - uint64_t ticks; - amd64_tss_t *tss; - uint64_t syscall_rsp; + uint64_t ticks; // 0x08 + amd64_tss_t *tss; // 0x10 uint64_t processor_id; diff --git a/include/sys/thread.h b/include/sys/thread.h index 34114ab..a48d99f 100644 --- a/include/sys/thread.h +++ b/include/sys/thread.h @@ -6,8 +6,14 @@ struct thread { uintptr_t rsp0; // 0x00 uintptr_t syscall_rsp; // 0x08 + uintptr_t rsp0_top; // 0x10 + uintptr_t cr3; // 0x18 + uintptr_t rsp0_base; size_t rsp0_size; + + uintptr_t rsp3_base; + size_t rsp3_size; } data; int pid; diff --git a/sys/amd64/sched_s.S b/sys/amd64/sched_s.S index 0d4117f..ec3268c 100644 --- a/sys/amd64/sched_s.S +++ b/sys/amd64/sched_s.S @@ -1,3 +1,4 @@ +#include "sys/amd64/asm/asm_cpu.h" .section .text .global context_enter context_enter: @@ -40,5 +41,22 @@ context_switch_first: popq %r14 popq %r15 + // Load TSS.RSP0 for user -> kernel transition + // %rax = top of task's stack + movq 0x10(%rdi), %rax + // %rcx = &tss + movq get_cpu(0x10), %rcx + // &tss->rsp0 = %rax + movq %rax, 4(%rcx) + + // Load new %cr3 if changed + movq 0x18(%rdi), %rax + movq %cr3, %rcx + test %rcx, %rax + jz 1f + // Only load if cr3 != thr->cr3 + movq %rax, %cr3 +1: + ret .size context_switch_to, . - context_switch_to diff --git a/sys/debug.c b/sys/debug.c index fee2f7e..132dfd9 100644 --- a/sys/debug.c +++ b/sys/debug.c @@ -61,6 +61,11 @@ void debug_backtrace(uintptr_t rbp, int depth, int limit) { return; } + if ((rbp & 0xFFFFFF0000000000) != 0xFFFFFF0000000000) { + kfatal("-- %rbp is not from kernel space\n"); + return; + } + uintptr_t rip = ((uintptr_t *) rbp)[1]; uintptr_t rbp_next = ((uintptr_t *) rbp)[0]; diff --git a/sys/sched.c b/sys/sched.c index c24622d..446cfb4 100644 --- a/sys/sched.c +++ b/sys/sched.c @@ -1,6 +1,8 @@ #include "sys/amd64/mm/phys.h" +#include "sys/amd64/mm/pool.h" #include "sys/amd64/hw/irq.h" #include "sys/amd64/hw/idt.h" +#include "sys/amd64/cpu.h" #include "sys/assert.h" #include "sys/thread.h" #include "sys/debug.h" @@ -73,6 +75,8 @@ static void init_thread(struct thread *thr, void *(*entry)(void *), void *arg) { thr->data.rsp0_base = MM_VIRTUALIZE(stack_pages); thr->data.rsp0_size = MM_PAGE_SIZE; + thr->data.cr3 = MM_PHYS(mm_kernel); + uint64_t *stack = (uint64_t *) (thr->data.rsp0_base + thr->data.rsp0_size); // Initial thread context @@ -143,6 +147,99 @@ static void init_thread(struct thread *thr, void *(*entry)(void *), void *arg) { thr->data.rsp0 = (uintptr_t) stack; } +static void init_user(struct thread *thr, void *(*entry)(void *), void *arg) { + uintptr_t stack_pages = amd64_phys_alloc_page(); + _assert(stack_pages != MM_NADDR); + uintptr_t stack3_pages = amd64_phys_alloc_page(); + _assert(stack3_pages != MM_NADDR); + + thr->data.rsp0_base = MM_VIRTUALIZE(stack_pages); + thr->data.rsp0_size = MM_PAGE_SIZE; + thr->data.rsp0_top = thr->data.rsp0_base + thr->data.rsp0_size; + + thr->data.rsp3_base = MM_VIRTUALIZE(stack3_pages); + thr->data.rsp3_size = MM_PAGE_SIZE; + + mm_space_t space = amd64_mm_pool_alloc(); + mm_space_clone(space, mm_kernel, MM_CLONE_FLG_KERNEL); + thr->data.cr3 = MM_PHYS(space); + + // Allow this thread to access upper pages for testing + space[AMD64_MM_STRIPSX(KERNEL_VIRT_BASE) >> 39] |= MM_PAGE_USER; + uint64_t *pdpt = (uint64_t *) MM_VIRTUALIZE(space[AMD64_MM_STRIPSX(KERNEL_VIRT_BASE) >> 39] & ~0xFFF); + for (uint64_t i = 0; i < 4; ++i) { + pdpt[((AMD64_MM_STRIPSX(KERNEL_VIRT_BASE) >> 30) + i) & 0x1FF] |= MM_PAGE_USER; + } + + uint64_t *stack = (uint64_t *) (thr->data.rsp0_base + thr->data.rsp0_size); + + // Initial thread context + + // Entry context + // ss + *--stack = 0x1B; + // rsp + *--stack = thr->data.rsp3_base + thr->data.rsp3_size; + // rflags + *--stack = 0x200; + // cs + *--stack = 0x23; + // rip + *--stack = (uintptr_t) entry; + + // Caller-saved + // r11 + *--stack = 0; + // r10 + *--stack = 0; + // r9 + *--stack = 0; + // r8 + *--stack = 0; + // rcx + *--stack = 0; + // rdx + *--stack = 0; + // rsi + *--stack = 0; + // rdi + *--stack = (uintptr_t) arg; + // rax + *--stack = 0; + + // Small stub so that context switch enters the thread properly + *--stack = (uintptr_t) context_enter; + // Callee-saved + // r15 + *--stack = 0; + // r14 + *--stack = 0; + // r13 + *--stack = 0; + // r12 + *--stack = 0; + // rbp + *--stack = 0; + // rbx + *--stack = 0; + + // Thread lifecycle: + // * context_switch_to(): + // - pops callee-saved registers (initializing them to 0) + // - enters context_enter() + // * context_enter(): + // - pops caller-saved registers (initializing them to 0 and setting up rdi) + // - enters proper execution context via iret + // ... Thread is running here until it yields + // * yield leads to context_switch_to(): + // - call to yield() automatically (per ABI) stores caller-saved registers + // - context_switch_to() pushes callee-saved registers onto current stack + // - selects a new thread + // - step one + + thr->data.rsp0 = (uintptr_t) stack; +} + static void *idle(void *arg) { while (1) { asm volatile ("hlt"); @@ -151,14 +248,17 @@ static void *idle(void *arg) { } static void *t0(void *arg) { + uint16_t *ptr = (uint16_t *) MM_VIRTUALIZE(0xB8000 + (uint64_t) arg * 2); size_t cntr = 0; + + *ptr = 0; while (1) { for (size_t i = 0; i < 10000000; ++i); - *((uint16_t *) MM_VIRTUALIZE(0xB8000 + (uint64_t) arg * 2)) ^= 'A'; + *ptr ^= 'A' | 0x700; ++cntr; - if (cntr == 50 * (((uint64_t) arg) +1)) { + if (cntr == 20 * (((uint64_t) arg) +1)) { kdebug("[%d] Død\n", arg); sched_unqueue(thread_current); } @@ -166,6 +266,17 @@ static void *t0(void *arg) { return 0; } +static void *u0(void *arg) { + uint16_t *ptr = (uint16_t *) MM_VIRTUALIZE(0xB8000 + (uint64_t) arg * 2); + *ptr = 0; + while (1) { + for (size_t i = 0; i < 10000000; ++i); + + *ptr ^= 'A' | 0x1200; + } + return 0; +} + void yield(void) { struct thread *from = thread_current; struct thread *to; @@ -183,12 +294,16 @@ void yield(void) { } static struct thread t_n[3] = {0}; +static struct thread t_u[3] = {0}; void sched_init(void) { init_thread(&t_n[0], t0, (void *) 0); init_thread(&t_n[1], t0, (void *) 1); init_thread(&t_n[2], t0, (void *) 2); - init_thread(&thread_idle, idle, NULL); + init_user(&t_u[0], u0, (void *) 5); + init_user(&t_u[1], u0, (void *) 6); + init_user(&t_u[2], u0, (void *) 7); + init_thread(&thread_idle, idle, 0); thread_idle.pid = -1; @@ -196,9 +311,17 @@ void sched_init(void) { t_n[1].pid = 2; t_n[2].pid = 3; + t_u[0].pid = 10; + t_u[1].pid = 11; + t_u[2].pid = 12; + sched_queue(&t_n[0]); sched_queue(&t_n[1]); sched_queue(&t_n[2]); + + sched_queue(&t_u[0]); + sched_queue(&t_u[1]); + sched_queue(&t_u[2]); } void sched_enter(void) {