initial signal implementation

This commit is contained in:
Matheus
2024-05-14 15:04:48 -03:00
parent 00c212fa8d
commit 7b0cc4c51b
22 changed files with 824 additions and 55 deletions
+6 -2
View File
@@ -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() {
+11 -1
View File
@@ -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:
+12 -2
View File
@@ -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
+2 -2
View File
@@ -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);
+1
View File
@@ -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
+20 -3
View File
@@ -7,6 +7,7 @@
#include <kernel/vfs.h>
#include <semaphore.h>
#include <mutex.h>
#include <kernel/signal.h>
#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 <arch/cpu.h>
#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();
+157
View File
@@ -0,0 +1,157 @@
#ifndef _SIGNAL_H
#define _SIGNAL_H
#include <stdint.h>
#include <stddef.h>
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
+17 -2
View File
@@ -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)
+20
View File
@@ -0,0 +1,20 @@
#ifndef _ARCH_SIGNAL_H
#define _ARCH_SIGNAL_H
#include <kernel/signal.h>
#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
+6
View File
@@ -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) {
}
+42 -9
View File
@@ -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);
+311
View File
@@ -0,0 +1,311 @@
#include <kernel/scheduler.h>
#include <arch/signal.h>
#include <logging.h>
#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);
}
+3 -3
View File
@@ -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();
}
+2 -4
View File
@@ -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();
+40
View File
@@ -0,0 +1,40 @@
#include <kernel/syscalls.h>
#include <arch/cpu.h>
#include <logging.h>
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;
}
+3 -3
View File
@@ -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;
}
+29
View File
@@ -0,0 +1,29 @@
#include <kernel/syscalls.h>
#include <arch/cpu.h>
#include <logging.h>
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;
}
+21
View File
@@ -0,0 +1,21 @@
#include <kernel/syscalls.h>
#include <arch/cpu.h>
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;
}
+32
View File
@@ -0,0 +1,32 @@
#include <kernel/syscalls.h>
#include <arch/cpu.h>
#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;
}
+26
View File
@@ -0,0 +1,26 @@
#include <kernel/syscalls.h>
#include <arch/signal.h>
#include <arch/cpu.h>
#include <logging.h>
// 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();
}
+8 -8
View File
@@ -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)
+55 -16
View File
@@ -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 <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/all-sysdeps.hpp>
@@ -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 <stddef.h>
+#include <stdint.h>
@@ -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