diff options
Diffstat (limited to 'kernel/proc/context.c')
-rw-r--r-- | kernel/proc/context.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/kernel/proc/context.c b/kernel/proc/context.c new file mode 100644 index 0000000..b1902d8 --- /dev/null +++ b/kernel/proc/context.c @@ -0,0 +1,150 @@ + +#include "proc/context.h" +#include "proc/kthread.h" +#include <main/cpuid.h> + +#include "main/apic.h" +#include "main/gdt.h" + +typedef struct context_initial_func_args +{ + context_func_t func; + long arg1; + void *arg2; +} packed context_initial_func_args_t; + +static void __context_thread_initial_func(context_initial_func_args_t args) +{ + preemption_reset(); + apic_setipl(IPL_LOW); + intr_enable(); + + void *result = (args.func)(args.arg1, args.arg2); + kthread_exit(result); + + panic("\nReturned from kthread_exit.\n"); +} + +void context_setup_raw(context_t *c, void (*func)(), void *kstack, + size_t kstacksz, pml4_t *pml4) +{ + KASSERT(NULL != pml4); + KASSERT(PAGE_ALIGNED(kstack)); + c->c_kstack = (uintptr_t)kstack; + c->c_kstacksz = kstacksz; + c->c_pml4 = pml4; + c->c_rsp = (uintptr_t)kstack + kstacksz; + c->c_rsp -= sizeof(uintptr_t); + *((uintptr_t *)c->c_rsp) = 0; + c->c_rbp = c->c_rsp; + c->c_rip = (uintptr_t)func; +} + +/* + * Initializes a context_t struct with the given parameters. arg1 and arg2 will + * appear as arguments to the function passed in when this context is first + * used. + */ +void context_setup(context_t *c, context_func_t func, long arg1, void *arg2, + void *kstack, size_t kstacksz, pml4_t *pml4) +{ + KASSERT(NULL != pml4); + KASSERT(PAGE_ALIGNED(kstack)); + + c->c_kstack = (uintptr_t)kstack; + c->c_kstacksz = kstacksz; + c->c_pml4 = pml4; + + /* put the arguments for __context_thread_initial_func onto the + * stack */ + c->c_rsp = (uintptr_t)kstack + kstacksz; + c->c_rsp -= sizeof(arg2); + *(void **)c->c_rsp = arg2; + c->c_rsp -= sizeof(arg1); + *(long *)c->c_rsp = arg1; + c->c_rsp -= sizeof(context_func_t); + *(context_func_t *)c->c_rsp = func; + // Take space for the function return address (unused) + c->c_rsp -= sizeof(uintptr_t); + + c->c_rbp = c->c_rsp; + c->c_rip = (uintptr_t)__context_thread_initial_func; +} + +/* + * WARNING!! POTENTIAL EDITOR BEWARE!! + * IF YOU REMOVE THE PT_SET CALLS BELOW, + * YOU ***MUST*** DEAL WITH SMP TLB SHOOTDOWN + * + * IN OTHER WORDS, THINK *VERY* CAREFULLY BEFORE + * REMOVING THE CALLS TO PT_SET BELOW + */ + +void context_make_active(context_t *c) +{ + // gdt_set_kernel_stack((void *)((uintptr_t)c->c_kstack + c->c_kstacksz)); + pt_set(c->c_pml4); + + /* Switch stacks and run the thread */ + __asm__ volatile( + "movq %0,%%rbp\n\t" /* update rbp */ + "movq %1,%%rsp\n\t" /* update rsp */ + "push %2\n\t" /* save rip */ + "ret" /* jump to new rip */ + ::"m"(c->c_rbp), + "m"(c->c_rsp), "m"(c->c_rip)); +} + +void context_switch(context_t *oldc, context_t *newc) +{ + gdt_set_kernel_stack( + (void *)((uintptr_t)newc->c_kstack + newc->c_kstacksz)); + + // sanity check that core-specific data is being managed (paged in) + // correctly + KASSERT(oldc->c_pml4 == pt_get()); + uintptr_t curthr_paddr = + pt_virt_to_phys_helper(oldc->c_pml4, (uintptr_t)&curthr); + uintptr_t new_curthr_paddr = + pt_virt_to_phys_helper(newc->c_pml4, (uintptr_t)&curthr); + + kthread_t *prev_curthr = curthr; + pt_set(newc->c_pml4); + KASSERT(pt_get() == newc->c_pml4); + + KASSERT(curthr_paddr == new_curthr_paddr); + KASSERT(prev_curthr == curthr); + + /* + * Save the current value of the stack pointer and the frame pointer into + * the old context. Set the instruction pointer to the return address + * (whoever called us). + */ + __asm__ volatile( + "pushfq;" /* save RFLAGS on the stack */ + "pushq %%rbp \n" /* save base pointer */ + "pushq %%rbx \n" /* save other callee-saved registers */ + "pushq %%r12 \n" + "pushq %%r13 \n" + "pushq %%r14 \n" + "pushq %%r15 \n" + "movq %%rsp, %0 \n" /* save RSP into oldc */ + "movq %2, %%rsp \n" /* restore RSP from newc */ + "pushq %%rax\n\t" + "movabs $1f, %%rax \n\t" /* save RIP into oldc (saves the label '1' + below) */ + "mov %%rax, %1\n\t" + "popq %%rax\n\t" + "pushq %3 \n\t" /* restore RIP */ + "ret \n\t" + "1:\t" /* this is where oldc starts executing later */ + "popq %%r15 \n\t" /* restore callee-saved registers */ + "popq %%r14 \n\t" + "popq %%r13 \n\t" + "popq %%r12 \n\t" + "popq %%rbx \n\t" + "popq %%rbp \n\t" /* restore base pointer */ + "popfq" /* restore RFLAGS */ + : "=m"(oldc->c_rsp), "=m"(oldc->c_rip) + : "m"(newc->c_rsp), "m"(newc->c_rip)); +} |