diff --git a/kernel-src/arch/x86-64/syscall.asm b/kernel-src/arch/x86-64/syscall.asm index 89b04804..37d3891e 100644 --- a/kernel-src/arch/x86-64/syscall.asm +++ b/kernel-src/arch/x86-64/syscall.asm @@ -48,6 +48,8 @@ extern syscall_recvmsg extern syscall_listen extern syscall_connect extern syscall_accept +extern syscall_newthread +extern syscall_threadexit syscalltab: dq syscall_print dq syscall_mmap @@ -98,7 +100,9 @@ dq syscall_recvmsg dq syscall_listen dq syscall_connect dq syscall_accept -syscallcount equ 49 +dq syscall_newthread +dq syscall_threadexit +syscallcount equ 51 section .text global arch_syscall_entry ; on entry: diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index 1b23171e..b7ec23a7 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -12,6 +12,7 @@ #define SCHED_THREAD_FLAGS_SLEEP 4 #define SCHED_THREAD_FLAGS_INTERRUPTIBLE 8 #define SCHED_THREAD_FLAGS_PREEMPTED 16 +#define SCHED_THREAD_FLAGS_DEAD 32 #define SCHED_PROC_STATE_NORMAL 0 #define SCHED_PROC_STATE_ZOMBIE 1 diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index 23f93e03..2691304e 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -270,6 +270,21 @@ void sched_procexit() { semaphore_signal(&sched_initproc->waitsem); } +static void threadexit_internal(context_t *) { + thread_t *thread = _cpu()->thread; + // we don't need to access the current thread anymore, as any state will be ignored and we are running on the scheduler stack + // as well as not being preempted at all + _cpu()->thread = NULL; + + thread->flags |= SCHED_THREAD_FLAGS_DEAD; + + // because a thread deallocating its own data is a nightmare, thread deallocation and such will be left to whoever frees the proc it's tied to + // (likely an exit(2) call) + // FIXME this doesn't apply to kernel threads and something should be figured out for them + + sched_stopcurrentthread(); +} + __attribute__((noreturn)) void sched_threadexit() { thread_t *thread = _cpu()->thread; proc_t *proc = thread->proc; @@ -290,13 +305,8 @@ __attribute__((noreturn)) void sched_threadexit() { } interrupt_set(false); - _cpu()->thread = NULL; - - // because a thread deallocating its own data is a nightmare, thread deallocation and such will be left to whoever frees the proc it's tied to - // (likely an exit(2) call) - // FIXME this doesn't apply to kernel threads and something should be figured out for them - - sched_stopcurrentthread(); + arch_context_saveandcall(threadexit_internal, _cpu()->schedulerstack); + __builtin_unreachable(); } // called right before going back to userspace in places like the syscall handler or arch_context_switch @@ -323,6 +333,7 @@ void sched_stopotherthreads() { proc->threads[i]->shouldexit = true; sched_wakeup(proc->threads[i], SCHED_WAKEUP_REASON_INTERRUPTED); + proc->threads[i] = NULL; } spinlock_release(&proc->lock); diff --git a/kernel-src/sys/syscalls/execve.c b/kernel-src/sys/syscalls/execve.c index 89254516..8c47c0f0 100644 --- a/kernel-src/sys/syscalls/execve.c +++ b/kernel-src/sys/syscalls/execve.c @@ -115,7 +115,7 @@ syscallret_t syscall_execve(context_t *context, char *upath, char *uargv[], char ret.ret = 0; - // close CLOEXEC fds + // TODO close CLOEXEC fds sched_stopotherthreads(); diff --git a/kernel-src/sys/syscalls/newthread.c b/kernel-src/sys/syscalls/newthread.c new file mode 100644 index 00000000..d43f0dc7 --- /dev/null +++ b/kernel-src/sys/syscalls/newthread.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include + +syscallret_t syscall_newthread(context_t *, void *entry, void *stack) { + syscallret_t ret = { + .ret = -1 + }; + + // if the kernel was specified to have no user threading in the cmdline + if (cmdline_get("nomultithread")) { + ret.errno = ENOSYS; + return ret; + } + + proc_t *proc = _cpu()->thread->proc; + + spinlock_acquire(&proc->lock); + + // if we currently don't want any more threads to start + if (proc->nomorethreads == true) { + spinlock_release(&proc->lock); + ret.errno = EAGAIN; + return ret; + } + + int i; + for (i = 0; i < proc->threadtablesize; ++i) { + thread_t *thread = proc->threads[i]; + if (thread == NULL || (thread->flags & SCHED_THREAD_FLAGS_DEAD) == 0) + continue; + + // reuse the slot in the threads table + sched_destroythread(thread); + break; + } + + if (i == proc->threadtablesize) { + // resize it + void *tmp = realloc(proc->threads, (proc->threadtablesize + 1) * sizeof(thread_t *)); + if (tmp == NULL) { + ret.errno = ENOMEM; + goto cleanup; + } + + proc->threads = tmp; + } + + proc->threads[i] = sched_newthread(entry, 16 * PAGE_SIZE, 1, _cpu()->thread->proc, stack); + if (proc->threads[i] == NULL) { + ret.errno = ENOMEM; + goto cleanup; + } + + proc->threads[i]->vmmctx = _cpu()->thread->vmmctx; + + sched_queue(proc->threads[i]); + + cleanup: + spinlock_release(&proc->lock); + return ret; +} diff --git a/kernel-src/sys/syscalls/threadexit.c b/kernel-src/sys/syscalls/threadexit.c new file mode 100644 index 00000000..65e7b694 --- /dev/null +++ b/kernel-src/sys/syscalls/threadexit.c @@ -0,0 +1,6 @@ +#include +#include + +syscallret_t syscall_threadexit(context_t *) { + sched_threadexit(); +} diff --git a/patches/mlibc/jinx-working-patch.patch b/patches/mlibc/jinx-working-patch.patch index 2c46425a..9a8ea401 100644 --- a/patches/mlibc/jinx-working-patch.patch +++ b/patches/mlibc/jinx-working-patch.patch @@ -188,10 +188,10 @@ index 0000000..e5b087a + diff --git mlibc-workdir/sysdeps/astral/generic/generic.cpp mlibc-workdir/sysdeps/astral/generic/generic.cpp new file mode 100644 -index 0000000..37e2732 +index 0000000..2e216f9 --- /dev/null +++ mlibc-workdir/sysdeps/astral/generic/generic.cpp -@@ -0,0 +1,570 @@ +@@ -0,0 +1,587 @@ +#include +#include +#include @@ -322,6 +322,23 @@ index 0000000..37e2732 + return 0; + } + ++ #ifndef MLIBC_BUILDING_RTLD ++ ++ [[noreturn]] void sys_thread_exit() { ++ syscall(SYSCALL_THREADEXIT, nullptr); ++ } ++ ++ extern "C" void __mlibc_thread_entry(); ++ ++ int sys_clone(void *tcb, pid_t *pid_out, void *stack) { ++ long ret; ++ long err = syscall(SYSCALL_NEWTHREAD, &ret, (uintptr_t)__mlibc_thread_entry, (uintptr_t)stack); ++ *pid_out = ret; ++ return err; ++ } ++ ++ #endif ++ + int sys_listen(int fd, int backlog) { + long ret; + return syscall(SYSCALL_LISTEN, &ret, fd, backlog); @@ -781,10 +798,10 @@ index 0000000..990eca0 +#endif diff --git mlibc-workdir/sysdeps/astral/include/astral/syscall.h mlibc-workdir/sysdeps/astral/include/astral/syscall.h new file mode 100644 -index 0000000..52590c3 +index 0000000..a733e60 --- /dev/null +++ mlibc-workdir/sysdeps/astral/include/astral/syscall.h -@@ -0,0 +1,72 @@ +@@ -0,0 +1,74 @@ +#ifndef _SYSCALL_H_INCLUDE +#define _SYSCALL_H_INCLUDE + @@ -837,6 +854,8 @@ index 0000000..52590c3 +#define SYSCALL_LISTEN 46 +#define SYSCALL_CONNECT 47 +#define SYSCALL_ACCEPT 48 ++#define SYSCALL_NEWTHREAD 49 ++#define SYSCALL_THREADEXIT 50 + +#include +#include @@ -940,22 +959,23 @@ index 0000000..31d913e +endif diff --git mlibc-workdir/sysdeps/astral/threading/x86_64-thread-entry.S mlibc-workdir/sysdeps/astral/threading/x86_64-thread-entry.S new file mode 100644 -index 0000000..33d258d +index 0000000..4cb3ae0 --- /dev/null +++ mlibc-workdir/sysdeps/astral/threading/x86_64-thread-entry.S -@@ -0,0 +1,6 @@ +@@ -0,0 +1,7 @@ + .section .text +.global __mlibc_thread_entry +__mlibc_thread_entry: + pop %rdi + pop %rsi + pop %rdx ++ call __mlibc_thread_trampoline diff --git mlibc-workdir/sysdeps/astral/threading/x86_64-thread.cpp mlibc-workdir/sysdeps/astral/threading/x86_64-thread.cpp new file mode 100644 -index 0000000..e2569fc +index 0000000..3b893f0 --- /dev/null +++ mlibc-workdir/sysdeps/astral/threading/x86_64-thread.cpp -@@ -0,0 +1,58 @@ +@@ -0,0 +1,59 @@ +#include +#include +#include @@ -963,54 +983,55 @@ index 0000000..e2569fc +#include +#include + ++extern "C" void __mlibc_thread_trampoline(void *(*fn)(void *), Tcb *tcb, void *arg) { ++ if (mlibc::sys_tcb_set(tcb)) { ++ __ensure(!"failed to set tcb for new thread"); ++ } + -+ /* -+extern "C" void __mlibc_thread_trampoline(void* (*fn)(void*), Tcb* tcb, void* arg){ -+ if (mlibc::sys_tcb_set(tcb)) -+ __ensure(!"failed to set tcb for new thread"); ++ while (__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED) == 0) { ++ mlibc::sys_futex_wait(&tcb->tid, 0, nullptr); ++ } + -+ while (__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED) == 0) -+ mlibc::sys_futex_wait(&tcb->tid, 0, nullptr); ++ tcb->invokeThreadFunc(reinterpret_cast(fn), arg); + -+ //tcb->returnValue = fn(arg); -+ + __atomic_store_n(&tcb->didExit, 1, __ATOMIC_RELEASE); + mlibc::sys_futex_wake(&tcb->didExit); + + mlibc::sys_thread_exit(); +} -+ */ + +#define DEFAULT_STACK 0x400000 + -+namespace mlibc{ -+ /* -+ -+ int sys_prepare_stack(void **stack, void *entry, void *arg, void *tcb, size_t *stack_size, size_t *guard_size){ ++namespace mlibc { ++ int sys_prepare_stack(void **stack, void *entry, void *arg, void *tcb, size_t *stack_size, size_t *guard_size, void **stack_base) { + // TODO guard + + mlibc::infoLogger() << "mlibc: sys_prepare_stack() does not setup a guard!" << frg::endlog; + + *guard_size = 0; -+ ++ + *stack_size = *stack_size ? *stack_size : DEFAULT_STACK; + -+ if(!*stack){ -+ *stack = mmap(NULL, *stack_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0) + *stack_size; -+ if(*stack == MAP_FAILED) ++ if (!*stack) { ++ *stack_base = mmap(NULL, *stack_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ++ if (*stack_base == MAP_FAILED) { + return errno; ++ } ++ } else { ++ *stack_base = *stack; + } -+ -+ void** stack_it = (void**)*stack; + ++ *stack = (void *)((char *)*stack_base + *stack_size); ++ ++ void **stack_it = (void **)*stack; ++ + *--stack_it = arg; + *--stack_it = tcb; + *--stack_it = entry; -+ -+ *stack = (void*)stack_it; -+ ++ ++ *stack = (void *)stack_it; ++ + return 0; + } -+ */ -+ ++ +}