mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[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:
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<uintptr_t*>(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<uintptr_t>(kernel_to_user_trampoline);
|
||||
reinterpret_cast<uintptr_t>(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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<uint64_t>(this);
|
||||
uint64_t arg1 = reinterpret_cast<uint64_t>(user);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user