From 2cbd3e65c476ff5fed13e690eb71a13fc1e4ce3a Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 29 Dec 2019 16:29:16 +0200 Subject: [PATCH] Signals kinda work! --- include/sys/amd64/sys/thread.h | 10 +++- include/sys/sched.h | 1 + include/sys/signum.h | 13 +++++ include/sys/syscall.h | 3 ++ include/sys/thread.h | 10 ++++ sys/amd64/sys/sched.c | 34 +++++++++++++ sys/amd64/sys/thread.c | 88 ++++++++++++++++++++++++++++++++- sys/amd64/syscall.c | 62 ++++++++++++++++++++++- usr/Makefile | 1 + usr/init.c | 15 ++++++ usr/libc/include/bits/syscall.h | 3 ++ usr/libc/include/signal.h | 6 +++ usr/libc/init.c | 2 + usr/libc/signal.c | 63 +++++++++++++++++++++++ usr/libc/syscall.c | 13 +++++ 15 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 include/sys/signum.h create mode 100644 usr/libc/include/signal.h create mode 100644 usr/libc/signal.c diff --git a/include/sys/amd64/sys/thread.h b/include/sys/amd64/sys/thread.h index 7bd98a7..cc8460d 100644 --- a/include/sys/amd64/sys/thread.h +++ b/include/sys/amd64/sys/thread.h @@ -5,10 +5,16 @@ struct thread; struct amd64_thread { - uintptr_t rsp0; + uintptr_t rsp0, rsp0s; + // Normal context uintptr_t stack0_base; uintptr_t stack0_size; + // Signal handling context + uintptr_t stack0s_base; + uintptr_t stack0s_size; + + // User stack uintptr_t stack3_base; uintptr_t stack3_size; @@ -16,3 +22,5 @@ struct amd64_thread { }; void amd64_thread_set_ip(struct thread *t, uintptr_t ip); +void amd64_thread_sigenter(struct thread *t, int signum); +void amd64_thread_sigret(struct thread *t); diff --git a/include/sys/sched.h b/include/sys/sched.h index 3f0e2fa..74deef0 100644 --- a/include/sys/sched.h +++ b/include/sys/sched.h @@ -1,6 +1,7 @@ #pragma once #include "sys/thread.h" +struct thread *sched_find(int pid); void sched_set_cpu_count(size_t ncpus); void sched_add(struct thread *t); void sched_init(void); diff --git a/include/sys/signum.h b/include/sys/signum.h new file mode 100644 index 0000000..67a8a75 --- /dev/null +++ b/include/sys/signum.h @@ -0,0 +1,13 @@ +#pragma once + +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGABRT 6 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGSEGV 11 + +#define SIGUSR1 16 +#define SIGUSR2 17 + diff --git a/include/sys/syscall.h b/include/sys/syscall.h index d7d9ede..6a4e881 100644 --- a/include/sys/syscall.h +++ b/include/sys/syscall.h @@ -8,6 +8,9 @@ #define SYSCALL_NR_READDIR 89 #define SYSCALL_NR_BRK 12 +#define SYSCALL_NRX_SIGNAL 48 #define SYSCALL_NR_FORK 57 #define SYSCALL_NR_EXECVE 59 #define SYSCALL_NR_EXIT 60 +#define SYSCALL_NR_KILL 62 +#define SYSCALL_NRX_SIGRET 119 diff --git a/include/sys/thread.h b/include/sys/thread.h index c388e02..e0e63d4 100644 --- a/include/sys/thread.h +++ b/include/sys/thread.h @@ -5,6 +5,7 @@ #define THREAD_KERNEL (1 << 31) #define THREAD_CTX_ONLY (1 << 30) #define THREAD_STOPPED (1 << 2) +#define THREAD_SIGRET (1 << 29) #if defined(ARCH_AMD64) typedef uint64_t *mm_space_t; @@ -28,6 +29,13 @@ struct thread { struct vfs_ioctx ioctx; struct ofile fds[4]; + // Signal processing queue + int sigqueue[8]; + size_t sigqsz; + // Signal handler stack and entry + uintptr_t sigentry; + uintptr_t sigstack; + mm_space_t space; // TODO: maybe __sched_thread @@ -46,3 +54,5 @@ int thread_init(struct thread *t, uint32_t flags, void *arg); void thread_cleanup(struct thread *t); + +void thread_signal(struct thread *t, int signum); diff --git a/sys/amd64/sys/sched.c b/sys/amd64/sys/sched.c index 4dc2321..4991c34 100644 --- a/sys/amd64/sys/sched.c +++ b/sys/amd64/sys/sched.c @@ -145,6 +145,23 @@ void sched_set_cpu_count(size_t count) { sched_ncpus = count; } +struct thread *sched_find(int pid) { + if (pid <= 0) { + return NULL; + } + + // Search through queues + for (size_t cpu = 0; cpu < sched_ncpus; ++cpu) { + for (struct thread *t = sched_queue_heads[cpu]; t; t = t->next) { + if ((int) t->pid == pid) { + return t; + } + } + } + + return NULL; +} + void sched_add_to(int cpu, struct thread *t) { t->next = NULL; @@ -262,6 +279,23 @@ int sched(void) { return -1; } + if (to->flags & THREAD_SIGRET) { + to->flags &= ~THREAD_SIGRET; + amd64_thread_sigret(to); + } + + if (to->sigqsz) { + // Should we enter signal handler? + int signum = to->sigqueue[0]; + --to->sigqsz; + // Shift the queue + for (size_t i = 0; i < to->sigqsz; ++i) { + to->sigqueue[i] = to->sigqueue[i + 1]; + } + + amd64_thread_sigenter(to, signum); + } + get_cpu()->thread = to; return 0; diff --git a/sys/amd64/sys/thread.c b/sys/amd64/sys/thread.c index 7f31b0e..2dfe474 100644 --- a/sys/amd64/sys/thread.c +++ b/sys/amd64/sys/thread.c @@ -61,7 +61,18 @@ int thread_init( t->data.stack3_size = rsp3_size; } - // Setup context + + // Alloc signal handling kstack + void *kstack_sig = kmalloc(THREAD_KSTACK_SIZE); + _assert(kstack_sig); + + t->data.stack0s_base = (uintptr_t) kstack_sig; + t->data.stack0s_size = THREAD_KSTACK_SIZE; + + // No need to setup context for signal handler stack as it will be set up on + // signal entry + + // Setup normal context struct cpu_context *ctx = (struct cpu_context *) t->data.rsp0; ctx->rip = entry; @@ -141,6 +152,17 @@ void thread_cleanup(struct thread *t) { kfree(t); } +// TODO: may be moved to platform-independent file +void thread_signal(struct thread *t, int s) { + _assert(t); + + if (t->sigqsz == 8) { + panic("Thread received too many unhandled signals\n"); + } + + t->sigqueue[t->sigqsz++] = s; +} + int sys_execve(const char *filename, const char *const argv[], const char *const envp[]) { asm volatile ("cli"); struct thread *thr = get_cpu()->thread; @@ -237,12 +259,15 @@ int sys_fork(void) { // Clone process state void *dst_kstack = kmalloc(THREAD_KSTACK_SIZE); + void *dst_kstack_sig = kmalloc(THREAD_KSTACK_SIZE); struct amd64_thread *dst_data = &thr_dst->data; - _assert(dst_kstack); + _assert(dst_kstack && dst_kstack_sig); dst_data->data_flags |= 1 /* Don't free kstack */; dst_data->stack0_base = (uintptr_t) dst_kstack; dst_data->stack0_size = THREAD_KSTACK_SIZE; + dst_data->stack0s_base = (uintptr_t) dst_kstack_sig; + dst_data->stack0s_size = THREAD_KSTACK_SIZE; dst_data->stack3_base = thr_src->data.stack3_base; dst_data->stack3_size = thr_src->data.stack3_size; dst_data->rsp0 = dst_data->stack0_base + dst_data->stack0_size - sizeof(struct cpu_context); @@ -287,3 +312,62 @@ void amd64_thread_set_ip(struct thread *t, uintptr_t ip) { ctx->rip = ip; ctx->rflags = 0x248; } + +void amd64_thread_sigenter(struct thread *t, int signum) { + _assert(t); + + // Swap contexts + t->data.rsp0s = t->data.rsp0; + t->data.rsp0 = t->data.stack0s_base + t->data.stack0s_size - sizeof(struct cpu_context); + + // Setup signal handler context + struct cpu_context *ctx = (struct cpu_context *) t->data.rsp0; + + ctx->rip = t->sigentry; + ctx->rflags = 0x248; + + if (t->flags & THREAD_KERNEL) { + panic("TODO: kernel thread signal handling\n"); + } else { + // Give one page of user stack for signal handling + t->sigstack = t->data.stack3_base + 4096; + ctx->rsp = t->sigstack; + ctx->cs = 0x23; + ctx->ss = 0x1B; + + ctx->ds = 0x1B; + ctx->es = 0x1B; + ctx->fs = 0; + } + + ctx->rax = 0; + ctx->rcx = 0; + ctx->rdx = 0; + ctx->rdx = 0; + ctx->rbp = 0; + ctx->rsi = 0; + ctx->rdi = (uintptr_t) signum; + + ctx->cr3 = ({ _assert((uintptr_t) t->space >= 0xFFFFFF0000000000); (uintptr_t) t->space - 0xFFFFFF0000000000; }); + + ctx->r8 = 0; + ctx->r9 = 0; + ctx->r10 = 0; + ctx->r11 = 0; + ctx->r12 = 0; + ctx->r13 = 0; + ctx->r14 = 0; + ctx->r15 = 0; + + ctx->__canary = AMD64_STACK_CTX_CANARY; + + if (t == get_cpu()->thread) { + panic("NYI\n"); + } +} + +void amd64_thread_sigret(struct thread *t) { + _assert(t); + + t->data.rsp0 = t->data.rsp0s; +} diff --git a/sys/amd64/syscall.c b/sys/amd64/syscall.c index bd1f38c..68167b9 100644 --- a/sys/amd64/syscall.c +++ b/sys/amd64/syscall.c @@ -3,6 +3,7 @@ #include "sys/thread.h" #include "sys/string.h" #include "sys/assert.h" +#include "sys/sched.h" #include "sys/types.h" #include "sys/debug.h" #include "sys/errno.h" @@ -20,10 +21,15 @@ static int sys_open(const char *filename, int flags, int mode); static void sys_close(int fd); static int sys_stat(const char *filename, struct stat *st); static void sys_exit(int status); +static int sys_kill(int pid, int signum); static int sys_brk(void *ptr); extern int sys_execve(const char *filename, const char *const argv[], const char *const envp[]); +// Non-compliant with linux style, but fuck'em, it just works +static void sys_signal(uintptr_t entry); +static void sys_sigret(void); + __attribute__((noreturn)) void amd64_syscall_yield_stopped(void); intptr_t amd64_syscall(uintptr_t rdi, uintptr_t rsi, uintptr_t rdx, uintptr_t rcx, uintptr_t r10, uintptr_t rax) { @@ -45,14 +51,24 @@ intptr_t amd64_syscall(uintptr_t rdi, uintptr_t rsi, uintptr_t rdx, uintptr_t rc case SYSCALL_NR_READDIR: return sys_readdir((int) rdi, (struct dirent *) rsi); + case SYSCALL_NRX_SIGNAL: + sys_signal(rdi); + return 0; case SYSCALL_NR_BRK: return sys_brk((void *) rdi); case SYSCALL_NR_EXIT: sys_exit((int) rdi); amd64_syscall_yield_stopped(); + case SYSCALL_NR_KILL: + return sys_kill((int) rdi, (int) rsi); + case SYSCALL_NRX_SIGRET: + sys_sigret(); + return 0; default: - kdebug("unknown syscall: %u\n", rax); + kerror("unknown syscall: %u\n", rax); + // TODO: I guess this should ideally crash the application + // with invalid opcode or something return -1; } return 0; @@ -222,3 +238,47 @@ static void sys_exit(int status) { thr->flags |= THREAD_STOPPED; } +static void sys_signal(uintptr_t entry) { + struct thread *thr = get_cpu()->thread; + _assert(thr); + kdebug("%u set its signal handler to %p\n", thr->pid, entry); + thr->sigentry = entry; +} + +static int sys_kill(int pid, int signum) { + asm volatile ("cli"); + struct thread *cur_thread = get_cpu()->thread; + + // Find destination thread + struct thread *dst_thread = sched_find(pid); + + if (!dst_thread) { + return -ESRCH; + } + + // Push an item to signal queue + thread_signal(dst_thread, signum); + + if (cur_thread == dst_thread) { + // Suicide signal, just hang on and wait + // until scheduler decides it's our time + asm volatile ("sti; hlt; cli"); + + kdebug("Returned from shit\n"); + } + + return 0; +} + +extern void amd64_syscall_iretq(uintptr_t ctx); + +static void sys_sigret(void) { + struct thread *thr = get_cpu()->thread; + _assert(thr); + + thr->flags |= THREAD_SIGRET; + + asm volatile ("sti; hlt; cli"); + + panic("Fuck\n"); +} diff --git a/usr/Makefile b/usr/Makefile index 787bbf0..87fb99a 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -34,6 +34,7 @@ libc_OBJS=$(O)/libc/crt0.o \ $(O)/libc/init.o \ $(O)/libc/malloc.o \ $(O)/libc/dirent.o \ + $(O)/libc/signal.o \ $(O)/libc/global.o sys_CRTBEGIN=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o) diff --git a/usr/init.c b/usr/init.c index 416e9f9..74aa581 100644 --- a/usr/init.c +++ b/usr/init.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,6 +7,10 @@ #include #include +static void dummy_handler(int signum) { + printf("Yay, no crash for this one\n"); +} + static int cmd_exec(const char *cmd) { if (!strcmp(cmd, "fork")) { int pid = fork(); @@ -21,6 +26,13 @@ static int cmd_exec(const char *cmd) { } } + if (!strcmp(cmd, "sig")) { + if (kill(1, SIGABRT) != 0) { + return -1; + } + return 0; + } + if (!strcmp(cmd, "crash")) { uint32_t *value_ptr = (uint32_t *) 0; *value_ptr = 123; @@ -87,6 +99,9 @@ int main(int argc, char **argv) { char c; size_t l = 0; + // Install dummy handler for SIGABRT + signal(SIGABRT, dummy_handler); + printf("> "); while (1) { if (read(STDIN_FILENO, &c, 1) < 0) { diff --git a/usr/libc/include/bits/syscall.h b/usr/libc/include/bits/syscall.h index b2ed2e1..e3b106c 100644 --- a/usr/libc/include/bits/syscall.h +++ b/usr/libc/include/bits/syscall.h @@ -13,5 +13,8 @@ void *mmap(void *addr, size_t len, int prot, int flags, int fd, uintptr_t offset int fork(void); int execve(const char *filename, const char *const argv[], const char *const envp[]); ssize_t sys_readdir(int fd, struct dirent *entp); +int kill(int pid, int signum); +void __kernel_signal(uintptr_t handler); +__attribute__((noreturn)) void __kernel_sigret(void); __attribute__((noreturn)) void exit(int code); diff --git a/usr/libc/include/signal.h b/usr/libc/include/signal.h new file mode 100644 index 0000000..1bb88c9 --- /dev/null +++ b/usr/libc/include/signal.h @@ -0,0 +1,6 @@ +#pragma once +#include + +typedef void (*sighandler_t) (int); + +sighandler_t signal(int signum, sighandler_t handler); diff --git a/usr/libc/init.c b/usr/libc/init.c index 06a0eaa..bc28c2b 100644 --- a/usr/libc/init.c +++ b/usr/libc/init.c @@ -2,8 +2,10 @@ #include "bits/global.h" extern char __heap_start; +extern void __libc_signal_init(void); void __libc_init(void) { + __libc_signal_init(); __cur_brk = &__heap_start; errno = 0; } diff --git a/usr/libc/signal.c b/usr/libc/signal.c new file mode 100644 index 0000000..fe685b2 --- /dev/null +++ b/usr/libc/signal.c @@ -0,0 +1,63 @@ +#include +#include +#include + +static sighandler_t signal_handlers[16] = {0}; + +void SIG_IGN(int signum) { + printf("Ignored: %d\n", signum); + + while (1); +} + +void SIG_DFL(int signum) { + // TODO: use getpid() + + switch (signum) { + case SIGKILL: + printf(": killed\n"); + break; + case SIGABRT: + printf(": aborted\n"); + break; + case SIGSEGV: + printf(": segmentation fault\n"); + break; + default: + printf(": signal %d\n", signum); + break; + } + + exit(1); +} + +static void __libc_signal_handle(int signum) { + if (signum >= 16) { + SIG_DFL(signum); + } else { + signal_handlers[signum](signum); + } + + __kernel_sigret(); +} + +void __libc_signal_init(void) { + for (size_t i = 0; i < 16; ++i) { + signal_handlers[i] = SIG_DFL; + } + + __kernel_signal((uintptr_t) __libc_signal_handle); +} + +sighandler_t signal(int signum, sighandler_t new_handler) { + sighandler_t old_handler; + + if (signum >= 16) { + printf("Fuck\n"); + exit(1); + } + + old_handler = signal_handlers[signum]; + signal_handlers[signum] = new_handler; + return old_handler; +} diff --git a/usr/libc/syscall.c b/usr/libc/syscall.c index f114950..b1769f2 100644 --- a/usr/libc/syscall.c +++ b/usr/libc/syscall.c @@ -96,6 +96,19 @@ __attribute__((noreturn)) void exit(int code) { while (1); } +int kill(int pid, int signum) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_KILL, pid, signum)); +} + +void __kernel_signal(uintptr_t entry) { + (void) ASM_SYSCALL1(SYSCALL_NRX_SIGNAL, entry); +} + +__attribute__((noreturn)) void __kernel_sigret(void) { + (void) ASM_SYSCALL0(SYSCALL_NRX_SIGRET); + while (1); +} + // Although sbrk() is implemented in userspace, I guess it should also be here void *sbrk(intptr_t inc) { if (inc == 0) {