Signals kinda work!
This commit is contained in:
@@ -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,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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <sys/signum.h>
|
||||
|
||||
typedef void (*sighandler_t) (int);
|
||||
|
||||
sighandler_t signal(int signum, sighandler_t handler);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user