From 880930e91730973c304c03b0b8854b80561305fa Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sun, 8 Feb 2026 11:58:27 +0000 Subject: [PATCH] x86_64: implement functions to jump to userspace --- arch/x86_64/include/mango/machine/thread.h | 25 +++++++++++++++--- arch/x86_64/thread.c | 23 ++++++++++++++++- arch/x86_64/thread_switch.S | 30 +++++++++++++++++++--- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/arch/x86_64/include/mango/machine/thread.h b/arch/x86_64/include/mango/machine/thread.h index a9b8fb3..fa22802 100644 --- a/arch/x86_64/include/mango/machine/thread.h +++ b/arch/x86_64/include/mango/machine/thread.h @@ -3,8 +3,27 @@ #include -extern void switch_to(struct thread *from, struct thread *to); -extern void prepare_stack(uintptr_t ip, uintptr_t *sp); -extern void user_jump(uintptr_t ip, uintptr_t sp); +struct ml_cpu_context; + +/* switch from one thread to another. the stack of the `to` thread must have + * been prepared in one of two ways: + * 1) a previous call to ml_thread_switch where it was the `from` thread. + * 2) a call to ml_thread_prepare_kernel_context + * the switch occurs entirely with kernel mode. a further return from an + * interrupt context is then used to return to usermode if necessary. + */ +extern void ml_thread_switch(struct thread *from, struct thread *to); + +/* perform the initial transition to userspace. the stack must be prepared using + * ml_thread_prepare_user_context before this function can be used */ +extern void ml_thread_switch_user(void); +/* prepare the stack so that ml_thread_switch can jump to the specified IP/SP */ +extern void ml_thread_prepare_kernel_context(uintptr_t ip, uintptr_t *sp); +/* prepare the stack so that ml_thread_switch_user can jump to usermode + * with the specified IP/user SP */ +extern void ml_thread_prepare_user_context( + virt_addr_t ip, + virt_addr_t user_sp, + virt_addr_t *kernel_sp); #endif diff --git a/arch/x86_64/thread.c b/arch/x86_64/thread.c index 59cc3c5..823b5d2 100644 --- a/arch/x86_64/thread.c +++ b/arch/x86_64/thread.c @@ -1,12 +1,16 @@ +#include #include +/* this is the context information restored by ml_thread_switch. + * since ml_thread_switch only jumps to kernel-mode, IRETQ isn't used, + * and the extra register values needed by IRETQ aren't present. */ struct thread_ctx { uint64_t r15, r14, r13, r12, r11, r10, r9, r8; uint64_t rdi, rsi, rbp, unused_rsp, rbx, rdx, rcx, rax; uint64_t rfl; } __packed; -void prepare_stack(uintptr_t ip, uintptr_t *sp) +void ml_thread_prepare_kernel_context(uintptr_t ip, uintptr_t *sp) { (*sp) -= sizeof(uintptr_t); uintptr_t *dest_ip = (uintptr_t *)(*sp); @@ -18,3 +22,20 @@ void prepare_stack(uintptr_t ip, uintptr_t *sp) ctx->rfl = 0x202; } + +extern void ml_thread_prepare_user_context( + virt_addr_t ip, + virt_addr_t user_sp, + virt_addr_t *kernel_sp) +{ + (*kernel_sp) -= sizeof(struct ml_cpu_context); + + struct ml_cpu_context *ctx = (struct ml_cpu_context *)(*kernel_sp); + ctx->rip = ip; + ctx->rsp = user_sp; + ctx->ss = 0x23; + ctx->cs = 0x1B; + ctx->rflags = 0x202; + ctx->rdi = 0; // arg 0 + ctx->rsi = 0; // arg 1 +} diff --git a/arch/x86_64/thread_switch.S b/arch/x86_64/thread_switch.S index f4bd690..47b7407 100644 --- a/arch/x86_64/thread_switch.S +++ b/arch/x86_64/thread_switch.S @@ -2,12 +2,12 @@ .extern THREAD_sp -.global switch_to -.type switch_to, @function +.global ml_thread_switch +.type ml_thread_switch, @function // %rdi = (struct thread *) current thread. // %rsi = (struct thread *) next thread. -switch_to: +ml_thread_switch: pushfq push %rax @@ -50,3 +50,27 @@ switch_to: popfq ret + + +.global ml_thread_switch_user +.type ml_thread_switch_user, @function +ml_thread_switch_user: + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rbp + add $8, %rsp + pop %rbx + pop %rdx + pop %rcx + pop %rax + + add $16, %rsp + iretq