[kernel] Give threads initial arguments

This commit changes the add_user_thunk to point to a new routine,
initialize_user_cpu, which sets all the registers that were previously
unset when starting a new user thread. The values for rdi and rsi are
popped off the initial stack values that add_user_thunk sets up, so that
user thread procs can take up to two arguments.

To suppor this, j6_thread_create gained two new arguments, which are
passed on to the thread.

This also let me finally get rid of the hack of passing an argument in
rsp when starting init.
This commit is contained in:
Justin C. Miller
2023-02-08 23:10:17 -08:00
parent 1cb8f1258d
commit 4125175870
10 changed files with 81 additions and 51 deletions

View File

@@ -10,6 +10,8 @@ object thread : object {
param process ref process [cap:create_thread] param process ref process [cap:create_thread]
param stack_top address param stack_top address
param entrypoint address param entrypoint address
param arg0 uint64
param arg1 uint64
} }
method kill [destructor cap:kill] method kill [destructor cap:kill]

View File

@@ -106,12 +106,6 @@ load_init_server(bootproto::program &program, uintptr_t modules_address)
uint64_t iopl = (3ull << 12); uint64_t iopl = (3ull << 12);
obj::thread *main = p->create_thread(); obj::thread *main = p->create_thread();
main->add_thunk_user(program.entrypoint, 0, iopl); main->add_thunk_user(program.entrypoint, modules_address, 0, 0, iopl);
main->set_state(obj::thread::state::ready); main->set_state(obj::thread::state::ready);
// Hacky: No process exists to have created a stack for init; it needs to create
// its own stack. We take advantage of that to use rsp to pass it the init modules
// address.
auto *tcb = main->tcb();
tcb->rsp3 = modules_address;
} }

View File

@@ -9,7 +9,7 @@
#include "objects/vm_area.h" #include "objects/vm_area.h"
#include "scheduler.h" #include "scheduler.h"
extern "C" void kernel_to_user_trampoline(); extern "C" void initialize_user_cpu();
extern obj::vm_area_guarded &g_kernel_stacks; extern obj::vm_area_guarded &g_kernel_stacks;
@@ -113,7 +113,7 @@ thread::add_thunk_kernel(uintptr_t rip)
} }
void void
thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags) thread::add_thunk_user(uintptr_t rip3, uint64_t arg0, uint64_t arg1, uintptr_t rip0, uint64_t flags)
{ {
// This sets up the stack to: // This sets up the stack to:
// a) come out of task_switch and return to rip0 (default is the // a) come out of task_switch and return to rip0 (default is the
@@ -126,19 +126,22 @@ thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
flags |= 0x200; flags |= 0x200;
m_tcb.rflags3 = flags; m_tcb.rflags3 = flags;
m_tcb.rsp -= sizeof(uintptr_t) * 7; m_tcb.rsp -= sizeof(uintptr_t) * 9;
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp); uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
stack[6] = rip3; // return rip in rcx stack[8] = rip3; // return rip in rcx
stack[5] = m_tcb.rsp3; // rbp stack[7] = m_tcb.rsp3; // rbp
stack[4] = 0xbbbbbbbb; // rbx stack[6] = 0xbbbbbbbb; // rbx
stack[3] = 0x12121212; // r12 stack[5] = 0x12121212; // r12
stack[2] = 0x13131313; // r13 stack[4] = 0x13131313; // r13
stack[1] = 0x14141414; // r14 stack[3] = 0x14141414; // r14
stack[0] = 0x15151515; // r15 stack[2] = 0x15151515; // r15
stack[1] = arg1; // rsi
stack[0] = arg0; // rdi
static const uintptr_t trampoline = static const uintptr_t trampoline =
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline); reinterpret_cast<uintptr_t>(initialize_user_cpu);
add_thunk_kernel(rip0 ? rip0 : trampoline); add_thunk_kernel(rip0 ? rip0 : trampoline);
} }
@@ -149,8 +152,8 @@ thread::setup_kernel_stack()
using mem::kernel_stack_pages; using mem::kernel_stack_pages;
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size; static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
constexpr unsigned null_frame_entries = 2; static constexpr unsigned null_frame_entries = 2;
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t); static constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
uintptr_t stack_addr = g_kernel_stacks.get_section(); uintptr_t stack_addr = g_kernel_stacks.get_section();
uintptr_t stack_end = stack_addr + stack_bytes; uintptr_t stack_end = stack_addr + stack_bytes;

View File

@@ -152,9 +152,16 @@ public:
/// Add a stack header that returns to the given address in user space /// Add a stack header that returns to the given address in user space
/// via a function in kernel space. /// via a function in kernel space.
/// \arg rip3 The user space address to return to /// \arg rip3 The user space address to return to
/// \arg arg0 An argument passed to the userspace function
/// \arg arg1 An argument passed to the userspace function
/// \arg rip0 The kernel function to pass through, optional /// \arg rip0 The kernel function to pass through, optional
/// \arg flags Extra RFLAGS values to set, optional /// \arg flags Extra RFLAGS values to set, optional
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0); void add_thunk_user(
uintptr_t rip3,
uint64_t arg0 = 0,
uint64_t arg1 = 0,
uintptr_t rip0 = 0,
uint64_t flags = 0);
/// Get the handle representing this thread to its process /// Get the handle representing this thread to its process
j6_handle_t self_handle() const { return m_self_handle; } j6_handle_t self_handle() const { return m_self_handle; }

View File

@@ -77,6 +77,21 @@ syscall_handler_prelude:
call syscall_invalid call syscall_invalid
.end: .end:
global initialize_user_cpu: function hidden (initialize_user_cpu.end - initialize_user_cpu)
initialize_user_cpu:
mov rax, 0xaaaaaaaa
mov rdx, 0xdddddddd
mov r8, 0x08080808
mov r9, 0x09090909
mov r10, 0x10101010
mov r11, 0x11111111
pop rdi
pop rsi
; fall through to kernel_to_user_trampoline
.end:
global kernel_to_user_trampoline: function hidden (kernel_to_user_trampoline.end - kernel_to_user_trampoline) global kernel_to_user_trampoline: function hidden (kernel_to_user_trampoline.end - kernel_to_user_trampoline)
kernel_to_user_trampoline: kernel_to_user_trampoline:
pop r15 pop r15

View File

@@ -12,13 +12,13 @@ using namespace obj;
namespace syscalls { namespace syscalls {
j6_status_t j6_status_t
thread_create(j6_handle_t *self, process *proc, uintptr_t stack_top, uintptr_t entrypoint) thread_create(j6_handle_t *self, process *proc, uintptr_t stack_top, uintptr_t entrypoint, uint64_t arg0, uint64_t arg1)
{ {
thread &parent_th = thread::current(); thread &parent_th = thread::current();
process &parent_pr = parent_th.parent(); process &parent_pr = parent_th.parent();
thread *child = proc->create_thread(stack_top); thread *child = proc->create_thread(stack_top);
child->add_thunk_user(entrypoint); child->add_thunk_user(entrypoint, arg0, arg1);
*self = child->self_handle(); *self = child->self_handle();
child->set_state(thread::state::ready); child->set_state(thread::state::ready);

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
/// \file thread.h /// \file thread.hh
/// High level threading interface /// High level threading interface
// The kernel depends on libj6 for some shared code, // The kernel depends on libj6 for some shared code,
@@ -15,7 +15,7 @@ namespace j6 {
class thread class thread
{ {
public: public:
using proc = void (*)(); using proc = void (*)(void *);
/// Constructor. Create a thread and its stack space, but /// Constructor. Create a thread and its stack space, but
/// do not start executing the thread. /// do not start executing the thread.
@@ -24,8 +24,9 @@ public:
thread(proc p, uintptr_t stack_top); thread(proc p, uintptr_t stack_top);
/// Start executing the thread. /// Start executing the thread.
/// \returns j6_status_ok if the thread was successfully started. /// \arg user Optional pointer to user data to pass to the thread proc
j6_status_t start(); /// \returns j6_status_ok if the thread was successfully started.
j6_status_t start(void *user = nullptr);
/// Wait for the thread to stop executing. /// Wait for the thread to stop executing.
void join(); void join();
@@ -34,6 +35,8 @@ public:
thread(const thread&) = delete; thread(const thread&) = delete;
private: private:
static void init_proc(thread *t, void *user);
j6_status_t m_status; j6_status_t m_status;
j6_handle_t m_stack; j6_handle_t m_stack;
j6_handle_t m_thread; j6_handle_t m_thread;

View File

@@ -31,7 +31,7 @@ thread::thread(thread::proc p, uintptr_t stack_top) :
} }
j6_status_t j6_status_t
thread::start() thread::start(void *user)
{ {
if (m_status != j6_status_ok) if (m_status != j6_status_ok)
return m_status; return m_status;
@@ -39,8 +39,12 @@ thread::start()
if (m_thread != j6_handle_invalid) if (m_thread != j6_handle_invalid)
return j6_err_invalid_arg; return j6_err_invalid_arg;
uint64_t arg0 = reinterpret_cast<uint64_t>(this);
uint64_t arg1 = reinterpret_cast<uint64_t>(user);
m_status = j6_thread_create(&m_thread, __handle_self, m_status = j6_thread_create(&m_thread, __handle_self,
m_stack_top, reinterpret_cast<uintptr_t>(m_proc)); m_stack_top, reinterpret_cast<uintptr_t>(init_proc),
arg0, arg1);
return m_status; return m_status;
} }
@@ -51,6 +55,13 @@ thread::join()
j6_thread_join(m_thread); j6_thread_join(m_thread);
} }
void
thread::init_proc(thread *t, void *user)
{
t->m_proc(user);
j6_thread_exit();
} }
} // namespace j6
#endif // __j6kernel #endif // __j6kernel

View File

@@ -135,7 +135,7 @@ load_program(
} }
j6_handle_t thread = j6_handle_invalid; j6_handle_t thread = j6_handle_invalid;
res = j6_thread_create(&thread, proc, stack_top - 6*sizeof(uint64_t), progelf.entrypoint()); res = j6_thread_create(&thread, proc, stack_top - 6*sizeof(uint64_t), progelf.entrypoint(), 0, 0);
if (res != j6_status_ok) { if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': creating thread: %lx", name, res); sprintf(err_msg, " ** error loading program '%s': creating thread: %lx", name, res);
return false; return false;

View File

@@ -7,34 +7,29 @@ extern _arg_modules_phys
section .bss section .bss
align 0x100 align 0x100
init_stack_start: init_stack_start:
resb 0x8000 ; 16KiB stack space resb 0x8000 ; 16KiB stack space
init_stack_top: init_stack_top:
section .text section .text
global _start:function (_start.end - _start) global _start:function (_start.end - _start)
_start: _start:
; No parent process exists to have created init's stack, so we create a
; stack in BSS and assign that to be init's first stack
mov [_arg_modules_phys], rdi
mov rsp, init_stack_top
push 0
push 0
; No parent process exists to have created init's stack, so we override mov rbp, rsp
; _start to deal with that in two ways: mov rdi, rsp
call __init_libj6
call __init_libc
; 1. We create a stack in BSS and assign that to be init's first stack pop rdi
; 2. We take advantage of the fact that rsp is useless here as a way mov rsi, rsp
; for the kernel to tell init where its initial modules page is. call main
mov [_arg_modules_phys], rsp
mov rsp, init_stack_top
push 0
push 0
mov rbp, rsp mov rdi, rax
mov rdi, rsp call exit
call __init_libj6
call __init_libc
pop rdi
mov rsi, rsp
call main
mov rdi, rax
call exit
.end: .end: