diff --git a/kernel/src/api/Syscall.h b/kernel/src/api/Syscall.h index f6fd7960..995af42a 100644 --- a/kernel/src/api/Syscall.h +++ b/kernel/src/api/Syscall.h @@ -9,7 +9,7 @@ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ _e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \ _e(truncate) _e(ftruncate) _e(utimensat) _e(setitimer) _e(pledge) _e(memstat) \ - _e(setsid) _e(getsid) _e(getgroups) _e(setgroups) + _e(setsid) _e(getsid) _e(getgroups) _e(setgroups) _e(pause) _e(sigsuspend) enum Syscalls { diff --git a/kernel/src/arch/x86_64/CPU.asm b/kernel/src/arch/x86_64/CPU.asm index b8b433d2..20f8d5ac 100644 --- a/kernel/src/arch/x86_64/CPU.asm +++ b/kernel/src/arch/x86_64/CPU.asm @@ -50,19 +50,22 @@ load_tr: extern switch_task +; Create a new artificial stack frame simulating an IRQ, saving all the callee-saved registers and triggering a task switch. +; As we know this function was called directly using the SysV ABI, instead of arbitrarily interrupting any function at any point, +; we can trash some of the caller-saved registers to save state (in this case RDI and RCX). global kernel_yield kernel_yield: - mov rdi, [rsp] ; return address is now in RDI - mov rcx, rsp ; save current RSP + mov rdi, [rsp] ; return address is now in RDI (caller-saved) + mov rcx, rsp ; save current RSP in RCX (caller-saved) add rcx, 8 ; skip over the return address mov eax, ss push rax ; SS - push rcx ; RSP + push rcx ; RSP (saved in RCX) pushfq ; RFLAGS mov eax, cs push rax ; CS - push rdi ; RIP + push rdi ; RIP (return address previously on stack, saved to RDI) sub rsp, 24 diff --git a/kernel/src/sys/signal.cpp b/kernel/src/sys/signal.cpp index 2d2d05c9..c1034411 100644 --- a/kernel/src/sys/signal.cpp +++ b/kernel/src/sys/signal.cpp @@ -148,3 +148,49 @@ Result sys_sigprocmask(Registers*, SyscallArgs args) return 0; } + +Result sys_pause(Registers* regs, SyscallArgs) +{ + auto* current = Scheduler::current(); + while (1) + { + kernel_wait_for_event(); + if (current->interrupted) + { + if (current->will_ignore_pending_signal()) + { + current->process_pending_signals(regs); + continue; + } + return err(EINTR); + } + } +} + +Result sys_sigsuspend(Registers* regs, SyscallArgs args) +{ + const sigset_t* set = (const sigset_t*)args[0]; + + auto* current = Scheduler::current(); + + sigset_t kset; + if (!MemoryManager::copy_from_user_typed(set, &kset)) return err(EFAULT); + + sigset_t oldset = current->signal_mask.value(); + current->signal_mask = kset; + + while (1) + { + kernel_wait_for_event(); + if (current->interrupted) + { + if (current->will_ignore_pending_signal()) + { + current->process_pending_signals(regs); + continue; + } + current->signal_mask = oldset; + return err(EINTR); + } + } +} diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index a10218f9..f572e35c 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -193,7 +193,7 @@ void Thread::process_pending_signals(Registers* current_regs) } } // If we fail to deliver the signal (usually because there's not enough space on the stack), execute the - // default action. + // default action. FIXME: Should this be changed? if (!deliver_signal(signo, current_regs)) goto default_signal; return; } diff --git a/libc/include/signal.h b/libc/include/signal.h index 814ede80..17e76706 100644 --- a/libc/include/signal.h +++ b/libc/include/signal.h @@ -48,6 +48,9 @@ extern "C" /* Check if a signal is in set.*/ int sigismember(const sigset_t* set, int signo); + /* Change the signal mask temporarily and wait for a signal to arrive. */ + int sigsuspend(const sigset_t* set); + #ifdef __cplusplus } #endif diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 74c5ff64..d1996b18 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -208,6 +208,9 @@ extern "C" /* Get this process's list of supplementary groups. */ int getgroups(int size, gid_t* list); + /* Wait for a signal to arrive. */ + int pause(void); + #ifdef __cplusplus } #endif diff --git a/libc/src/signal.cpp b/libc/src/signal.cpp index 64849364..84bc8737 100644 --- a/libc/src/signal.cpp +++ b/libc/src/signal.cpp @@ -88,4 +88,10 @@ extern "C" return (*set & (1 << (signo - 1))) > 0; } + + int sigsuspend(const sigset_t* set) + { + long rc = syscall(SYS_sigsuspend, set); + __errno_return(rc, int); + } } diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index 69ae625c..4eec7969 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -551,4 +551,10 @@ extern "C" long rc = syscall(SYS_getgroups, size, list); __errno_return(rc, int); } + + int pause() + { + long rc = syscall(SYS_pause); + __errno_return(rc, int); + } }