diff --git a/kernel/include/kernel/process.h b/kernel/include/kernel/process.h index 13997bf..cd206db 100644 --- a/kernel/include/kernel/process.h +++ b/kernel/include/kernel/process.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -13,7 +14,7 @@ typedef struct process process; typedef struct process_queue process_queue; -typedef enum { TASK_RUNNING, TASK_READY, TASK_BLOCKED, TASK_DEAD } task_state_t; +typedef enum { TASK_RUNNING, TASK_READY, TASK_BLOCKED, TASK_STOPPED, TASK_DEAD } task_state_t; typedef union { int32_t raw; @@ -43,6 +44,10 @@ struct process { process* p_parent; exit_status p_exitstatus; int64_t p_waitforchild; + process_queue* p_waiting_on_queue; + uint64_t p_pending; + uint64_t p_sigmask; + sigaction p_sigactions[NSIG]; }; struct process_queue { @@ -59,6 +64,10 @@ int enqueue_process (process_queue* queue, process* new_process); void process_block (process_queue* wait_queue); void process_unblock (process* p); +void process_signal_wakeup (process* p); + +int send_signal (process* target, int signum); +void deliver_pending_signals (registers_t* registers); void schedule (registers_t* registers); diff --git a/kernel/include/kernel/signal.h b/kernel/include/kernel/signal.h new file mode 100644 index 0000000..624ab5b --- /dev/null +++ b/kernel/include/kernel/signal.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +#define SIGHUP 1 /* hangup */ +#define SIGINT 2 /* interrupt */ +#define SIGQUIT 3 /* quit */ +#define SIGILL 4 /* illegal instruction (not reset when caught) */ +#define SIGTRAP 5 /* trace trap (not reset when caught) */ +#define SIGIOT 6 /* IOT instruction */ +#define SIGABRT 6 /* used by abort, replace SIGIOT in the future */ +#define SIGEMT 7 /* EMT instruction */ +#define SIGFPE 8 /* floating point exception */ +#define SIGKILL 9 /* kill (cannot be caught or ignored) */ +#define SIGBUS 10 /* bus error */ +#define SIGSEGV 11 /* segmentation violation */ +#define SIGSYS 12 /* bad argument to system call */ +#define SIGPIPE 13 /* write on a pipe with no one to read it */ +#define SIGALRM 14 /* alarm clock */ +#define SIGTERM 15 /* software termination signal from kill */ +#define SIGURG 16 /* urgent condition on IO channel */ +#define SIGSTOP 17 /* sendable stop signal not from tty */ +#define SIGTSTP 18 /* stop signal from tty */ +#define SIGCONT 19 /* continue a stopped process */ +#define SIGCHLD 20 /* to parent on child stop or exit */ +#define SIGCLD 20 /* System V name for SIGCHLD */ +#define SIGTTIN 21 /* to readers pgrp upon background tty read */ +#define SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */ +#define SIGIO 23 /* input/output possible signal */ +#define SIGXCPU 24 /* exceeded CPU time limit */ +#define SIGXFSZ 25 /* exceeded file size limit */ +#define SIGVTALRM 26 /* virtual time alarm */ +#define SIGPROF 27 /* profiling time alarm */ +#define SIGWINCH 28 /* window changed */ +#define SIGLOST 29 /* resource lost (eg, record-lock lost) */ +#define SIGUSR1 30 /* user defined signal 1 */ +#define SIGUSR2 31 /* user defined signal 2 */ +#define NSIG 32 /* signal 0 implied */ + +#define SIG_DFL ((_sig_func_ptr)0) /* Default action */ +#define SIG_IGN ((_sig_func_ptr)1) /* Ignore action */ +#define SIG_ERR ((_sig_func_ptr) - 1) /* Error return */ + +extern uint8_t signal_trampoline_start[]; +extern uint8_t signal_trampoline_end[]; + +typedef unsigned long sigset_t; +typedef void (*_sig_func_ptr) (int); + +typedef struct { + _sig_func_ptr sa_handler; + sigset_t sa_mask; + int sa_flags; +} sigaction; + +typedef struct { + uint64_t rax, rbx, rcx, rdx, rbp, rsi, rdi; + uint64_t r8, r9, r10, r11, r12, r13, r14, r15; + uint64_t rip; + uint64_t rflags; + uint64_t rsp; + uint64_t cs, ss; + uint8_t trampoline[8]; + uint64_t saved_sigmask; +} signal_frame_t; diff --git a/kernel/include/kernel/syscall.h b/kernel/include/kernel/syscall.h index a9d7ab8..bdd2cb8 100644 --- a/kernel/include/kernel/syscall.h +++ b/kernel/include/kernel/syscall.h @@ -19,6 +19,8 @@ // The following will have numbers changed +#define SYSCALL_SYS_RT_SIGACTION 239 +#define SYSCALL_SYS_RT_SIGRETURN 240 #define SYSCALL_SYS_IOCTL 241 #define SYSCALL_SYS_GETCWD 242 #define SYSCALL_SYS_CHDIR 243 diff --git a/kernel/src/kernel/fs/chardev/chardev.c b/kernel/src/kernel/fs/chardev/chardev.c index 0c38a76..4dfd9c8 100644 --- a/kernel/src/kernel/fs/chardev/chardev.c +++ b/kernel/src/kernel/fs/chardev/chardev.c @@ -41,8 +41,11 @@ static int stdin_read (inode* node, file* f, void* buffer, size_t size) { bool stdio_buf = con_update_cache_set (); for (size_t i = 0; i < size; i++) { unsigned char c = 255; - while ((c = pop_next_char ()) == 255) + while ((c = pop_next_char ()) == 255) { process_block (&tty1_ptr->i_info.chardev_info->rsrc_wait_queue); + process* current = get_current_process (); + if (current->p_pending & ~current->p_sigmask) return -EINTR; + } if (c == '\x7F') { if (i > 0) { i -= 2; diff --git a/kernel/src/kernel/handlers.c b/kernel/src/kernel/handlers.c index bf6d42c..4efb05c 100644 --- a/kernel/src/kernel/handlers.c +++ b/kernel/src/kernel/handlers.c @@ -2,12 +2,13 @@ #include #include #include +#include void handle_gpf (registers_t* registers) { - kserial_printf ("Triggered GPF on PID #%lld\n", get_current_process ()->p_id); + kserial_printf ("Triggered GPF on PID #%lld. Sending SIGILL.\n", get_current_process ()->p_id); log_registers_to_serial (registers); - for (;;) - ; + send_signal (get_current_process (), SIGILL); + deliver_pending_signals (registers); } void init_handlers (void) { idt_register_handler (0xD, handle_gpf); } \ No newline at end of file diff --git a/kernel/src/kernel/memory/memmgt.c b/kernel/src/kernel/memory/memmgt.c index ebe5ad0..612649a 100644 --- a/kernel/src/kernel/memory/memmgt.c +++ b/kernel/src/kernel/memory/memmgt.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #define ALIGNUP(x, o) ((x + o - 1) & ~(o - 1)) @@ -40,11 +41,12 @@ static void page_fault_handler (registers_t* registers) { uint64_t cr2; __asm__ volatile ("mov %%cr2, %0" : "=r"(cr2)); - kserial_printf ("\nEncountered a page fault!\nFaulting address: 0x%llx\n", cr2); + kserial_printf ("\nEncountered a page fault!\nFaulting address: 0x%llx\nSending SIGSEGV.\n", + cr2); log_registers_to_serial (registers); - for (;;) - ; + send_signal (get_current_process (), SIGSEGV); + deliver_pending_signals (registers); } static uint64_t sys_brk (uint64_t addr, uint64_t arg2, uint64_t arg3) { diff --git a/kernel/src/kernel/process.c b/kernel/src/kernel/process.c index aeb4535..3f701f7 100644 --- a/kernel/src/kernel/process.c +++ b/kernel/src/kernel/process.c @@ -66,12 +66,12 @@ void schedule (registers_t* registers) { enqueue_process (get_ready_queue (), current_process); } - int errno = dequeue_process (get_ready_queue (), &upcoming_process); - - if (upcoming_process == nullptr) - for (;;) - ; - if (errno != 0) return; + do { + dequeue_process (get_ready_queue (), &upcoming_process); + if (upcoming_process == nullptr) + for (;;) + ; + } while (upcoming_process->p_state == TASK_DEAD || upcoming_process->p_state == TASK_STOPPED); current_process = upcoming_process; current_process->p_state = TASK_RUNNING; @@ -229,6 +229,11 @@ int process_fork (process* source_process, process** dest_ptr) { new_process->p_waiting = kmalloc (sizeof (process_queue)); kmemset (new_process->p_waiting, 0, sizeof (process_queue)); + new_process->p_sigmask = source_process->p_sigmask; + kmemcpy (new_process->p_sigactions, source_process->p_sigactions, + sizeof (source_process->p_sigactions)); + new_process->p_pending = 0; + errno = enqueue_process (get_ready_queue (), new_process); if (errno != 0) { free_vpages (new_kstack, STACK_SIZE / PAGE_SIZE); @@ -243,11 +248,35 @@ int process_fork (process* source_process, process** dest_ptr) { void process_block (process_queue* wait_queue) { current_process->p_state = TASK_BLOCKED; + current_process->p_waiting_on_queue = wait_queue; enqueue_process (wait_queue, current_process); do_sched_yield (); + current_process->p_waiting_on_queue = nullptr; } void process_unblock (process* p) { + p->p_state = TASK_READY; + p->p_waiting_on_queue = nullptr; + enqueue_process (&ready_queue, p); +} + +void process_signal_wakeup (process* p) { + if (p->p_waiting_on_queue) { + process_queue* q = p->p_waiting_on_queue; + if (q->head == p) { + q->head = p->next; + if (q->tail == p) q->tail = nullptr; + } else { + process* cur = q->head; + while (cur && cur->next != p) + cur = cur->next; + if (cur) { + cur->next = p->next; + if (q->tail == p) q->tail = cur; + } + } + p->p_waiting_on_queue = nullptr; + } p->p_state = TASK_READY; enqueue_process (&ready_queue, p); } @@ -305,6 +334,9 @@ static int do_waitpid (int64_t pid, exit_status* estatus, uint64_t options) { do { process_block (waitproc->p_waiting); + current = get_current_process (); + if (waitproc->p_state == TASK_DEAD) break; + if (current->p_pending & ~current->p_sigmask) return -EINTR; } while (waitproc->p_state != TASK_DEAD); pid0_exit: @@ -334,6 +366,8 @@ static uint64_t sys_exit (uint64_t status, uint64_t arg2, uint64_t arg3) { current->p_exitstatus.info = status; current->p_exitstatus.reason = 0; + if (current->p_parent) send_signal (current->p_parent, SIGCHLD); + for (int i = 0; i < MAX_FDS; i++) if (current->p_fds[i]) sys_close (i, 0, 0); @@ -364,6 +398,64 @@ static uint64_t sys_sched_yield (uint64_t arg1, uint64_t arg2, uint64_t arg3) { return do_sched_yield (); } +static uint64_t sys_kill (uint64_t pid, uint64_t signum, uint64_t unused) { + (void)unused; + process* target = (process*)hashmap_get (pid_map, pid); + if (!target) return (uint64_t)-ESRCH; + return (uint64_t)send_signal (target, (int)signum); +} + +static uint64_t sys_rt_sigaction (uint64_t signum, uint64_t new_action_ptr, + uint64_t old_action_ptr) { + if (signum < 1 || signum >= NSIG) return (uint64_t)-EINVAL; + if (signum == SIGKILL || signum == SIGSTOP) return (uint64_t)-EINVAL; + + process* p = get_current_process (); + if (!p) return (uint64_t)-ESRCH; + + if (old_action_ptr) + kmemcpy ((void*)old_action_ptr, &p->p_sigactions[signum], sizeof (sigaction)); + + if (new_action_ptr) + kmemcpy (&p->p_sigactions[signum], (void*)new_action_ptr, sizeof (sigaction)); + + return 0; +} + +static uint64_t sys_rt_sigreturn (uint64_t arg1, uint64_t arg2, uint64_t arg3) { + (void)arg1, (void)arg2, (void)arg3; + + registers_t* registers = get_latest_r_frame (); + uintptr_t frame_addr = registers->rsp; + signal_frame_t* frame = (signal_frame_t*)frame_addr; + + process* p = get_current_process (); + p->p_sigmask = frame->saved_sigmask; + + registers->rax = frame->rax; + registers->rbx = frame->rbx; + registers->rcx = frame->rcx; + registers->rdx = frame->rdx; + registers->rbp = frame->rbp; + registers->rsi = frame->rsi; + registers->rdi = frame->rdi; + registers->r8 = frame->r8; + registers->r9 = frame->r9; + registers->r10 = frame->r10; + registers->r11 = frame->r11; + registers->r12 = frame->r12; + registers->r13 = frame->r13; + registers->r14 = frame->r14; + registers->r15 = frame->r15; + registers->rip = frame->rip; + registers->rflags = frame->rflags; + registers->rsp = frame->rsp; + registers->cs = frame->cs; + registers->ss = frame->ss; + + return registers->rax; +} + void init_process (void) { ready_queue.head = ready_queue.tail = nullptr; next_free_pid = 2ll; @@ -375,4 +467,7 @@ void init_process (void) { register_syscall (SYSCALL_SYS_GETPID, sys_getpid); register_syscall (SYSCALL_SCHED_YIELD, sys_sched_yield); register_syscall (SYSCALL_SYS_WAITPID, sys_waitpid); + register_syscall (SYSCALL_SYS_KILL, sys_kill); + register_syscall (SYSCALL_SYS_RT_SIGACTION, sys_rt_sigaction); + register_syscall (SYSCALL_SYS_RT_SIGRETURN, sys_rt_sigreturn); } \ No newline at end of file diff --git a/kernel/src/kernel/signal.c b/kernel/src/kernel/signal.c new file mode 100644 index 0000000..507945e --- /dev/null +++ b/kernel/src/kernel/signal.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include + +constexpr uint64_t core_signals = + (1ULL << SIGSEGV) | (1ULL << SIGILL) | (1ULL << SIGFPE) | (1ULL << SIGBUS) | (1ULL << SIGABRT); + +int send_signal (process* target, int signum) { + if (!target) return -ESRCH; + if (signum < 1 || signum >= NSIG) return -EINVAL; + if (target->p_state == TASK_DEAD) return -ESRCH; + + if (signum == SIGSTOP || signum == SIGTSTP) { + target->p_state = TASK_STOPPED; + if (target == get_current_process ()) schedule (get_latest_r_frame ()); + return 0; + } + + if (signum == SIGCONT) { + if (target->p_state == TASK_STOPPED) { + target->p_state = TASK_READY; + enqueue_process (get_ready_queue (), target); + } + target->p_pending &= ~((1ULL << SIGSTOP) | (1ULL << SIGTSTP)); + return 0; + } + + if (signum == SIGKILL) { + target->p_state = TASK_DEAD; + target->p_exitstatus.raw = SIGKILL; + if (target->p_parent) send_signal (target->p_parent, SIGCHLD); + if (target == get_current_process ()) schedule (get_latest_r_frame ()); + return 0; + } + + if (!target->p_user) { + switch (signum) { + case SIGTERM: + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGPIPE: + case SIGALRM: + case SIGUSR1: + case SIGUSR2: + target->p_state = TASK_DEAD; + if (target->p_parent) send_signal (target->p_parent, SIGCHLD); + if (target == get_current_process ()) schedule (get_latest_r_frame ()); + return 0; + default: + target->p_pending |= (1ULL << signum); + return 0; + } + } + + target->p_pending |= (1ULL << signum); + + if (target->p_state == TASK_BLOCKED) process_signal_wakeup (target); + + return 0; +} + +void deliver_pending_signals (registers_t* registers) { + process* p = get_current_process (); + if (!p || !p->p_user) return; + + uint64_t deliverable = p->p_pending & ~p->p_sigmask; + if (!deliverable) return; + + int signum = __builtin_ctzll (deliverable); + + sigaction* action = &p->p_sigactions[signum]; + + p->p_pending &= ~(1ULL << signum); + + if (action->sa_handler == SIG_IGN) return; + if (action->sa_handler == SIG_DFL) { + if (signum == SIGCHLD || signum == SIGURG || signum == SIGWINCH) return; + p->p_exitstatus.raw = (core_signals >> signum & 1) ? signum | 0x80 : signum; + p->p_state = TASK_DEAD; + + process* blocked = nullptr; + do { + dequeue_process (p->p_waiting, &blocked); + if (blocked) enqueue_process (get_ready_queue (), blocked); + } while (blocked); + + if (p->p_parent && p->p_parent->p_waitforchild == -1) { + p->p_parent->p_state = TASK_READY; + p->p_parent->p_waitforchild = p->p_id; + enqueue_process (get_ready_queue (), p->p_parent); + } else if (p->p_parent) { + send_signal (p->p_parent, SIGCHLD); + } + + schedule (registers); + return; + } + + uintptr_t user_rsp = registers->rsp; + user_rsp -= sizeof (signal_frame_t); + user_rsp &= ~0xFULL; + + signal_frame_t* frame = (signal_frame_t*)user_rsp; + + frame->rax = registers->rax; + frame->rbx = registers->rbx; + frame->rcx = registers->rcx; + frame->rdx = registers->rdx; + frame->rbp = registers->rbp; + frame->rsi = registers->rsi; + frame->rdi = registers->rdi; + frame->r8 = registers->r8; + frame->r9 = registers->r9; + frame->r10 = registers->r10; + frame->r11 = registers->r11; + frame->r12 = registers->r12; + frame->r13 = registers->r13; + frame->r14 = registers->r14; + frame->r15 = registers->r15; + frame->rip = registers->rip; + frame->rflags = registers->rflags; + frame->rsp = registers->rsp; + frame->cs = registers->cs; + frame->ss = registers->ss; + + size_t trampoline_sz = signal_trampoline_end - signal_trampoline_start; + kmemcpy (frame->trampoline, signal_trampoline_start, trampoline_sz); + + frame->saved_sigmask = p->p_sigmask; + p->p_sigmask |= action->sa_mask; + p->p_sigmask |= (1ULL << signum); + + user_rsp -= sizeof (uintptr_t); + *((uintptr_t*)user_rsp) = (uintptr_t)frame->trampoline; + + registers->rip = (uintptr_t)action->sa_handler; + registers->rdi = signum; + registers->rsp = user_rsp; +} diff --git a/kernel/src/kernel/signal_trampoline.s b/kernel/src/kernel/signal_trampoline.s new file mode 100644 index 0000000..6e173da --- /dev/null +++ b/kernel/src/kernel/signal_trampoline.s @@ -0,0 +1,7 @@ +.global signal_trampoline_start +.global signal_trampoline_end + +signal_trampoline_start: + movl $240, %eax + int $0x80 +signal_trampoline_end: diff --git a/kernel/src/kernel/sys/execve.c b/kernel/src/kernel/sys/execve.c index 718de4a..279766b 100644 --- a/kernel/src/kernel/sys/execve.c +++ b/kernel/src/kernel/sys/execve.c @@ -42,6 +42,11 @@ static int do_execve (const char* path, char* const argv[], char* const envp[]) err = load_elf (path_cp, get_current_process (), &entry_point); if (err != 0) goto cleanup_envc_argc_path; + process* p = get_current_process (); + for (int i = 1; i < NSIG; i++) + if (p->p_sigactions[i].sa_handler != SIG_IGN) p->p_sigactions[i].sa_handler = SIG_DFL; + p->p_pending = 0; + vaddr_t us_base_vaddr = {254, 255, 0, 0, 0}; uintptr_t user_stack_base = (uintptr_t)vaddr_t_to_ptr (&us_base_vaddr); size_t stack_pages = 4; diff --git a/kernel/src/kernel/syscall.c b/kernel/src/kernel/syscall.c index 5c04f31..1fa989a 100644 --- a/kernel/src/kernel/syscall.c +++ b/kernel/src/kernel/syscall.c @@ -24,6 +24,8 @@ void syscall_handler (registers_t* registers) { } if (current) current->p_registers_ptr = prev_regs; + + deliver_pending_signals (registers); } uint64_t do_syscall (uint64_t syscall, uint64_t arg1, uint64_t arg2, uint64_t arg3) { diff --git a/lib/cos/include/arch/x86_64-cos/syscalls.h b/lib/cos/include/arch/x86_64-cos/syscalls.h index 4c96c3c..45e2c4f 100644 --- a/lib/cos/include/arch/x86_64-cos/syscalls.h +++ b/lib/cos/include/arch/x86_64-cos/syscalls.h @@ -16,6 +16,8 @@ #define SYSCALL_SYS_FORK 57 #define SYSCALL_SCHED_YIELD 158 +#define SYSCALL_SYS_RT_SIGACTION 239 +#define SYSCALL_SYS_RT_SIGRETURN 240 #define SYSCALL_SYS_IOCTL 241 #define SYSCALL_SYS_GETCWD 242 #define SYSCALL_SYS_CHDIR 243 diff --git a/lib/cos/src/kill.c b/lib/cos/src/kill.c new file mode 100644 index 0000000..e773ccd --- /dev/null +++ b/lib/cos/src/kill.c @@ -0,0 +1,6 @@ +#include +#include + +int kill (pid_t pid, int signal) { + return (int)syscall_ret ((long)syscall3 (SYSCALL_SYS_KILL, (long)pid, (long)signal, 0)); +} \ No newline at end of file diff --git a/lib/cos/src/sigaction.c b/lib/cos/src/sigaction.c new file mode 100644 index 0000000..bab196b --- /dev/null +++ b/lib/cos/src/sigaction.c @@ -0,0 +1,7 @@ +#include +#include + +int sigaction (int signum, const struct sigaction* act, struct sigaction* oldact) { + return (int)syscall_ret ( + (long)syscall3 (SYSCALL_SYS_RT_SIGACTION, (long)signum, (long)act, (long)oldact)); +} \ No newline at end of file diff --git a/lib/cos/src/sigprocmask.c b/lib/cos/src/sigprocmask.c new file mode 100644 index 0000000..8d782b7 --- /dev/null +++ b/lib/cos/src/sigprocmask.c @@ -0,0 +1,8 @@ +#include +#include + +int sigprocmask (int how, const sigset_t* set, sigset_t* oldset) { + (void)how, (void)set, (void)oldset; + errno = ENOSYS; + return -1; +} \ No newline at end of file diff --git a/lib/cos/src/stubs.c b/lib/cos/src/stubs.c index 30ba2e1..4bee5f7 100644 --- a/lib/cos/src/stubs.c +++ b/lib/cos/src/stubs.c @@ -9,11 +9,6 @@ extern int errno; // declaration int fcntl (int fd, int cmd, ...); -int kill (int pid, int sig) { - errno = EINVAL; - return -1; -} - int fcntl (int fd, int cmd, ...) { (void)fd; (void)cmd; diff --git a/user/cosh/src/dispatch.c b/user/cosh/src/dispatch.c index 450f417..f52bd8e 100644 --- a/user/cosh/src/dispatch.c +++ b/user/cosh/src/dispatch.c @@ -3,11 +3,27 @@ #include #include #include +#include #include #include +#ifndef WCOREDUMP +#define WCOREDUMP(s) ((s) & 0x80) +#endif + extern char** environ; +static void print_signal_status (int status) { + if (!WIFSIGNALED (status)) return; + fprintf (stderr, "%s%s\n", strsignal (WTERMSIG (status)), + WCOREDUMP (status) ? " (core dumped)" : ""); +} + +static int exit_status_from_wait (int status) { + if (WIFSIGNALED (status)) return 128 + WTERMSIG (status); + return WEXITSTATUS (status); +} + static int exec_and_wait (const char* path, char** argv) { pid_t pid = fork (); if (pid < 0) { @@ -20,7 +36,8 @@ static int exec_and_wait (const char* path, char** argv) { } int status = 0; waitpid (pid, &status, 0); - return WEXITSTATUS (status); + print_signal_status (status); + return exit_status_from_wait (status); } static int try_exec_in_path (char** argv) { @@ -43,9 +60,10 @@ static int try_exec_in_path (char** argv) { waitpid (pid, &status, 0); free (buf); - if (WEXITSTATUS (status) != 127) { + if (exit_status_from_wait (status) != 127) { free (path_copy); - return WEXITSTATUS (status); + print_signal_status (status); + return exit_status_from_wait (status); } entry = strtok (NULL, ":"); }