From 8970ddd9be6c013ff8cae2b1872f65348c7ded54 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Mon, 12 Aug 2024 22:05:00 -0400 Subject: kernel: Add initial support for signals Signed-off-by: Ian Moffett --- sys/include/sys/proc.h | 5 + sys/include/sys/signal.h | 84 ++++++++++++++++ sys/kern/kern_exec.c | 4 +- sys/kern/kern_fork.c | 2 + sys/kern/kern_sched.c | 1 + sys/kern/kern_sig.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++ sys/kern/kern_stub.c | 33 ++++++ 7 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 sys/include/sys/signal.h create mode 100644 sys/kern/kern_sig.c (limited to 'sys') diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h index b9db603..b10ee41 100644 --- a/sys/include/sys/proc.h +++ b/sys/include/sys/proc.h @@ -38,6 +38,7 @@ #include #include #include +#include #if defined(_KERNEL) #include #include @@ -48,10 +49,12 @@ #define PROC_STACK_PAGES 8 #define PROC_STACK_SIZE (PROC_STACK_PAGES * DEFAULT_PAGESIZE) #define PROC_MAX_FILEDES 256 +#define PROC_SIGMAX 64 struct proc { pid_t pid; struct exec_prog exec; + struct ksiginfo *ksig_list[PROC_SIGMAX]; struct filedesc *fds[PROC_MAX_FILEDES]; struct trapframe tf; struct pcb pcb; @@ -59,6 +62,8 @@ struct proc { bool rested; uint32_t flags; uintptr_t stack_base; + struct spinlock ksigq_lock; + TAILQ_HEAD(, ksiginfo) ksigq; TAILQ_ENTRY(proc) link; }; diff --git a/sys/include/sys/signal.h b/sys/include/sys/signal.h new file mode 100644 index 0000000..f35ff82 --- /dev/null +++ b/sys/include/sys/signal.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_SIGNAL_H_ +#define _SYS_SIGNAL_H_ + +#include +#include +#include + +#define SIGFPE 8 /* Floating point exception */ +#define SIGKILL 9 /* Kill */ +#define SIGSEGV 11 /* Segmentation violation */ + +typedef uint32_t sigset_t; + +typedef struct { + int si_signo; + int si_code; +} siginfo_t; + +struct sigaction { + void(*sa_handler)(int signo); + sigset_t sa_mask; + int sa_flags; + void(*sa_sigaction)(int signo, siginfo_t *si, void *p); +}; + +#if defined(_KERNEL) +struct proc; + +struct ksiginfo { + int signo; + int sigcode; + struct sigaction *si; + TAILQ_ENTRY(ksiginfo) link; +}; + +/* Signal management */ +int newsig(struct proc *td, int signo, struct ksiginfo **ksig); +int delsig(struct proc *td, int signo); +int sendsig(struct proc *td, const sigset_t *set); +void dispatch_signals(struct proc *td); +int signals_init(struct proc *td); + +/* Sigset functions */ +int sigemptyset(sigset_t *set); +int sigfillset(sigset_t *set); +int sigaddset(sigset_t *set, int signo); +int sigdelset(sigset_t *set, int signo); +int sigismember(const sigset_t *set, int signo); + +/* Default handlers */ +void sigfpe_default(int signo); +void sigkill_default(int signo); +void sigsegv_default(int signo); +#endif /* _KERNEL */ +#endif /* !_SYS_SIGNAL_H_ */ diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index ceb08f6..863f4ae 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -97,9 +98,10 @@ execve(struct proc *td, const struct execve_args *args) prog.envp = args->envp; stack_top = td->stack_base + (PROC_STACK_SIZE - 1); - /* Setup registers and stack */ + /* Setup registers, signals and stack */ md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog); setregs(td, &prog, stack_top); + signals_init(td); /* Done, reset flags and start the user thread */ td->flags &= ~PROC_EXEC; diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 037026b..7bcff4b 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,7 @@ fork1(struct proc *cur, int flags, void(*ip)(void), struct proc **newprocp) *newprocp = newproc; newproc->pid = ++nthreads; + signals_init(newproc); sched_enqueue_td(newproc); done: if (status != 0) diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index 6d85330..5f2b019 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -187,6 +187,7 @@ sched_switch(struct trapframe *tf) td = ci->curtd; if (td != NULL) { + dispatch_signals(td); td_pri_update(td); } diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c new file mode 100644 index 0000000..ed29397 --- /dev/null +++ b/sys/kern/kern_sig.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define sigmask(SIGNO) BIT(SIGNO) + +static struct sigaction sa_tab[] = { + [SIGFPE] = { + .sa_handler = sigfpe_default, + .sa_mask = 0, + .sa_flags = 0, + .sa_sigaction = NULL + }, + + [SIGKILL] = { + .sa_handler = sigkill_default, + .sa_mask = 0, + .sa_flags = 0, + .sa_sigaction = NULL + }, + + [SIGSEGV] = { + .sa_handler = sigsegv_default, + .sa_mask = 0, + .sa_flags = 0, + .sa_sigaction = NULL + }, +}; + +/* + * Register a new signal descriptor, set it in the process + * structure and return it in `ksig`. + * + * @td: Process to register signal to. + * @signo: Signal number to register. + * @ksig: Will contain a pointer to new signal descriptor. + */ +int +newsig(struct proc *td, int signo, struct ksiginfo **ksig) +{ + struct ksiginfo *ksig_tmp; + + /* Ensure we have valid args */ + if (td == NULL || signo >= PROC_SIGMAX) + return -EINVAL; + + /* + * Make sure we have a valid signal list. If we already + * have a signal registered in a slot, free up memory used + * for that signal descriptor so we can override it with the + * new one. + */ + if (td->ksig_list == NULL) + return -EIO; + if ((ksig_tmp = td->ksig_list[signo]) != NULL) + dynfree(ksig_tmp); + + /* Allocate our new signal */ + ksig_tmp = dynalloc(sizeof(*ksig_tmp)); + if (ksig_tmp == NULL) + return -ENOMEM; + + memset(ksig_tmp, 0, sizeof(*ksig_tmp)); + ksig_tmp->signo = signo; + td->ksig_list[signo] = ksig_tmp; + *ksig = ksig_tmp; + return 0; +} + +/* + * Remove a signal from the signal table. + * + * @td: Process to remove signal from. + * @signo: Signal to remove. + */ +int +delsig(struct proc *td, int signo) +{ + struct ksiginfo *ksig_tmp; + + /* Ensure we have valid args */ + if (td == NULL || signo >= PROC_SIGMAX) + return -EINVAL; + + /* Don't do anything if it doesn't exist */ + if ((ksig_tmp = td->ksig_list[signo]) == NULL) + return 0; + + dynfree(ksig_tmp); + td->ksig_list[signo] = NULL; + return 0; +} + +int +sendsig(struct proc *td, const sigset_t *set) +{ + struct ksiginfo *ksig_tmp; + + /* Ensure arguments are correct */ + if (td == NULL || set == NULL) + return -EINVAL; + + /* Enqueue required ksiginfo structures */ + for (int i = 0; i < PROC_SIGMAX; ++i) { + if ((ksig_tmp = td->ksig_list[i]) == NULL) { + continue; + } + + /* Enqueue if it is a member of the sigset */ + if (sigismember(set, i)) { + spinlock_acquire(&td->ksigq_lock); + TAILQ_INSERT_TAIL(&td->ksigq, ksig_tmp, link); + spinlock_release(&td->ksigq_lock); + } + } + + return 0; +} + +int +signals_init(struct proc *td) +{ + struct sigaction *sa; + struct ksiginfo *ksig; + int error, i; + + TAILQ_INIT(&td->ksigq); + + /* Populate process signal table with defaults */ + for (i = 0; i < NELEM(sa_tab); ++i) { + sa = &sa_tab[i]; + + /* Drop actions that aren't set up */ + if (sa->sa_handler == NULL && sa->sa_sigaction == NULL) { + continue; + } + + /* Attempt to register the new signal */ + if ((error = newsig(td, i, &ksig)) != 0) { + goto fail; + } + + ksig->si = sa; + } + + return 0; + +fail: + /* + * We do not want the process signal table to be in + * an inconsistent state so we clean up on failure. + */ + for (int j = 0; j < i; ++j) { + if ((error = delsig(td, j)) != 0) { + kprintf("delsig() failed on signal %d (returned %d)\n", + j, error); + } + } + + return error; +} + +void +dispatch_signals(struct proc *td) +{ + struct ksiginfo *ksig_tmp; + struct sigaction *action; + + spinlock_acquire(&td->ksigq_lock); + while (!TAILQ_EMPTY(&td->ksigq)) { + /* Dequeue signal descriptor */ + ksig_tmp = TAILQ_FIRST(&td->ksigq); + TAILQ_REMOVE(&td->ksigq, ksig_tmp, link); + + /* Invoke handler */ + action = ksig_tmp->si; + action->sa_handler(ksig_tmp->signo); + } + + spinlock_release(&td->ksigq_lock); +} + +int +sigemptyset(sigset_t *set) +{ + *set = 0; + return 0; +} + +int +sigfillset(sigset_t *set) +{ + *set = ~(sigset_t)0; + return 0; +} + +int +sigaddset(sigset_t *set, int signo) +{ + if (signo <= 0 || signo >= PROC_SIGMAX) + return -EINVAL; + + *set |= sigmask(signo); + return 0; +} + +int +sigdelset(sigset_t *set, int signo) +{ + if (signo <= 0 || signo >= PROC_SIGMAX) + return -EINVAL; + + *set &= ~sigmask(signo); + return 0; +} + +int +sigismember(const sigset_t *set, int signo) +{ + if (signo <= 0 || signo >= PROC_SIGMAX) + return -EINVAL; + + return (*set & sigmask(signo)) != 0; +} diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c index 65688e2..5a53cc1 100644 --- a/sys/kern/kern_stub.c +++ b/sys/kern/kern_stub.c @@ -30,6 +30,39 @@ #include #include #include +#include +#include +#include + +void +sigfpe_default(int signo) +{ + static struct proc *td; + + td = this_td(); + kprintf("Floating point exception (pid=%d)\n", td->pid); + exit1(td); +} + +void +sigkill_default(int signo) +{ + static struct proc *td; + + td = this_td(); + kprintf("Terminated (pid=%d)\n", td->pid); + exit1(td); +} + +void +sigsegv_default(int signo) +{ + static struct proc *td; + + td = this_td(); + kprintf("Segmentation fault (pid=%d)\n", td->pid); + exit1(td); +} int dev_noread(void) -- cgit v1.2.3