diff --git a/kernel-src/arch/x86-64/mmu.c b/kernel-src/arch/x86-64/mmu.c index 52b244cf..c960f4e9 100644 --- a/kernel-src/arch/x86-64/mmu.c +++ b/kernel-src/arch/x86-64/mmu.c @@ -267,8 +267,12 @@ static void pfisr(isr_t *self, context_t *ctx) { if (ctx->error & ERROR_FETCH) vmmerror |= VMM_ACTION_EXEC; - if (vmm_pagefault((void *)ctx->cr2, ctx->cs != 8, vmmerror) == false) - _panic("Page fault", ctx); + if (vmm_pagefault((void *)ctx->cr2, ctx->cs != 8, vmmerror) == false) { + if (ARCH_CONTEXT_ISUSER(ctx)) + signal_signalthread(_cpu()->thread, SIGSEGV, true); + else + _panic("Page fault", ctx); + } } void arch_mmu_init() { diff --git a/kernel-src/arch/x86-64/syscall.asm b/kernel-src/arch/x86-64/syscall.asm index 8eaf2393..2fe7909d 100644 --- a/kernel-src/arch/x86-64/syscall.asm +++ b/kernel-src/arch/x86-64/syscall.asm @@ -57,6 +57,11 @@ extern syscall_getpgid extern syscall_getsid extern syscall_setsid extern syscall_setpgid +extern syscall_sigaction +extern syscall_sigaltstack +extern syscall_sigprocmask +extern syscall_kill +extern syscall_sigreturn syscalltab: dq syscall_print dq syscall_mmap @@ -116,7 +121,12 @@ dq syscall_getpgid dq syscall_getsid dq syscall_setsid dq syscall_setpgid -syscallcount equ 58 +dq syscall_sigaction +dq syscall_sigaltstack +dq syscall_sigprocmask +dq syscall_kill +dq syscall_sigreturn +syscallcount equ 63 section .text global arch_syscall_entry ; on entry: diff --git a/kernel-src/arch/x86-64/syscalllog.c b/kernel-src/arch/x86-64/syscalllog.c index 733e8ba0..af8a77a0 100644 --- a/kernel-src/arch/x86-64/syscalllog.c +++ b/kernel-src/arch/x86-64/syscalllog.c @@ -4,7 +4,7 @@ #ifdef SYSCALL_LOGGING -#define SYSCALL_COUNT 58 +#define SYSCALL_COUNT 63 #define LOGSTR(x) arch_e9_puts(x) static char *name[] = { @@ -65,7 +65,12 @@ static char *name[] = { "getpgid", "getsid", "setsid", - "setpgid" + "setpgid", + "sigaction", + "sigaltstack", + "sigprocmask", + "kill", + "sigreturn" }; static char *args[] = { @@ -127,6 +132,11 @@ static char *args[] = { "pid %d", // getsid "N/A", // setsid "pid %d pgid %d", // setpgid + "signal %d new %p old %p", // sigaction + "new %p old %p", // sigaltstack + "how %d set %p oldset %p", // sigprocmask + "pid %d signal %d", // kill + "N/A" // sigreturn }; #endif diff --git a/kernel-src/fs/vfs.c b/kernel-src/fs/vfs.c index 6d8025fc..6c9e89cc 100644 --- a/kernel-src/fs/vfs.c +++ b/kernel-src/fs/vfs.c @@ -346,10 +346,10 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int vnode_t *derefstart = current; if (*linkderef == '/' && _cpu()->thread && _cpu()->thread->proc) { - spinlock_acquire(&_cpu()->thread->proc->lock); + MUTEX_ACQUIRE(&_cpu()->thread->proc->mutex, false); derefstart = _cpu()->thread->proc->root; VOP_HOLD(derefstart); - spinlock_release(&_cpu()->thread->proc->lock); + MUTEX_RELEASE(&_cpu()->thread->proc->mutex); } else if (*linkderef == '/') { derefstart = vfsroot; VOP_HOLD(derefstart); diff --git a/kernel-src/include/kernel/jobctl.h b/kernel-src/include/kernel/jobctl.h index 0e39256c..6e9f34d3 100644 --- a/kernel-src/include/kernel/jobctl.h +++ b/kernel-src/include/kernel/jobctl.h @@ -13,5 +13,6 @@ pid_t jobctl_getpgid(proc_t *proc); pid_t jobctl_getsid(proc_t *proc); void jobctl_setctty(proc_t *proc, void *ctty); void *jobctl_getctty(proc_t *proc); +int jobctl_signal(proc_t *proc, int signal); #endif diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index 4961dbf8..41adb73f 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -7,6 +7,7 @@ #include #include #include +#include #define SCHED_THREAD_FLAGS_QUEUED 1 #define SCHED_THREAD_FLAGS_RUNNING 2 @@ -48,10 +49,17 @@ typedef struct thread_t { int wakeupreason; bool shouldexit; void *kernelarg; + struct { + spinlock_t lock; + stack_t stack; + sigset_t mask; + sigset_t pending; + sigset_t urgent; + } signals; } thread_t; typedef struct proc_t { - spinlock_t lock; + mutex_t mutex; int refcount; int status; int state; @@ -84,7 +92,6 @@ typedef struct proc_t { // these are only applicable if the process is leader struct proc_t *foreground; // process group leader, NULL if self void *controllingtty; - int processcount; // refcount } session; struct { @@ -96,23 +103,33 @@ typedef struct proc_t { // these are only applicable if the process is leader spinlock_t lock; // protects the linked list - int processcount; // refcount } pgrp; + struct { + spinlock_t lock; + sigaction_t actions[NSIG]; + sigset_t pending; + } signals; } proc_t; #include #define UMASK(mode) ((mode) & ~_cpu()->thread->proc->umask) #define PROC_HOLD(v) __atomic_add_fetch(&(v)->refcount, 1, __ATOMIC_SEQ_CST) +// the acquire of the pid table mutex is done here to prevent a nasty race condition where sched_inactiveproc could be called multiple times +// due to the pid table itself not holding any references to the process #define PROC_RELEASE(v) {\ + MUTEX_ACQUIRE(&sched_pidtablemutex, false); \ if (__atomic_sub_fetch(&(v)->refcount, 1, __ATOMIC_SEQ_CST) == 0) {\ sched_inactiveproc(v); \ (v) = NULL; \ } \ + MUTEX_RELEASE(&sched_pidtablemutex); \ } extern proc_t *sched_initproc; +extern mutex_t sched_pidtablemutex; +proc_t *sched_getprocfrompid(int pid); void sched_init(); void sched_stopotherthreads(); void sched_runinit(); diff --git a/kernel-src/include/kernel/signal.h b/kernel-src/include/kernel/signal.h new file mode 100644 index 00000000..df0a18bb --- /dev/null +++ b/kernel-src/include/kernel/signal.h @@ -0,0 +1,157 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include +#include + +typedef struct { + void *base; + int flags; + size_t size; +} stack_t; + +typedef struct { + uint64_t sig[1024 / 64]; +} sigset_t; + +#define SIGNAL_GET(sigset, signal) ((sigset)->sig[(signal) / 64] & (1lu << ((signal) % 64))) +#define SIGNAL_SETON(sigset, signal) (sigset)->sig[(signal) / 64] |= (1lu << ((signal) % 64)) +#define SIGNAL_SETOFF(sigset, signal) (sigset)->sig[(signal) / 64] &= ~(1lu << ((signal) % 64)) + +#define SIG_DFL ((void *)(0)) +#define SIG_IGN ((void *)(1)) + +#define SA_NOCLDSTOP 1 +#define SA_NOCLDWAIT 2 +#define SA_SIGINFO 4 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 +#define SA_RESTORER 0x04000000 + +#define NSIG 65 + +typedef struct { + void *address; + unsigned long flags; + void (*restorer)(void); + sigset_t mask; +} sigaction_t; + +#define POLL_IN 1 +#define POLL_OUT 2 +#define POLL_MSG 3 +#define POLL_ERR 4 +#define POLL_PRI 5 +#define POLL_HUP 6 + +union sigval { + int sival_int; + void *sival_ptr; +}; + +typedef long clock_t; + +// struct taken from musl. + +typedef struct { + int si_signo, si_errno, si_code; + union { + char __pad[128 - 2*sizeof(int) - sizeof(long)]; + struct { + union { + struct { + pid_t si_pid; + uid_t si_uid; + } __piduid; + struct { + int si_timerid; + int si_overrun; + } __timer; + } __first; + union { + union sigval si_value; + struct { + int si_status; + clock_t si_utime, si_stime; + } __sigchld; + } __second; + } __si_common; + struct { + void *si_addr; + short si_addr_lsb; + union { + struct { + void *si_lower; + void *si_upper; + } __addr_bnd; + unsigned si_pkey; + } __first; + } __sigfault; + struct { + long si_band; + int si_fd; + } __sigpoll; + struct { + void *si_call_addr; + int si_syscall; + unsigned si_arch; + } __sigsys; + } __si_fields; +} siginfo_t; + +#define SIGHUP 1 +#define SIGQUIT 3 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT SIGABRT +#define SIGBUS 7 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGWINCH 28 +#define SIGPOLL 29 +#define SIGSYS 31 +#define SIGUNUSED SIGSYS +#define SIGCANCEL 32 +#define SIGABRT 6 +#define SIGFPE 8 +#define SIGILL 4 +#define SIGINT 2 +#define SIGSEGV 11 +#define SIGTERM 15 +#define SIGPROF 27 +#define SIGIO 29 +#define SIGPWR 30 +#define SIGRTMIN 35 +#define SIGRTMAX 64 + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 + +struct proc_t; +struct thread_t; +void signal_action(struct proc_t *proc, int signal, sigaction_t *new, sigaction_t *old); +void signal_altstack(struct thread_t *thread, stack_t *new, stack_t *old); +void signal_changemask(struct thread_t *thread, int how, sigset_t *new, sigset_t *old); +void signal_signalproc(struct proc_t *proc, int signal); +void signal_signalthread(struct thread_t *thread, int signal, bool urgent); +void signal_pending(struct thread_t *, sigset_t *sigset); +void signal_check(struct thread_t *thread, context_t *context); + +#endif diff --git a/kernel-src/include/x86-64/arch/context.h b/kernel-src/include/x86-64/arch/context.h index 90f03dcc..774f12c0 100644 --- a/kernel-src/include/x86-64/arch/context.h +++ b/kernel-src/include/x86-64/arch/context.h @@ -40,9 +40,9 @@ typedef struct { typedef struct { uint64_t gsbase; uint64_t fsbase; - uint8_t fx[512]; + __attribute__((aligned(16))) uint8_t fx[512]; uint32_t mxcsr; -} extracontext_t __attribute__((aligned(16))); +} extracontext_t; #define CTX_INIT(x,u,interrupts) \ if (u) { \ @@ -67,6 +67,9 @@ typedef struct { #define CTX_IP(x) (x)->rip #define CTX_RET(x) (x)->rax #define CTX_ERRNO(x) (x)->rdx +#define CTX_ARG0(x) (x)->rdi +#define CTX_ARG1(x) (x)->rsi +#define CTX_ARG2(x) (x)->rdx #define PRINT_CTX(x) { \ printf("cr2: %016lx gs: %04x\nrax: %016lx fs: %04x\nrbx: %016lx es: %04x\nrcx: %016lx ds: %04x\nrdx: %016lx cs: %04x\n r8: %016lx ss: %04x\n r9: %016lx r10: %016lx\nr11: %016lx r12: %016lx\nr13: %016lx r14: %016lx\nr15: %016lx rdi: %016lx\nrsi: %016lx rbp: %016lx\nrip: %016lx rsp: %016lx\nerr: %016lx rfl: %016lx\n", \ @@ -103,6 +106,18 @@ void arch_context_saveandcall(void (*fn)(context_t *context), void *stack); asm("fxrstor (%%rax)" : : "a"(&(t)->extracontext.fx[0])); \ asm("ldmxcsr (%%rax)" : : "a"(&(t)->extracontext.mxcsr)); +#define ARCH_EXTRACONTEXT_LOAD(c) \ + wrmsr(MSR_KERNELGSBASE, (c)->gsbase); \ + wrmsr(MSR_FSBASE, (c)->fsbase); \ + asm("fxrstor (%%rax)" : : "a"(&(c)->fx[0])); \ + asm("ldmxcsr (%%rax)" : : "a"(&(c)->mxcsr)); + +#define ARCH_EXTRACONTEXT_SAVE(c) \ + (c)->gsbase = rdmsr(MSR_KERNELGSBASE); \ + (c)->fsbase = rdmsr(MSR_FSBASE); \ + asm("fxsave (%%rax)" : : "a"(&(c)->fx[0])); \ + asm("stmxcsr (%%rax)" : : "a"(&(c)->mxcsr)); + #define ARCH_CONTEXT_INTSTATUS(x) ((x)->rflags & 0x200 ? true : false) #define ARCH_CONTEXT_ISUSER(x) ((x)->cs == 0x23) diff --git a/kernel-src/include/x86-64/arch/signal.h b/kernel-src/include/x86-64/arch/signal.h new file mode 100644 index 00000000..93a7e893 --- /dev/null +++ b/kernel-src/include/x86-64/arch/signal.h @@ -0,0 +1,20 @@ +#ifndef _ARCH_SIGNAL_H +#define _ARCH_SIGNAL_H + +#include + +#define ARCH_SIGNAL_STACK_GROWS_DOWNWARDS 1 +#define ARCH_SIGNAL_REDZONE_SIZE 128 + +#define ARCH_SIGNAL_GETFROMRETURN(x) + +typedef struct { + void *restorer; + stack_t oldstack; + sigset_t oldmask; + context_t context; + extracontext_t extracontext; + siginfo_t siginfo; +} sigframe_t; + +#endif diff --git a/kernel-src/sys/jobctl.c b/kernel-src/sys/jobctl.c index 20ee0d32..7776aa76 100644 --- a/kernel-src/sys/jobctl.c +++ b/kernel-src/sys/jobctl.c @@ -284,3 +284,9 @@ void jobctl_procremove(proc_t *proc) { tty_t *tty = proc->session.controllingtty; VOP_RELEASE(tty->mastervnode); } + +// proc can be null if the signal is being sent to the calling processes own process group +// since that is only passed from a userland thread, we can assume _cpu()->thread->proc is not null +int jobctl_signal(proc_t *proc, int signal) { + +} diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index e7500eb5..120fbbcd 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -29,10 +29,21 @@ typedef struct { static rqueue_t runqueue[RUNQUEUE_COUNT]; static uint64_t runqueuebitmap; static spinlock_t runqueuelock; +static hashtable_t pidtable; +// not defined as static because its acquired/released from a macro in kernel/scheduler.h +mutex_t sched_pidtablemutex; proc_t *sched_initproc; static pid_t currpid = 1; +proc_t *sched_getprocfrompid(int pid) { + MUTEX_ACQUIRE(&sched_pidtablemutex, false); + void *proc = NULL; + hashtable_get(&pidtable, &proc, &pid, sizeof(pid)); + MUTEX_RELEASE(&sched_pidtablemutex); + return proc; +} + proc_t *sched_newproc() { proc_t *proc = slab_allocate(processcache); if (proc == NULL) @@ -50,7 +61,7 @@ proc_t *sched_newproc() { proc->state = SCHED_PROC_STATE_NORMAL; proc->threadtablesize = 1; proc->runningthreadcount = 1; - SPINLOCK_INIT(proc->lock); + MUTEX_INIT(&proc->mutex); SPINLOCK_INIT(proc->nodeslock); proc->fdcount = 3; proc->refcount = 1; @@ -59,6 +70,7 @@ proc_t *sched_newproc() { SEMAPHORE_INIT(&proc->waitsem, 0); SPINLOCK_INIT(proc->jobctllock); SPINLOCK_INIT(proc->pgrp.lock); + SPINLOCK_INIT(proc->signals.lock); proc->fd = alloc(sizeof(fd_t) * 3); if (proc->fd == NULL) { @@ -70,6 +82,17 @@ proc_t *sched_newproc() { proc->pid = __atomic_fetch_add(&currpid, 1, __ATOMIC_SEQ_CST); proc->umask = 022; // default umask + // add to pid table + MUTEX_ACQUIRE(&sched_pidtablemutex, false); + if (hashtable_set(&pidtable, proc, &proc->pid, sizeof(proc->pid), true)) { + MUTEX_RELEASE(&sched_pidtablemutex); + free(proc->fd); + free(proc->threads); + slab_free(processcache, proc); + return NULL; + } + MUTEX_RELEASE(&sched_pidtablemutex); + return proc; } @@ -105,6 +128,7 @@ thread_t *sched_newthread(void *ip, size_t kstacksize, int priority, proc_t *pro CTX_SP(&thread->context) = proc ? (ctxreg_t)ustack : (ctxreg_t)thread->kernelstacktop; CTX_IP(&thread->context) = (ctxreg_t)ip; SPINLOCK_INIT(thread->sleeplock); + SPINLOCK_INIT(thread->signals.lock); return thread; } @@ -241,6 +265,7 @@ __attribute__((noreturn)) void sched_stopcurrentthread() { // called when all threads in a process have exited void sched_procexit() { proc_t *proc = _cpu()->thread->proc; + __assert(proc != sched_initproc); // the process is going to be referenced by the child list, hold it // it only holds it now because it has to have running threads to be in the list @@ -254,7 +279,7 @@ void sched_procexit() { fd_close(fd); // zombify the proc - spinlock_acquire(&proc->lock); + MUTEX_ACQUIRE(&proc->mutex, false); VOP_RELEASE(proc->root); VOP_RELEASE(proc->cwd); @@ -263,7 +288,7 @@ void sched_procexit() { proc_t *lastchild = proc->child; - spinlock_acquire(&sched_initproc->lock); + MUTEX_ACQUIRE(&sched_initproc->mutex, false); bool haszombie = false; @@ -280,8 +305,8 @@ void sched_procexit() { sched_initproc->child = proc->child; } - spinlock_release(&sched_initproc->lock); - spinlock_release(&proc->lock); + MUTEX_RELEASE(&sched_initproc->mutex); + MUTEX_RELEASE(&proc->mutex); semaphore_signal(&proc->parent->waitsem); if (haszombie) @@ -290,9 +315,10 @@ void sched_procexit() { void sched_inactiveproc(proc_t *proc) { // proc refcount 0, we can free the structure - // TODO remove from the pid list + // the pid table lock is already acquired. + + hashtable_remove(&pidtable, &proc->pid, sizeof(proc->pid)); - //printf("destroying proc!\n"); sched_destroyproc(proc); } @@ -346,12 +372,14 @@ __attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t * interrupt_set(true); sched_threadexit(); } + + signal_check(thread, context); } void sched_stopotherthreads() { thread_t *thread = _cpu()->thread; proc_t *proc = thread->proc; - spinlock_acquire(&proc->lock); + MUTEX_ACQUIRE(&proc->mutex, false); proc->nomorethreads = true; for (int i = 0; i < proc->threadtablesize; ++i) { @@ -363,7 +391,7 @@ void sched_stopotherthreads() { proc->threads[i] = NULL; } - spinlock_release(&proc->lock); + MUTEX_RELEASE(&proc->mutex); while (__atomic_load_n(&proc->runningthreadcount, __ATOMIC_SEQ_CST) > 1) sched_yield(); @@ -563,6 +591,9 @@ void sched_init() { processcache = slab_newcache(sizeof(proc_t), 0, NULL, NULL); __assert(processcache); + __assert(hashtable_init(&pidtable, 100) == 0); + MUTEX_INIT(&sched_pidtablemutex); + _cpu()->schedulerstack = vmm_map(NULL, SCHEDULER_STACK_SIZE, VMM_FLAGS_ALLOCATE, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, NULL); __assert(_cpu()->schedulerstack); _cpu()->schedulerstack = (void *)((uintptr_t)_cpu()->schedulerstack + SCHEDULER_STACK_SIZE); @@ -658,6 +689,8 @@ void sched_runinit() { thread_t *uthread = sched_newthread(entry, PAGE_SIZE * 16, 1, proc, stack); __assert(uthread); + proc->threads[0] = uthread; + uthread->vmmctx = vmmctx; sched_queue(uthread); diff --git a/kernel-src/sys/signal.c b/kernel-src/sys/signal.c new file mode 100644 index 00000000..7b42e1bc --- /dev/null +++ b/kernel-src/sys/signal.c @@ -0,0 +1,311 @@ +#include +#include +#include + +#define PROCESS_ENTER(proc) \ + bool procintstatus = interrupt_set(false); \ + spinlock_acquire(&proc->signals.lock); + +#define PROCESS_LEAVE(proc) \ + spinlock_release(&proc->signals.lock); \ + interrupt_set(procintstatus); + +#define THREAD_ENTER(thread) \ + bool threadintstatus = interrupt_set(false); \ + spinlock_acquire(&thread->signals.lock); + +#define THREAD_LEAVE(thread) \ + spinlock_release(&thread->signals.lock); \ + interrupt_set(threadintstatus); + +#define SIGNAL_ASSERT(s) __assert((s) < NSIG && (s) >= 0) + +void signal_action(struct proc_t *proc, int signal, sigaction_t *new, sigaction_t *old) { + SIGNAL_ASSERT(signal); + PROCESS_ENTER(proc); + + if (old) + *old = proc->signals.actions[signal]; + + if (new) + proc->signals.actions[signal] = *new; + + PROCESS_LEAVE(proc); +} + +void signal_altstack(struct thread_t *thread, stack_t *new, stack_t *old) { + THREAD_ENTER(thread); + + if (old) + *old = thread->signals.stack; + + if (new) + thread->signals.stack = *new; + + THREAD_LEAVE(thread); +} + +void signal_changemask(struct thread_t *thread, int how, sigset_t *new, sigset_t *old) { + THREAD_ENTER(thread); + + if (old) + *old = thread->signals.mask; + + if (new) { + switch (how) { + case SIG_BLOCK: + case SIG_UNBLOCK: { + for (int i = 0; i < NSIG; ++i) { + if (SIGNAL_GET(new, i) == 0) + continue; + + if (how == SIG_BLOCK && SIGNAL_GET(&thread->signals.mask, i) == 0) + SIGNAL_SETON(&thread->signals.mask, i); + else if (how == SIG_UNBLOCK && SIGNAL_GET(&thread->signals.mask, i)) + SIGNAL_SETOFF(&thread->signals.mask, i); + } + break; + } + case SIG_SETMASK: { + thread->signals.mask = *new; + break; + } + default: + __assert(!"bad how"); + } + } + + THREAD_LEAVE(thread); +} + +void signal_pending(struct thread_t *thread, sigset_t *sigset) { + proc_t *proc = thread->proc; + PROCESS_ENTER(proc); + THREAD_ENTER(thread); + + // urgent set doesn't get returned here as it will always be handled + // before a return to userspace + memset(sigset, 0, sizeof(sigset_t)); + for (int i = 0; i < NSIG; ++i) { + if (SIGNAL_GET(&proc->signals.pending, i)) + SIGNAL_SETON(sigset, i); + + if (SIGNAL_GET(&thread->signals.pending, i)) + SIGNAL_SETON(sigset, i); + } + + THREAD_LEAVE(thread); + PROCESS_LEAVE(proc); +} + +void signal_signalthread(struct thread_t *thread, int signal, bool urgent) { + THREAD_ENTER(thread); + + sigset_t *sigset = urgent ? &thread->signals.urgent : &thread->signals.pending; + SIGNAL_SETON(sigset, signal); + if (signal == SIGKILL || signal == SIGSTOP || urgent || SIGNAL_GET(&thread->signals.mask, signal) == 0) + sched_wakeup(thread, SCHED_WAKEUP_REASON_INTERRUPTED); + + THREAD_LEAVE(thread); +} + +void signal_signalproc(struct proc_t *proc, int signal) { + // first, check if any threads have the signal unmasked, and set as pending for the first one + MUTEX_ACQUIRE(&proc->mutex, false); + PROCESS_ENTER(proc); + for (int i = 0; i < proc->threadtablesize; ++i) { + if (proc->threads[i] == NULL || (proc->threads[i]->flags & SCHED_THREAD_FLAGS_DEAD)) + continue; + + THREAD_ENTER(proc->threads[i]); + + // sigcont will wake up all the threads or unset sigstop as pending + if (signal == SIGCONT) { + // TODO this + } else if (SIGNAL_GET(&proc->threads[i]->signals.mask, signal) == 0 || signal == SIGKILL || signal == SIGSTOP) { + SIGNAL_SETON(&proc->threads[i]->signals.pending, signal); + // TODO ipi if running isn't self + sched_wakeup(proc->threads[i], SCHED_WAKEUP_REASON_INTERRUPTED); + THREAD_LEAVE(proc->threads[i]); + PROCESS_LEAVE(proc); + MUTEX_RELEASE(&proc->mutex); + return; + } + + THREAD_LEAVE(proc->threads[i]); + } + MUTEX_RELEASE(&proc->mutex); + + // there wasn't any thread with it unmasked, so set it as pending for the whole process + + SIGNAL_SETON(&proc->signals.pending, signal); + + PROCESS_LEAVE(proc); +} + +#define ACTION_TERM 0 +#define ACTION_IGN 1 +#define ACTION_CORE 2 +#define ACTION_STOP 3 +#define ACTION_CONT 4 + +static int defaultactions[NSIG] = { + [SIGABRT] = ACTION_CORE, + [SIGALRM] = ACTION_TERM, + [SIGBUS] = ACTION_CORE, + [SIGCHLD] = ACTION_IGN, + [SIGCONT] = ACTION_CONT, + [SIGFPE] = ACTION_CORE, + [SIGHUP] = ACTION_TERM, + [SIGILL] = ACTION_CORE, + [SIGINT] = ACTION_TERM, + [SIGIO] = ACTION_TERM, + [SIGKILL] = ACTION_TERM, + [SIGPIPE] = ACTION_TERM, + [SIGPOLL] = ACTION_TERM, + [SIGPROF] = ACTION_TERM, + [SIGPWR] = ACTION_TERM, + [SIGQUIT] = ACTION_CORE, + [SIGSEGV] = ACTION_CORE, + [SIGSTOP] = ACTION_STOP, + [SIGTSTP] = ACTION_STOP, + [SIGSYS] = ACTION_CORE, + [SIGTERM] = ACTION_TERM, + [SIGTRAP] = ACTION_CORE, + [SIGTTIN] = ACTION_STOP, + [SIGTTOU] = ACTION_STOP, + [SIGURG] = ACTION_IGN, + [SIGUSR1] = ACTION_TERM, + [SIGUSR2] = ACTION_TERM, + [SIGVTALRM] = ACTION_TERM, + [SIGXCPU] = ACTION_CORE, + [SIGXFSZ] = ACTION_CORE, + [SIGWINCH] = ACTION_IGN +}; + +void signal_check(struct thread_t *thread, context_t *context) { + proc_t *proc = thread->proc; + + PROCESS_ENTER(proc); + THREAD_ENTER(thread); + + if (SIGNAL_GET(&proc->signals.pending, SIGKILL) || SIGNAL_GET(&thread->signals.pending, SIGKILL)) { + THREAD_LEAVE(thread); + PROCESS_LEAVE(proc); + proc->status = SIGKILL; + interrupt_set(true); + sched_threadexit(); + } + + // find the first pending signal which is unmasked + + int signal; + sigset_t *sigset = NULL; + + // check the urgent sigset first + for (int i = 0; i < NSIG; ++i) { + if (SIGNAL_GET(&thread->signals.urgent, i) == 0) + continue; + + signal = i; + sigset = &thread->signals.urgent; + } + + for (int i = 0; i < NSIG && sigset == NULL; ++i) { + if (SIGNAL_GET(&thread->signals.mask, i)) + continue; + + if (SIGNAL_GET(&thread->signals.pending, i)) { + signal = i; + sigset = &thread->signals.pending; + break; + } + + if (SIGNAL_GET(&proc->signals.pending, i)) { + signal = i; + sigset = &proc->signals.pending; + break; + } + } + + if (sigset == NULL) + goto leave; + + sigaction_t *action = &proc->signals.actions[signal]; + SIGNAL_SETOFF(sigset, signal); + + // urgent signals cannot be ignored, if they are the default action will be run + if (action->address == SIG_IGN && sigset != &thread->signals.urgent) { + goto leave; + } else if ((action->address == SIG_DFL && (action->flags & SA_SIGINFO) == 0) || (action->address == SIG_IGN && sigset == &thread->signals.urgent)) { + // default action + int defaction = defaultactions[signal]; + switch (defaction) { + case ACTION_IGN: + goto leave; + case ACTION_CORE: + case ACTION_TERM: + proc->status = signal; + interrupt_set(true); + sched_threadexit(); + default: + __assert(!"unsupported signal action"); + } + } else { + // execute handler + ARCH_CONTEXT_THREADSAVE(thread, context); + + void *altstack = NULL; + // figure out the stack we will be running the signal handler in + if (action->flags & SA_ONSTACK && thread->signals.stack.size > 0) + altstack = thread->signals.stack.base; + + // get where in memory to put the frame + void *stack = altstack ? altstack : (void *)CTX_SP(context); + #if ARCH_SIGNAL_STACK_GROWS_DOWNWARDS == 1 + stack = (void *)(((uintptr_t)stack - ARCH_SIGNAL_REDZONE_SIZE - sizeof(sigframe_t)) & ~0xf); + #else + #error unsupported + #endif + + // configure stack frame + sigframe_t *sigframe = stack; + sigframe->restorer = action->restorer; + if (altstack) { + memcpy(&sigframe->oldstack, &thread->signals.stack, sizeof(stack_t)); + memset(&thread->signals.stack, 0, sizeof(stack_t)); + } + memcpy(&sigframe->oldmask, &thread->signals.mask, sizeof(sigset_t)); + memcpy(&sigframe->context, context, sizeof(context_t)); + memcpy(&sigframe->extracontext, &thread->extracontext, sizeof(extracontext_t)); + // TODO siginfo + + // configure new thread signal mask + if ((action->flags & SA_NODEFER) == 0) + SIGNAL_SETON(&thread->signals.mask, signal); + + for (int i = 0; i < signal; ++i) { + if (SIGNAL_GET(&action->mask, i) == 0) + continue; + + SIGNAL_SETON(&thread->signals.mask, i); + } + + // configure return context + memset(context, 0, sizeof(context_t)); + CTX_INIT(context, true, true); + CTX_IP(context) = (uint64_t)action->address; + CTX_SP(context) = (uint64_t)stack; + CTX_ARG0(context) = signal; + CTX_ARG1(context) = 0; // TODO pass siginfo + CTX_ARG2(context) = 0; // TODO ucontext + + // reset handler if asked for + if (action->flags & SA_RESETHAND) + memset(&action, 0, sizeof(sigaction_t)); + } + + leave: + THREAD_LEAVE(thread); + PROCESS_LEAVE(proc); +} diff --git a/kernel-src/sys/syscalls/exit.c b/kernel-src/sys/syscalls/exit.c index 9558fb25..aa46b454 100644 --- a/kernel-src/sys/syscalls/exit.c +++ b/kernel-src/sys/syscalls/exit.c @@ -8,12 +8,12 @@ __attribute__((noreturn)) void syscall_exit(context_t *context, int status) { thread_t *thread = _cpu()->thread; proc_t *proc = thread->proc; - __assert(proc != sched_initproc); - __assert(spinlock_try(&proc->exiting)); // only one thread can exit at the same time + if (spinlock_try(&proc->exiting) == false) + sched_threadexit(); sched_stopotherthreads(); - proc->status = status; + proc->status = status << 8; sched_threadexit(); } diff --git a/kernel-src/sys/syscalls/fork.c b/kernel-src/sys/syscalls/fork.c index 44297ffe..782caed0 100644 --- a/kernel-src/sys/syscalls/fork.c +++ b/kernel-src/sys/syscalls/fork.c @@ -35,15 +35,13 @@ syscallret_t syscall_fork(context_t *ctx) { if (ret.errno) goto cleanup; - bool intstate = interrupt_set(false); - spinlock_acquire(&proc->lock); + MUTEX_ACQUIRE(&proc->mutex, false); nproc->parent = proc; nproc->sibling = proc->child; proc->child = nproc; - spinlock_release(&proc->lock); - interrupt_set(intstate); + MUTEX_RELEASE(&proc->mutex); nproc->umask = _cpu()->thread->proc->umask; nproc->root = sched_getroot(); diff --git a/kernel-src/sys/syscalls/kill.c b/kernel-src/sys/syscalls/kill.c new file mode 100644 index 00000000..e6694645 --- /dev/null +++ b/kernel-src/sys/syscalls/kill.c @@ -0,0 +1,40 @@ +#include +#include +#include + +syscallret_t syscall_kill(context_t *context, int pid, int signal) { + syscallret_t ret = { + .ret = -1 + }; + + int effectivepid = -1; + + if (pid > 0) // specific process + effectivepid = pid; + else if (pid == 0) // own process group + effectivepid = 0; + else if (pid == -1) // every process, unsupported atm + effectivepid = -1; + else if (pid < -1) + effectivepid = -pid; // target specific process group + + if (effectivepid == -1) { + ret.errno = ESRCH; + return ret; + } + + proc_t *target = sched_getprocfrompid(pid); + if (target == NULL && pid != 0) { + ret.errno = ESRCH; + return ret; + } + + if (pid >= 1) + signal_signalproc(target, signal); + else + ret.errno = jobctl_signal(target, signal); + + ret.errno = 0; + ret.ret = 0; + return ret; +} diff --git a/kernel-src/sys/syscalls/newthread.c b/kernel-src/sys/syscalls/newthread.c index c0e4d10a..87afb877 100644 --- a/kernel-src/sys/syscalls/newthread.c +++ b/kernel-src/sys/syscalls/newthread.c @@ -16,11 +16,11 @@ syscallret_t syscall_newthread(context_t *, void *entry, void *stack) { proc_t *proc = _cpu()->thread->proc; - spinlock_acquire(&proc->lock); + MUTEX_ACQUIRE(&proc->mutex, false); // if we currently don't want any more threads to start if (proc->nomorethreads == true) { - spinlock_release(&proc->lock); + MUTEX_RELEASE(&proc->mutex); ret.errno = EAGAIN; return ret; } @@ -63,6 +63,6 @@ syscallret_t syscall_newthread(context_t *, void *entry, void *stack) { sched_queue(proc->threads[i]); cleanup: - spinlock_release(&proc->lock); + MUTEX_RELEASE(&proc->mutex); return ret; } diff --git a/kernel-src/sys/syscalls/sigaction.c b/kernel-src/sys/syscalls/sigaction.c new file mode 100644 index 00000000..977830e0 --- /dev/null +++ b/kernel-src/sys/syscalls/sigaction.c @@ -0,0 +1,29 @@ +#include +#include +#include + +syscallret_t syscall_sigaction(context_t *, int sig, sigaction_t *new, sigaction_t *old) { + syscallret_t ret = { + .ret = -1 + }; + + if (sig > NSIG || sig == SIGKILL || sig == SIGSTOP) { + ret.errno = EINVAL; + return ret; + } + + sigaction_t newtmp, oldtmp; + if (new) { + newtmp = *new; + __assert(newtmp.flags & SA_RESTORER); + } + + signal_action(_cpu()->thread->proc, sig, new ? &newtmp : NULL, old ? &oldtmp : NULL); + + if (old) + *old = oldtmp; + + ret.errno = 0; + ret.ret = 0; + return ret; +} diff --git a/kernel-src/sys/syscalls/sigaltstack.c b/kernel-src/sys/syscalls/sigaltstack.c new file mode 100644 index 00000000..091557ee --- /dev/null +++ b/kernel-src/sys/syscalls/sigaltstack.c @@ -0,0 +1,21 @@ +#include +#include + +syscallret_t syscall_sigaltstack(context_t *, stack_t *new, stack_t *old) { + syscallret_t ret = { + .ret = -1 + }; + + stack_t newtmp, oldtmp; + if (new) + newtmp = *new; + + signal_altstack(_cpu()->thread, new ? &newtmp : NULL, old ? &oldtmp : NULL); + + if (old) + *old = oldtmp; + + ret.errno = 0; + ret.ret = 0; + return ret; +} diff --git a/kernel-src/sys/syscalls/sigprocmask.c b/kernel-src/sys/syscalls/sigprocmask.c new file mode 100644 index 00000000..7c61a667 --- /dev/null +++ b/kernel-src/sys/syscalls/sigprocmask.c @@ -0,0 +1,32 @@ +#include +#include + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 + +syscallret_t syscall_sigprocmask(context_t *, int how, sigset_t *set, sigset_t *oldset) { + syscallret_t ret = { + .ret = -1 + }; + + sigset_t new, old; + + if (set) { + if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) { + ret.errno = EINVAL; + return ret; + } + + new = *set; + } + + signal_changemask(_cpu()->thread, how, set ? &new : NULL, oldset ? &old : NULL); + + if (oldset) + *oldset = old; + + ret.ret = 0; + ret.errno = 0; + return ret; +} diff --git a/kernel-src/sys/syscalls/sigreturn.c b/kernel-src/sys/syscalls/sigreturn.c new file mode 100644 index 00000000..66cb6fee --- /dev/null +++ b/kernel-src/sys/syscalls/sigreturn.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +// this expects CTX_SP to be pointing to the signal frame +__attribute__((noreturn)) void syscall_sigreturn(context_t *context) { + // TODO check if the information is valid + sigframe_t sigframe; + memcpy(&sigframe, (void *)CTX_SP(context), sizeof(sigframe_t)); + + interrupt_set(false); + signal_altstack(_cpu()->thread, &sigframe.oldstack, NULL); + signal_changemask(_cpu()->thread, SIG_SETMASK, &sigframe.oldmask, NULL); + + __assert(ARCH_CONTEXT_ISUSER(&sigframe.context)); + memcpy(&_cpu()->thread->extracontext, &sigframe.extracontext, sizeof(extracontext_t)); + ARCH_CONTEXT_THREADLOAD(_cpu()->thread, context); + + arch_context_switch(&sigframe.context); + + // if we were to return normally, CTX_RET and CTX_ERRNO would be corrupted, + // so we will manually call arch_context_switch, which will jump to the right userland code + // (and also check for pending signals again) + __builtin_unreachable(); +} diff --git a/kernel-src/sys/syscalls/waitpid.c b/kernel-src/sys/syscalls/waitpid.c index 1e2d5db9..4d8672eb 100644 --- a/kernel-src/sys/syscalls/waitpid.c +++ b/kernel-src/sys/syscalls/waitpid.c @@ -19,7 +19,7 @@ syscallret_t syscall_waitpid(context_t *context, pid_t pid, int *status, int opt thread_t *thread = _cpu()->thread; proc_t *proc = thread->proc; - spinlock_acquire(&proc->lock); + MUTEX_ACQUIRE(&proc->mutex, false); proc_t *prev = NULL; proc_t *iterator = proc->child; @@ -29,10 +29,10 @@ syscallret_t syscall_waitpid(context_t *context, pid_t pid, int *status, int opt if (iterator == NULL) { if (pid > 0 && desired == NULL) { ret.errno = ECHILD; - spinlock_release(&proc->lock); + MUTEX_RELEASE(&proc->mutex); return ret; } - spinlock_release(&proc->lock); + MUTEX_RELEASE(&proc->mutex); if ((options & WNOHANG) && semaphore_test(&proc->waitsem) == false) { ret.errno = 0; ret.ret = 0; @@ -41,7 +41,7 @@ syscallret_t syscall_waitpid(context_t *context, pid_t pid, int *status, int opt ret.errno = EINTR; return ret; } - spinlock_acquire(&proc->lock); + MUTEX_ACQUIRE(&proc->mutex, false); prev = NULL; iterator = proc->child; desired = NULL; // set as null to be certain another thread didn't get it @@ -65,12 +65,12 @@ syscallret_t syscall_waitpid(context_t *context, pid_t pid, int *status, int opt else proc->child = iterator->sibling; - spinlock_release(&proc->lock); + MUTEX_RELEASE(&proc->mutex); // TODO memory safe operation - // TODO the signal number goes in the low byte of status - if (status) - *status = iterator->status << 8; + if (status) { + *status = iterator->status; + } for (int i = 0; i < iterator->threadtablesize; ++i) { if (iterator->threads[i] == NULL) diff --git a/patches/mlibc/jinx-working-patch.patch b/patches/mlibc/jinx-working-patch.patch index 68ebcef6..a2ff5e4a 100644 --- a/patches/mlibc/jinx-working-patch.patch +++ b/patches/mlibc/jinx-working-patch.patch @@ -13,10 +13,10 @@ index eb7be7b..eb83b56 100644 #define IPPROTO_HOPOPTS 0 #define IPPROTO_ICMP 1 diff --git mlibc-clean/meson.build mlibc-workdir/meson.build -index 471604e..3c5bea0 100644 +index 905fbb9..6360ca5 100644 --- mlibc-clean/meson.build +++ mlibc-workdir/meson.build -@@ -248,6 +248,12 @@ elif host_machine.system() == 'dripos' +@@ -253,6 +253,12 @@ elif host_machine.system() == 'dripos' rtld_include_dirs += include_directories('sysdeps/dripos/include') libc_include_dirs += include_directories('sysdeps/dripos/include') subdir('sysdeps/dripos') @@ -175,10 +175,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..6ece9e1 +index 0000000..440f0dc --- /dev/null +++ mlibc-workdir/sysdeps/astral/generic/generic.cpp -@@ -0,0 +1,652 @@ +@@ -0,0 +1,673 @@ +#include +#include +#include @@ -310,19 +310,40 @@ index 0000000..6ece9e1 + return ENOSYS; + } + -+ int sys_sigprocmask(int how, const sigset_t *__restrict set, -+ sigset_t *__restrict retrieve) { -+ //mlibc::infoLogger() << "mlibc: sigprocmask is a stub" << frg::endlog; -+ return 0; ++ int sys_kill(int pid, int signal) { ++ long ret; ++ return syscall(SYSCALL_KILL, &ret, pid, signal); + } + -+ int sys_sigaction(int, const struct sigaction *__restrict, -+ struct sigaction *__restrict) { -+ //mlibc::infoLogger() << "mlibc: sigaction is a stub" << frg::endlog; -+ return 0; ++ int sys_sigprocmask(int how, const sigset_t *__restrict set, sigset_t *__restrict retrieve) { ++ long ret; ++ return syscall(SYSCALL_SIGPROCMASK, &ret, how, (uint64_t)set, (uint64_t)retrieve); ++ } ++ ++ int sys_sigaltstack(const stack_t *ss, stack_t *oss) { ++ long ret; ++ return syscall(SYSCALL_SIGALTSTACK, &ret, (uint64_t)ss, (uint64_t)oss); + } + + #ifndef MLIBC_BUILDING_RTLD ++ extern "C" void __mlibc_restorer(); ++ ++ int sys_sigaction(int sig, const struct sigaction *__restrict act, ++ struct sigaction *__restrict oldact) { ++ long ret; ++ ++ struct sigaction newact; ++ if (act) ++ newact = *act; ++ ++ if (act && (newact.sa_flags & SA_RESTORER) == 0) { ++ newact.sa_restorer = __mlibc_restorer; ++ newact.sa_flags |= SA_RESTORER; ++ } ++ ++ return syscall(SYSCALL_SIGACTION, &ret, sig, act ? (uint64_t)&newact : 0, (uint64_t)oldact); ++ } ++ + int sys_ptsname(int fd, char *buffer, size_t length) { + int index; + int tmp; @@ -850,10 +871,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..859c459 +index 0000000..14f3c2b --- /dev/null +++ mlibc-workdir/sysdeps/astral/include/astral/syscall.h -@@ -0,0 +1,81 @@ +@@ -0,0 +1,86 @@ +#ifndef _SYSCALL_H_INCLUDE +#define _SYSCALL_H_INCLUDE + @@ -915,6 +936,11 @@ index 0000000..859c459 +#define SYSCALL_GETSID 55 +#define SYSCALL_SETSID 56 +#define SYSCALL_SETPGID 57 ++#define SYSCALL_SIGACTION 58 ++#define SYSCALL_SIGALTSTACK 59 ++#define SYSCALL_SIGPROCMASK 60 ++#define SYSCALL_KILL 61 ++#define SYSCALL_SIGRETURN 62 + +#include +#include @@ -937,10 +963,10 @@ index 0000000..859c459 +#endif diff --git mlibc-workdir/sysdeps/astral/meson.build mlibc-workdir/sysdeps/astral/meson.build new file mode 100644 -index 0000000..31d913e +index 0000000..f7c5b72 --- /dev/null +++ mlibc-workdir/sysdeps/astral/meson.build -@@ -0,0 +1,75 @@ +@@ -0,0 +1,76 @@ + +rtld_sources += files( + 'generic/generic.cpp', @@ -953,6 +979,7 @@ index 0000000..31d913e + 'generic/generic.cpp', + 'threading/x86_64-thread.cpp', + 'threading/x86_64-thread-entry.S', ++ 'signal/x86_64-restorer.S', +) + +if not no_headers @@ -1016,6 +1043,18 @@ index 0000000..31d913e + install_dir: get_option('libdir') + ) +endif +diff --git mlibc-workdir/sysdeps/astral/signal/x86_64-restorer.S mlibc-workdir/sysdeps/astral/signal/x86_64-restorer.S +new file mode 100644 +index 0000000..dd33a36 +--- /dev/null ++++ mlibc-workdir/sysdeps/astral/signal/x86_64-restorer.S +@@ -0,0 +1,6 @@ ++.section .text ++.global __mlibc_restorer ++__mlibc_restorer: ++ sub $8, %rsp ++ mov $62, %rax ++ syscall 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..4cb3ae0