diff --git a/arch/x86_64/include/kernel/machine/thread.h b/arch/x86_64/include/kernel/machine/thread.h index de7d189..eba7f5f 100644 --- a/arch/x86_64/include/kernel/machine/thread.h +++ b/arch/x86_64/include/kernel/machine/thread.h @@ -31,6 +31,12 @@ extern kern_status_t ml_thread_prepare_user_context( virt_addr_t *kernel_sp, const uintptr_t *args, size_t nr_args); +/* prepare the stack so that ml_thread_switch_user can jump to usermode + * with the specified register context */ +extern kern_status_t ml_thread_clone_user_context( + const struct ml_cpu_context *ctx, + uintptr_t return_value, + virt_addr_t *kernel_sp); extern kern_status_t ml_thread_config_get( struct thread *thread, diff --git a/arch/x86_64/thread.c b/arch/x86_64/thread.c index 51a3824..78862d4 100644 --- a/arch/x86_64/thread.c +++ b/arch/x86_64/thread.c @@ -80,6 +80,21 @@ extern kern_status_t ml_thread_prepare_user_context( return KERN_OK; } +kern_status_t ml_thread_clone_user_context( + const struct ml_cpu_context *src_ctx, + uintptr_t return_value, + virt_addr_t *kernel_sp) +{ + (*kernel_sp) -= sizeof(struct ml_cpu_context); + + struct ml_cpu_context *ctx = (struct ml_cpu_context *)(*kernel_sp); + memcpy(ctx, src_ctx, sizeof *ctx); + + ctx->rax = return_value; + + return KERN_OK; +} + kern_status_t ml_thread_config_get( struct thread *thread, kern_config_key_t key, diff --git a/include/kernel/thread.h b/include/kernel/thread.h index 1d6969d..a377b67 100644 --- a/include/kernel/thread.h +++ b/include/kernel/thread.h @@ -62,6 +62,10 @@ extern kern_status_t thread_init_user( virt_addr_t sp, const uintptr_t *args, size_t nr_args); +extern kern_status_t thread_init_user_clone( + struct thread *thr, + const struct thread *src, + uintptr_t return_value); extern int thread_priority(struct thread *thr); extern void thread_awaken(struct thread *thr); extern void thread_exit(void); diff --git a/sched/thread.c b/sched/thread.c index d08a8e7..00fb9f0 100644 --- a/sched/thread.c +++ b/sched/thread.c @@ -101,6 +101,47 @@ kern_status_t thread_init_user( return KERN_OK; } +kern_status_t thread_init_user_clone( + struct thread *thr, + const struct thread *src, + uintptr_t return_value) +{ + thr->tr_state = THREAD_READY; + thr->tr_quantum_target = default_quantum(); + + thr->tr_kstack = vm_page_alloc(THREAD_KSTACK_ORDER, VM_NORMAL); + if (!thr->tr_kstack) { + return KERN_NO_MEMORY; + } + + thr->tr_sp = (uintptr_t)vm_page_get_vaddr(thr->tr_kstack) + + vm_page_order_to_bytes(THREAD_KSTACK_ORDER); + thr->tr_bp = thr->tr_sp; + thr->tr_cpu_kernel_sp = thr->tr_sp; + + /* the new thread needs two contextx: + * 1) to get the thread running in kernel mode, so that it can + * execute ml_thread_switch_user + * 2) to allow ml_thread_switch_user to jump to the correct place + * in usermode (and with the correct stack). + * + * these two contexts are constructed on the thread's kernel stack + * in reverse order. + */ + + /* this context will be used by ml_user_return to jump to userspace + * with the specified instruction pointer and user stack */ + ml_thread_clone_user_context(src->tr_irqctx, return_value, &thr->tr_sp); + /* this context will be used by the scheduler and ml_thread_switch to + * jump to ml_user_return in kernel mode with the thread's kernel stack. + */ + ml_thread_prepare_kernel_context( + (uintptr_t)ml_thread_switch_user, + &thr->tr_sp); + + return KERN_OK; +} + void thread_free(struct thread *thr) { }