diff --git a/definitions/objects/thread.def b/definitions/objects/thread.def index 94c2b5e..93484be 100644 --- a/definitions/objects/thread.def +++ b/definitions/objects/thread.def @@ -10,6 +10,8 @@ object thread : object { param process ref process [cap:create_thread] param stack_top address param entrypoint address + param arg0 uint64 + param arg1 uint64 } method kill [destructor cap:kill] diff --git a/src/kernel/kernel_main.cpp b/src/kernel/kernel_main.cpp index 6c62603..4b3f5ad 100644 --- a/src/kernel/kernel_main.cpp +++ b/src/kernel/kernel_main.cpp @@ -106,12 +106,6 @@ load_init_server(bootproto::program &program, uintptr_t modules_address) uint64_t iopl = (3ull << 12); 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); - - // 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; } diff --git a/src/kernel/objects/thread.cpp b/src/kernel/objects/thread.cpp index 2450b71..6e8abdb 100644 --- a/src/kernel/objects/thread.cpp +++ b/src/kernel/objects/thread.cpp @@ -9,7 +9,7 @@ #include "objects/vm_area.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; @@ -113,7 +113,7 @@ thread::add_thunk_kernel(uintptr_t rip) } 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: // 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; m_tcb.rflags3 = flags; - m_tcb.rsp -= sizeof(uintptr_t) * 7; + m_tcb.rsp -= sizeof(uintptr_t) * 9; uintptr_t *stack = reinterpret_cast(m_tcb.rsp); - stack[6] = rip3; // return rip in rcx - stack[5] = m_tcb.rsp3; // rbp - stack[4] = 0xbbbbbbbb; // rbx - stack[3] = 0x12121212; // r12 - stack[2] = 0x13131313; // r13 - stack[1] = 0x14141414; // r14 - stack[0] = 0x15151515; // r15 + stack[8] = rip3; // return rip in rcx + stack[7] = m_tcb.rsp3; // rbp + stack[6] = 0xbbbbbbbb; // rbx + stack[5] = 0x12121212; // r12 + stack[4] = 0x13131313; // r13 + stack[3] = 0x14141414; // r14 + stack[2] = 0x15151515; // r15 + + stack[1] = arg1; // rsi + stack[0] = arg0; // rdi static const uintptr_t trampoline = - reinterpret_cast(kernel_to_user_trampoline); + reinterpret_cast(initialize_user_cpu); add_thunk_kernel(rip0 ? rip0 : trampoline); } @@ -149,8 +152,8 @@ thread::setup_kernel_stack() using mem::kernel_stack_pages; static constexpr size_t stack_bytes = kernel_stack_pages * frame_size; - constexpr unsigned null_frame_entries = 2; - constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t); + static constexpr unsigned null_frame_entries = 2; + 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_end = stack_addr + stack_bytes; diff --git a/src/kernel/objects/thread.h b/src/kernel/objects/thread.h index 241f382..813cb28 100644 --- a/src/kernel/objects/thread.h +++ b/src/kernel/objects/thread.h @@ -152,9 +152,16 @@ public: /// Add a stack header that returns to the given address in user space /// via a function in kernel space. /// \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 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 j6_handle_t self_handle() const { return m_self_handle; } diff --git a/src/kernel/syscall.s b/src/kernel/syscall.s index d4eb245..f27eea3 100644 --- a/src/kernel/syscall.s +++ b/src/kernel/syscall.s @@ -77,6 +77,21 @@ syscall_handler_prelude: call syscall_invalid .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) kernel_to_user_trampoline: pop r15 diff --git a/src/kernel/syscalls/thread.cpp b/src/kernel/syscalls/thread.cpp index d6f668a..724a6dc 100644 --- a/src/kernel/syscalls/thread.cpp +++ b/src/kernel/syscalls/thread.cpp @@ -12,13 +12,13 @@ using namespace obj; namespace syscalls { 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(); process &parent_pr = parent_th.parent(); thread *child = proc->create_thread(stack_top); - child->add_thunk_user(entrypoint); + child->add_thunk_user(entrypoint, arg0, arg1); *self = child->self_handle(); child->set_state(thread::state::ready); diff --git a/src/libraries/j6/j6/thread.hh b/src/libraries/j6/j6/thread.hh index dd63e51..9cbf7f9 100644 --- a/src/libraries/j6/j6/thread.hh +++ b/src/libraries/j6/j6/thread.hh @@ -1,5 +1,5 @@ #pragma once -/// \file thread.h +/// \file thread.hh /// High level threading interface // The kernel depends on libj6 for some shared code, @@ -15,7 +15,7 @@ namespace j6 { class thread { public: - using proc = void (*)(); + using proc = void (*)(void *); /// Constructor. Create a thread and its stack space, but /// do not start executing the thread. @@ -24,8 +24,9 @@ public: thread(proc p, uintptr_t stack_top); /// Start executing the thread. - /// \returns j6_status_ok if the thread was successfully started. - j6_status_t start(); + /// \arg user Optional pointer to user data to pass to the thread proc + /// \returns j6_status_ok if the thread was successfully started. + j6_status_t start(void *user = nullptr); /// Wait for the thread to stop executing. void join(); @@ -34,6 +35,8 @@ public: thread(const thread&) = delete; private: + static void init_proc(thread *t, void *user); + j6_status_t m_status; j6_handle_t m_stack; j6_handle_t m_thread; diff --git a/src/libraries/j6/thread.cpp b/src/libraries/j6/thread.cpp index 4e4c770..e65b1e0 100644 --- a/src/libraries/j6/thread.cpp +++ b/src/libraries/j6/thread.cpp @@ -31,7 +31,7 @@ thread::thread(thread::proc p, uintptr_t stack_top) : } j6_status_t -thread::start() +thread::start(void *user) { if (m_status != j6_status_ok) return m_status; @@ -39,8 +39,12 @@ thread::start() if (m_thread != j6_handle_invalid) return j6_err_invalid_arg; + uint64_t arg0 = reinterpret_cast(this); + uint64_t arg1 = reinterpret_cast(user); + m_status = j6_thread_create(&m_thread, __handle_self, - m_stack_top, reinterpret_cast(m_proc)); + m_stack_top, reinterpret_cast(init_proc), + arg0, arg1); return m_status; } @@ -51,6 +55,13 @@ thread::join() j6_thread_join(m_thread); } +void +thread::init_proc(thread *t, void *user) +{ + t->m_proc(user); + j6_thread_exit(); } +} // namespace j6 + #endif // __j6kernel diff --git a/src/user/srv.init/loader.cpp b/src/user/srv.init/loader.cpp index cb5549d..9dc4f8b 100644 --- a/src/user/srv.init/loader.cpp +++ b/src/user/srv.init/loader.cpp @@ -135,7 +135,7 @@ load_program( } 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) { sprintf(err_msg, " ** error loading program '%s': creating thread: %lx", name, res); return false; diff --git a/src/user/srv.init/start.s b/src/user/srv.init/start.s index 33229dd..8917145 100644 --- a/src/user/srv.init/start.s +++ b/src/user/srv.init/start.s @@ -7,34 +7,29 @@ extern _arg_modules_phys section .bss align 0x100 init_stack_start: - resb 0x8000 ; 16KiB stack space + resb 0x8000 ; 16KiB stack space init_stack_top: section .text global _start:function (_start.end - _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 - ; _start to deal with that in two ways: + mov rbp, rsp + 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 - ; 2. We take advantage of the fact that rsp is useless here as a way - ; for the kernel to tell init where its initial modules page is. - mov [_arg_modules_phys], rsp - mov rsp, init_stack_top - push 0 - push 0 + pop rdi + mov rsi, rsp + call main - mov rbp, rsp - mov rdi, rsp - call __init_libj6 - call __init_libc - - pop rdi - mov rsi, rsp - call main - - mov rdi, rax - call exit + mov rdi, rax + call exit .end: