Decoupling Architectural-specific Code (#35)
[lunaix-os.git] / lunaix-os / kernel / process / signal.c
index 8b66663a676faa743390e7be02aa2f359c2c65a6..e40526ee9bb6fd2583a286e2645ffadfefcc71a3 100644 (file)
@@ -5,6 +5,7 @@
 #include <lunaix/status.h>
 #include <lunaix/syscall.h>
 #include <lunaix/syslog.h>
+#include <lunaix/mm/valloc.h>
 
 #include <klibc/string.h>
 
@@ -14,43 +15,47 @@ LOG_MODULE("SIG")
 
 extern struct scheduler sched_ctx; /* kernel/sched.c */
 
-#define UNMASKABLE (sigset(SIGKILL) | sigset(SIGTERM))
+#define UNMASKABLE (sigset(SIGKILL) | sigset(SIGTERM) | sigset(SIGILL))
 #define TERMSIG (sigset(SIGSEGV) | sigset(SIGINT) | UNMASKABLE)
 #define CORE (sigset(SIGSEGV))
 #define within_kstack(addr)                                                    \
-    (KERNEL_STACK <= (addr) && (addr) <= KERNEL_STACK_END)
+    (KSTACK_AREA <= (addr) && (addr) <= KSTACK_AREA_END)
 
 static inline void
-signal_terminate(int errcode)
+signal_terminate(int caused_by)
 {
-    terminate_proc(errcode | PEXITSIG);
+    terminate_current(caused_by | PEXITSIG);
 }
 
 // Referenced in kernel/asm/x86/interrupt.S
 void*
 signal_dispatch()
 {
-    if (!__current->sigctx.sig_pending) {
+    if (kernel_process(__current)) {
+        // signal is undefined under 'kernel process'
+        return 0;
+    }
+
+    if (!pending_sigs(current_thread)) {
         // 没有待处理信号
         return 0;
     }
 
-    struct sighail* psig = &__current->sigctx;
-    struct sigact* prev_working = psig->inprogress;
+    struct sigregistry* sigreg = __current->sigreg;
+    struct sigctx* psig = &current_thread->sigctx;
+    struct sigact* prev_working = active_signal(current_thread);
     sigset_t mask = psig->sig_mask | (prev_working ? prev_working->sa_mask : 0);
 
     int sig_selected = 31 - clz(psig->sig_pending & ~mask);
-
     sigset_clear(psig->sig_pending, sig_selected);
 
-    struct sigact* action = &psig->signals[sig_selected];
-
-    if (sig_selected == 0) {
+    if (!sig_selected) {
         // SIG0 is reserved
         return 0;
     }
 
-    if (!action->sa_actor) {
+    struct sigact* action = sigreg->signals[sig_selected];
+    if (!action || !action->sa_actor) {
         if (sigset_test(TERMSIG, sig_selected)) {
             signal_terminate(sig_selected);
             schedule();
@@ -59,9 +64,9 @@ signal_dispatch()
         return 0;
     }
 
-    ptr_t ustack = __current->ustack_top;
-
-    if ((int)(ustack - USR_STACK) < (int)sizeof(struct proc_sig)) {
+    ptr_t ustack = current_thread->ustack_top;
+    ptr_t ustack_start = current_thread->ustack->start;
+    if ((int)(ustack - ustack_start) < (int)sizeof(struct proc_sig)) {
         // 用户栈没有空间存放信号上下文
         return 0;
     }
@@ -73,32 +78,76 @@ signal_dispatch()
     sigframe->sigact = action->sa_actor;
     sigframe->sighand = action->sa_handler;
 
-    sigframe->saved_ictx = __current->intr_ctx;
+    sigframe->saved_hstate = current_thread->hstate;
 
-    action->prev = prev_working;
-    psig->inprogress = action;
+    sigactive_push(current_thread, sig_selected);
 
     return sigframe;
 }
 
+static inline void must_inline
+__set_signal(struct thread* thread, signum_t signum) 
+{
+    raise_signal(thread, signum);
+
+    // for these mutually exclusive signal
+    if (signum == SIGCONT || signum == SIGSTOP) {
+        sigset_clear(thread->sigctx.sig_pending, signum ^ 1);
+    }
+    
+    struct sigact* sig = sigact_of(thread->process, signum);
+    if (sig) {
+        sig->sender = __current->pid;
+    }
+}
+
+static inline void must_inline
+__set_signal_all_threads(struct proc_info* proc, signum_t signum) 
+{
+    struct thread *pos, *n;
+    llist_for_each(pos, n, &proc->threads, proc_sibs) {
+        __set_signal(pos, signum);
+    }
+}
+
 void
-proc_clear_signal(struct proc_info* proc)
+thread_setsignal(struct thread* thread, signum_t signum)
 {
-    memset(&proc->sigctx, 0, sizeof(proc->sigctx));
+    if (unlikely(kernel_process(thread->process))) {
+        return;
+    }
+
+    __set_signal(thread, signum);
 }
 
 void
-proc_setsignal(struct proc_info* proc, int signum)
+proc_setsignal(struct proc_info* proc, signum_t signum)
 {
-    sigset_add(proc->sigctx.sig_pending, signum);
-    proc->sigctx.signals[signum].sender = __current->pid;
+    if (unlikely(kernel_process(proc))) {
+        return;
+    }
+
+    // FIXME handle signal delivery at process level.
+    switch (signum)
+    {
+    case SIGKILL:
+        signal_terminate(signum);
+        break;
+    case SIGCONT:
+    case SIGSTOP:
+        __set_signal_all_threads(proc, signum);
+    default:
+        break;
+    }
+    
+    __set_signal(proc->th_active, signum);
 }
 
 int
-signal_send(pid_t pid, int signum)
+signal_send(pid_t pid, signum_t signum)
 {
-    if (signum < 0 || signum >= _SIG_NUM) {
-        __current->k_status = EINVAL;
+    if (signum >= _SIG_NUM) {
+        syscall_result(EINVAL);
         return -1;
     }
 
@@ -117,50 +166,115 @@ signal_send(pid_t pid, int signum)
     } else {
         // TODO: send to all process.
         //  But I don't want to support it yet.
-        __current->k_status = EINVAL;
+        syscall_result(EINVAL);
         return -1;
     }
 
-send_grp:
+send_grp: ;
     struct proc_info *pos, *n;
     llist_for_each(pos, n, &proc->grp_member, grp_member)
     {
-        struct sighail* sh = &pos->sigctx;
-        sigset_add(sh->sig_pending, signum);
-        sh->signals[signum].sender = sender_pid;
+        proc_setsignal(pos, signum);
     }
 
 send_single:
     if (proc_terminated(proc)) {
-        __current->k_status = EINVAL;
+        syscall_result(EINVAL);
         return -1;
     }
 
-    sigset_add(proc->sigctx.sig_pending, signum);
-    proc->sigctx.signals[signum].sender = sender_pid;
+    proc_setsignal(proc, signum);
 
     return 0;
 }
 
-__DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
+void
+signal_dup_context(struct sigctx* dest_ctx) 
+{
+    struct sigctx* old_ctx = &current_thread->sigctx;
+    memcpy(dest_ctx, old_ctx, sizeof(struct sigctx));
+}
+
+void
+signal_dup_registry(struct sigregistry* dest_reg)
+{
+    struct sigregistry* oldreg = __current->sigreg;
+    for (int i = 0; i < _SIG_NUM; i++) {
+        struct sigact* oldact = oldreg->signals[i];
+        if (!oldact) {
+            continue;
+        }
+        
+        struct sigact* newact = valloc(sizeof(struct sigact));
+        memcpy(newact, oldact, sizeof(struct sigact));
+
+        dest_reg->signals[i] = newact;
+    }
+}
+
+void
+signal_reset_context(struct sigctx* sigctx) {
+    memset(sigctx, 0, sizeof(struct sigctx));
+}
+
+void
+signal_reset_registry(struct sigregistry* sigreg) {
+    for (int i = 0; i < _SIG_NUM; i++) {
+        struct sigact* act = sigreg->signals[i];
+        if (act) {
+            vfree(act);
+            sigreg->signals[i] = NULL;
+        }
+    }
+}
+
+void
+signal_free_registry(struct sigregistry* sigreg) {
+    signal_reset_registry(sigreg);
+    vfree(sigreg);
+}
+
+static bool
+signal_set_sigmask(struct thread* thread, int how, sigset_t* oldset, sigset_t* set)
 {
-    __current->intr_ctx = sig_ctx->saved_ictx;
+    struct sigctx* sh = &current_thread->sigctx;
+    *oldset = sh->sig_mask;
 
-    struct sigact* current = __current->sigctx.inprogress;
-    if (current) {
-        __current->sigctx.inprogress = current->prev;
-        current->prev = NULL;
+    if (how == _SIG_BLOCK) {
+        sigset_union(sh->sig_mask, *set);
+    } else if (how == _SIG_UNBLOCK) {
+        sigset_intersect(sh->sig_mask, ~(*set));
+    } else if (how == _SIG_SETMASK) {
+        sh->sig_mask = *set;
     } else {
-        __current->sigctx.inprogress = NULL;
+        return false;
     }
 
+    sigset_intersect(sh->sig_mask, ~UNMASKABLE);
+    return true;
+}
+
+__DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
+{
+    struct sigctx* sigctx = &current_thread->sigctx;
+    struct sigact* active = active_signal(current_thread);
+
+    /* We choose signal#0 as our base case, that is sig#0 means no signal.
+        Therefore, it is an ill situation to return from such sigctx.
+    */
+    if (!active) {
+        signal_terminate(SIGSEGV);
+        schedule();
+    }
+
+    current_thread->hstate = sig_ctx->saved_hstate;
     if (proc_terminated(__current)) {
         __current->exit_code |= PEXITSIG;
     } else if (sigset_test(CORE, sig_ctx->sig_num)) {
         signal_terminate(sig_ctx->sig_num);
     }
 
-    ptr_t ictx = (ptr_t)__current->intr_ctx;
+    ptr_t ictx = (ptr_t)current_thread->hstate;
 
     /*
         Ensure our restored context is within kernel stack
@@ -172,6 +286,8 @@ __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
         signal_terminate(SIGSEGV);
     }
 
+    sigactive_pop(current_thread);
+
     schedule();
 
     // never reach!
@@ -181,21 +297,25 @@ __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
 __DEFINE_LXSYSCALL3(
     int, sigprocmask, int, how, const sigset_t, *set, sigset_t, *oldset)
 {
-    struct sighail* sh = &__current->sigctx;
-    *oldset = sh->sig_mask;
+    // TODO maybe it is a good opportunity to introduce a process-wide 
+    //      signal mask?
+    
+    if (signal_set_sigmask(current_thread, how, oldset, set)) {
+        return 0;
+    }
 
-    if (how == _SIG_BLOCK) {
-        sigset_union(sh->sig_mask, *set);
-    } else if (how == _SIG_UNBLOCK) {
-        sigset_intersect(sh->sig_mask, ~(*set));
-    } else if (how == _SIG_SETMASK) {
-        sh->sig_mask = *set;
-    } else {
+    syscall_result(EINVAL);
+    return -1;
+}
+
+__DEFINE_LXSYSCALL3(
+    int, th_sigmask, int, how, const sigset_t, *set, sigset_t, *oldset)
+{
+    if (signal_set_sigmask(current_thread, how, oldset, set)) {
         return 0;
     }
 
-    sigset_intersect(sh->sig_mask, ~UNMASKABLE);
-    return 1;
+    return EINVAL;
 }
 
 __DEFINE_LXSYSCALL2(int, sys_sigaction, int, signum, struct sigaction*, action)
@@ -208,7 +328,17 @@ __DEFINE_LXSYSCALL2(int, sys_sigaction, int, signum, struct sigaction*, action)
         return -1;
     }
 
-    struct sigact* sa = &__current->sigctx.signals[signum];
+    struct sigctx* sigctx = &current_thread->sigctx;
+    if (signum == sigctx->sig_active) {
+        return -1;
+    }
+
+    struct sigact* sa = sigact_of(__current, signum);
+
+    if (!sa) {
+        sa = vzalloc(sizeof(struct sigact));
+        set_sigact(__current, signum, sa);
+    }
 
     sa->sa_actor = (void*)action->sa_sigaction;
     sa->sa_handler = (void*)action->sa_handler;
@@ -219,10 +349,10 @@ __DEFINE_LXSYSCALL2(int, sys_sigaction, int, signum, struct sigaction*, action)
 
 __DEFINE_LXSYSCALL(int, pause)
 {
-    pause_current();
-    sched_yieldk();
+    pause_current_thread();
+    sched_pass();
 
-    __current->k_status = EINTR;
+    syscall_result(EINTR);
     return -1;
 }
 
@@ -233,18 +363,19 @@ __DEFINE_LXSYSCALL2(int, kill, pid_t, pid, int, signum)
 
 __DEFINE_LXSYSCALL1(int, sigpending, sigset_t, *sigset)
 {
-    *sigset = __current->sigctx.sig_pending;
+    *sigset = pending_sigs(current_thread);
     return 0;
 }
 
 __DEFINE_LXSYSCALL1(int, sigsuspend, sigset_t, *mask)
 {
-    sigset_t tmp = __current->sigctx.sig_mask;
-    __current->sigctx.sig_mask = (*mask) & ~UNMASKABLE;
+    struct sigctx* sigctx = &current_thread->sigctx;
+    sigset_t tmp = current_thread->sigctx.sig_mask;
+    sigctx->sig_mask = (*mask) & ~UNMASKABLE;
 
-    pause_current();
-    sched_yieldk();
+    pause_current_thread();
+    sched_pass();
 
-    __current->sigctx.sig_mask = tmp;
+    sigctx->sig_mask = tmp;
     return -1;
 }
\ No newline at end of file