Signals kinda work!

This commit is contained in:
Mark
2019-12-29 16:29:16 +02:00
parent ccc13ef0d5
commit 2cbd3e65c4
15 changed files with 320 additions and 4 deletions
+9 -1
View File
@@ -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);
+1
View File
@@ -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);
+13
View File
@@ -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
+3
View File
@@ -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
+10
View File
@@ -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);
+34
View File
@@ -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;
+86 -2
View File
@@ -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;
}
+61 -1
View File
@@ -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");
}
+1
View File
@@ -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)
+15
View File
@@ -1,4 +1,5 @@
#include <sys/fcntl.h>
#include <signal.h>
#include <dirent.h>
#include <unistd.h>
#include <stdint.h>
@@ -6,6 +7,10 @@
#include <errno.h>
#include <stdio.h>
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) {
+3
View File
@@ -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);
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <sys/signum.h>
typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);
+2
View File
@@ -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;
}
+63
View File
@@ -0,0 +1,63 @@
#include <bits/syscall.h>
#include <signal.h>
#include <stdio.h>
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;
}
+13
View File
@@ -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) {