From 6089df1556cb17976f85ca4980365ee7a80f9ead Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 31 Jan 2020 23:35:46 +0200 Subject: [PATCH] Global thread list, proper kill() implementation, proper signal entry in sched --- include/sys/amd64/context.h | 16 ++++++++ include/sys/sched.h | 9 ----- include/sys/thread.h | 11 +++++- sys/amd64/sched_s.S | 13 ++++-- sys/sched.c | 23 +++++++++++ sys/thread.c | 79 +++++++++++++++++++++++++++++++++---- 6 files changed, 129 insertions(+), 22 deletions(-) create mode 100644 include/sys/amd64/context.h diff --git a/include/sys/amd64/context.h b/include/sys/amd64/context.h new file mode 100644 index 0000000..839542e --- /dev/null +++ b/include/sys/amd64/context.h @@ -0,0 +1,16 @@ +#pragma once +#include "sys/types.h" + +struct thread; + +// Enter a newly-created task +extern void context_enter(struct thread *thr); +// Enter a task from exec +extern void context_exec_enter(void *arg, struct thread *thr, uintptr_t stack3, uintptr_t entry); +// Stores current task context, loads new one's +extern void context_switch_to(struct thread *thr, struct thread *from); +// No current task, only load the first task to begin execution +extern void context_switch_first(struct thread *thr); + +extern void context_sigenter(uintptr_t entry, uintptr_t stack, int signum); +extern void context_sigreturn(void); diff --git a/include/sys/sched.h b/include/sys/sched.h index bde3db0..bc14494 100644 --- a/include/sys/sched.h +++ b/include/sys/sched.h @@ -2,15 +2,6 @@ enum thread_state; struct thread; -// Enter a newly-created task -extern void context_enter(struct thread *thr); -// Enter a task from exec -extern void context_exec_enter(void *arg, struct thread *thr, uintptr_t stack3, uintptr_t entry); -// Stores current task context, loads new one's -extern void context_switch_to(struct thread *thr, struct thread *from); -// No current task, only load the first task to begin execution -extern void context_switch_first(struct thread *thr); - void sched_queue(struct thread *thr); void sched_unqueue(struct thread *thr, enum thread_state new_state); diff --git a/include/sys/thread.h b/include/sys/thread.h index c9083c0..b3660e7 100644 --- a/include/sys/thread.h +++ b/include/sys/thread.h @@ -30,6 +30,7 @@ struct thread { // Signal uintptr_t signal_entry; + uint64_t sigq; // State pid_t pid; @@ -38,12 +39,18 @@ struct thread { struct thread *first_child; struct thread *next_child; + // Global thread list (for stuff like finding by PID) + struct thread *g_prev, *g_next; + // Scheduler struct thread *prev, *next; }; pid_t thread_alloc_pid(int is_user); - -void thread_sleep(struct thread *thr, uint64_t deadline); void thread_ioctx_fork(struct thread *dst, struct thread *src); int thread_init(struct thread *thr, uintptr_t entry, void *arg, int user); + +struct thread *thread_find(pid_t pid); + +void thread_sleep(struct thread *thr, uint64_t deadline); +void thread_sigenter(int signum); diff --git a/sys/amd64/sched_s.S b/sys/amd64/sched_s.S index 5a961f5..a43a1e4 100644 --- a/sys/amd64/sched_s.S +++ b/sys/amd64/sched_s.S @@ -115,11 +115,11 @@ context_sigreturn: ret - .global context_sigenter context_sigenter: // %rdi - entry // %rsi - stack + // %rdx - argument pushq %r15 pushq %r14 pushq %r13 @@ -128,14 +128,18 @@ context_sigenter: pushq %rbx // Use alternate TSS.RSP0 so syscall frame does not get overwritten + // Current stack pointer becomes a new "top" of the stack, essentially + // making it impossible for those scenarios to happen: + // 1. userspace -> kernel ISR transition via TSS starts rewriting syscall/old ISR context + // 2. syscalls issued from signal context overwriting normal context movq get_cpu(CPU_THREAD), %rbx movq %rsp, THREAD_RSP0_SIGENTER(%rbx) movq %rsp, THREAD_RSP0(%rbx) movq %rsp, THREAD_RSP0_TOP(%rbx) - movq THREAD_RSP0_TOP(%rbx), %rbx movq get_cpu(CPU_TSS), %r12 - movq %rbx, TSS_RSP0(%r12) + movq %rsp, TSS_RSP0(%r12) + // iretq frame pushq $0x1B pushq %rsi @@ -144,4 +148,7 @@ context_sigenter: pushq $0x23 pushq %rdi + // Move argument + movq %rdx, %rdi + iretq diff --git a/sys/sched.c b/sys/sched.c index 080cd09..5eed144 100644 --- a/sys/sched.c +++ b/sys/sched.c @@ -1,3 +1,4 @@ +#include "sys/amd64/context.h" #include "sys/amd64/hw/irq.h" #include "sys/amd64/hw/idt.h" #include "sys/amd64/cpu.h" @@ -168,7 +169,29 @@ void yield(void) { to->state = THREAD_RUNNING; get_cpu()->thread = to; + context_switch_to(to, from); + + // Check if instead of switching to a proper thread context we + // have to use signal handling + if (to->sigq) { + // Pick one signal to handle at a time + int signum = 0; + for (int i = 0; i < 64; ++i) { + if (to->sigq & (1ULL << i)) { + to->sigq &= ~(1ULL << i); + signum = i + 1; + break; + } + } + _assert(signum); + thread_sigenter(signum); + + // Theoretically, a rogue thread could steal all the CPU time by sending itself signals + // in normal context, as after returning from thread_sigenter() this code will return + // to a normal execution + // XXX: Maybe makes sense to just yield() here + } } void sched_init(void) { diff --git a/sys/thread.c b/sys/thread.c index 727f2fe..36162cc 100644 --- a/sys/thread.c +++ b/sys/thread.c @@ -1,6 +1,7 @@ #include "sys/amd64/hw/timer.h" #include "sys/amd64/mm/phys.h" #include "sys/amd64/mm/pool.h" +#include "sys/amd64/context.h" #include "sys/user/fcntl.h" #include "sys/binfmt_elf.h" #include "sys/user/errno.h" @@ -32,6 +33,7 @@ struct sys_fork_frame { //// +static struct thread *threads_all_head = NULL; static pid_t last_kernel_pid = 0; static pid_t last_user_pid = 0; @@ -43,6 +45,15 @@ pid_t thread_alloc_pid(int is_user) { } } +static void thread_add(struct thread *thr) { + if (threads_all_head) { + threads_all_head->g_prev = thr; + } + thr->g_next = threads_all_head; + thr->g_prev = NULL; + threads_all_head = thr; +} + //// static void thread_ioctx_empty(struct thread *thr) { @@ -68,6 +79,15 @@ void thread_ioctx_fork(struct thread *dst, struct thread *src) { //// +struct thread *thread_find(pid_t pid) { + for (struct thread *thr = threads_all_head; thr; thr = thr->g_next) { + if (thr->pid == pid) { + return thr; + } + } + return NULL; +} + int thread_init(struct thread *thr, uintptr_t entry, void *arg, int user) { uintptr_t stack_pages = amd64_phys_alloc_page(); _assert(stack_pages != MM_NADDR); @@ -184,10 +204,17 @@ int thread_init(struct thread *thr, uintptr_t entry, void *arg, int user) { // - step one thr->data.rsp0 = (uintptr_t) stack; + thr->sigq = 0; + + thread_add(thr); return 0; } +// TODO: support kthread forking() +// (Although I don't really think it's very useful - +// threads can just be created by thread_init() and +// sched_queue()) int sys_fork(struct sys_fork_frame *frame) { struct thread *dst = kmalloc(sizeof(struct thread)); _assert(dst); @@ -209,6 +236,8 @@ int sys_fork(struct sys_fork_frame *frame) { dst->data.cr3 = MM_PHYS(space); dst->space = space; + dst->signal_entry = src->signal_entry; + thread_ioctx_fork(dst, src); dst->state = THREAD_READY; @@ -273,7 +302,9 @@ int sys_fork(struct sys_fork_frame *frame) { // Allocate a new PID for userspace thread dst->pid = thread_alloc_pid(1); + dst->sigq = 0; + thread_add(dst); sched_queue(dst); return dst->pid; @@ -303,6 +334,7 @@ int sys_execve(const char *path, const char **argp, const char **envp) { thr->first_child = NULL; thr->next_child = NULL; thr->parent = NULL; + thr->sigq = 0; thr->space = amd64_mm_pool_alloc(); _assert(thr->space); @@ -319,6 +351,7 @@ int sys_execve(const char *path, const char **argp, const char **envp) { vfs_close(&thr->ioctx, &fd); + thr->signal_entry = 0; thr->data.rsp0 = thr->data.rsp0_top; // Allocate a new user stack @@ -331,6 +364,17 @@ int sys_execve(const char *path, const char **argp, const char **envp) { panic("This code shouldn't run\n"); } +void thread_sigenter(int signum) { + struct thread *thr = thread_self; + uintptr_t old_rsp0_top = thr->data.rsp0_top; + // XXX: Either use a separate stack or ensure stuff doesn't get overwritten + uintptr_t signal_rsp3 = thr->data.rsp3_base + 0x800; + + context_sigenter(thr->signal_entry, signal_rsp3, signum); + + thr->data.rsp0_top = old_rsp0_top; +} + __attribute__((noreturn)) void sys_exit(int status) { struct thread *thr = thread_self; kdebug("Thread %d exited with status %d\n", thr->pid, status); @@ -345,21 +389,40 @@ void thread_sleep(struct thread *thr, uint64_t deadline) { sched_unqueue(thr, THREAD_WAITING); } -extern void context_sigenter(uintptr_t entry, uintptr_t stack); -extern void context_sigreturn(void); - void sys_sigreturn(void) { context_sigreturn(); } int sys_kill(pid_t pid, int signum) { - struct thread *thr = thread_self; - _assert(thr); - uintptr_t old_rsp0_top = thr->data.rsp0_top; + struct thread *thr; - context_sigenter(thr->signal_entry, 0x100500); + if (pid > 0) { + thr = thread_find(pid); + } else if (pid == 0) { + thr = thread_self; + } else { + // Not implemented + thr = NULL; + } - thr->data.rsp0_top = old_rsp0_top; + if (!thr) { + return -ESRCH; + } + + if (signum <= 0 || signum >= 64) { + return -EINVAL; + } + + if (!thr) { + // No such process + return -ESRCH; + } + + if (thr == thread_self) { + thread_sigenter(signum); + } else { + thr->sigq |= 1ULL << (1 - signum); + } return 0; }