This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / arch / um / kernel / signal_kern.c
index fdebc09..23aefcb 100644 (file)
@@ -38,55 +38,53 @@ EXPORT_SYMBOL(unblock_signals);
 /*
  * OK, we're invoking a handler
  */    
-static int handle_signal(struct pt_regs *regs, unsigned long signr,
-                        struct k_sigaction *ka, siginfo_t *info,
-                        sigset_t *oldset)
+static int handle_signal(struct pt_regs *regs, unsigned long signr, 
+                        struct k_sigaction *ka, siginfo_t *info, 
+                        sigset_t *oldset, int error)
 {
+        __sighandler_t handler;
+       void (*restorer)(void);
        unsigned long sp;
-       int err;
+       sigset_t save;
+       int err, ret;
 
+       ret = 0;
        /* Always make any pending restarted system calls return -EINTR */
        current_thread_info()->restart_block.fn = do_no_restart_syscall;
-
-       /* Did we come from a system call? */
-       if(PT_REGS_SYSCALL_NR(regs) >= 0){
-               /* If so, check system call restarting.. */
-               switch(PT_REGS_SYSCALL_RET(regs)){
-               case -ERESTART_RESTARTBLOCK:
-               case -ERESTARTNOHAND:
-                       PT_REGS_SYSCALL_RET(regs) = -EINTR;
-                       break;
-
-               case -ERESTARTSYS:
-                       if (!(ka->sa.sa_flags & SA_RESTART)) {
-                               PT_REGS_SYSCALL_RET(regs) = -EINTR;
-                               break;
-                       }
-               /* fallthrough */
-               case -ERESTARTNOINTR:
-                       PT_REGS_RESTART_SYSCALL(regs);
-                       PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
+       switch(error){
+       case -ERESTART_RESTARTBLOCK:
+       case -ERESTARTNOHAND:
+               ret = -EINTR;
+               break;
+
+       case -ERESTARTSYS:
+               if (!(ka->sa.sa_flags & SA_RESTART)) {
+                       ret = -EINTR;
                        break;
                }
+               /* fallthrough */
+       case -ERESTARTNOINTR:
+               PT_REGS_RESTART_SYSCALL(regs);
+               PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
+
+               /* This is because of the UM_SET_SYSCALL_RETURN and the fact
+                * that on i386 the system call number and return value are
+                * in the same register.  When the system call restarts, %eax
+                * had better have the system call number in it.  Since the
+                * return value doesn't matter (except that it shouldn't be
+                * -ERESTART*), we'll stick the system call number there.
+                */
+               ret = PT_REGS_SYSCALL_NR(regs);
+               break;
        }
 
-       sp = PT_REGS_SP(regs);
-       if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
-               sp = current->sas_ss_sp + current->sas_ss_size;
+       handler = ka->sa.sa_handler;
+       save = *oldset;
 
-       if(ka->sa.sa_flags & SA_SIGINFO)
-               err = setup_signal_stack_si(sp, signr, ka, regs, info, oldset);
-       else
-               err = setup_signal_stack_sc(sp, signr, ka, regs, oldset);
+       if (ka->sa.sa_flags & SA_ONESHOT)
+               ka->sa.sa_handler = SIG_DFL;
 
-       if(err){
-               spin_lock_irq(&current->sighand->siglock);
-               current->blocked = *oldset;
-               recalc_sigpending();
-               spin_unlock_irq(&current->sighand->siglock);
-               force_sigsegv(signr, current);
-       }
-       else if(!(ka->sa.sa_flags & SA_NODEFER)){
+       if (!(ka->sa.sa_flags & SA_NODEFER)) {
                spin_lock_irq(&current->sighand->siglock);
                sigorsets(&current->blocked, &current->blocked, 
                          &ka->sa.sa_mask);
@@ -95,24 +93,50 @@ static int handle_signal(struct pt_regs *regs, unsigned long signr,
                spin_unlock_irq(&current->sighand->siglock);
        }
 
-       return err;
+       sp = PT_REGS_SP(regs);
+
+       if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
+               sp = current->sas_ss_sp + current->sas_ss_size;
+       
+       if(error != 0) PT_REGS_SET_SYSCALL_RETURN(regs, ret);
+
+       if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer;
+       else restorer = NULL;
+
+       if(ka->sa.sa_flags & SA_SIGINFO)
+               err = setup_signal_stack_si(sp, signr, (unsigned long) handler,
+                                           restorer, regs, info, &save);
+       else
+               err = setup_signal_stack_sc(sp, signr, (unsigned long) handler,
+                                           restorer, regs, &save);
+       if(err) goto segv;
+
+       return(0);
+ segv:
+       force_sigsegv(signr, current);
+       return(1);
 }
 
-static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset)
+static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error)
 {
        struct k_sigaction ka_copy;
        siginfo_t info;
-       int sig, handled_sig = 0;
+       int err, sig;
 
-       while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){
-               handled_sig = 1;
-               /* Whee!  Actually deliver the signal.  */
-               if(!handle_signal(regs, sig, &ka_copy, &info, oldset))
-                       break;
-       }
+       if (!oldset)
+               oldset = &current->blocked;
+
+       sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL);
+       if(sig == 0)
+               return(0);
+
+       /* Whee!  Actually deliver the signal.  */
+       err = handle_signal(regs, sig, &ka_copy, &info, oldset, error);
+       if(!err)
+               return(1);
 
        /* Did we come from a system call? */
-       if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){
+       if(PT_REGS_SYSCALL_NR(regs) >= 0){
                /* Restart the system call - no handlers present */
                if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND ||
                   PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS ||
@@ -133,21 +157,21 @@ static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset)
         * on the host.  The tracing thread will check this flag and 
         * PTRACE_SYSCALL if necessary.
         */
-       if(current->ptrace & PT_DTRACE)
-               current->thread.singlestep_syscall =
-                       is_syscall(PT_REGS_IP(&current->thread.regs));
-       return(handled_sig);
+       if((current->ptrace & PT_DTRACE) && 
+          is_syscall(PT_REGS_IP(&current->thread.regs)))
+               (void) CHOOSE_MODE(current->thread.mode.tt.singlestep_syscall = 1, 0);
+       return(0);
 }
 
-int do_signal(void)
+int do_signal(int error)
 {
-       return(kern_do_signal(&current->thread.regs, &current->blocked));
+       return(kern_do_signal(&current->thread.regs, NULL, error));
 }
 
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
-long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
+int sys_sigsuspend(int history0, int history1, old_sigset_t mask)
 {
        sigset_t saveset;
 
@@ -158,16 +182,15 @@ long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       PT_REGS_SYSCALL_RET(&current->thread.regs) = -EINTR;
        while (1) {
                current->state = TASK_INTERRUPTIBLE;
                schedule();
-               if(kern_do_signal(&current->thread.regs, &saveset))
+               if(kern_do_signal(&current->thread.regs, &saveset, -EINTR))
                        return(-EINTR);
        }
 }
 
-long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
+int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
 {
        sigset_t saveset, newset;
 
@@ -185,11 +208,10 @@ long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       PT_REGS_SYSCALL_RET(&current->thread.regs) = -EINTR;
        while (1) {
                current->state = TASK_INTERRUPTIBLE;
                schedule();
-               if (kern_do_signal(&current->thread.regs, &saveset))
+               if (kern_do_signal(&current->thread.regs, &saveset, -EINTR))
                        return(-EINTR);
        }
 }
@@ -225,7 +247,7 @@ int sys_sigaction(int sig, const struct old_sigaction __user *act,
        return ret;
 }
 
-long sys_sigaltstack(const stack_t *uss, stack_t *uoss)
+int sys_sigaltstack(const stack_t *uss, stack_t *uoss)
 {
        return(do_sigaltstack(uss, uoss, PT_REGS_SP(&current->thread.regs)));
 }
@@ -243,7 +265,7 @@ static int copy_sc_from_user(struct pt_regs *to, void *from,
        return(ret);
 }
 
-long sys_sigreturn(struct pt_regs regs)
+int sys_sigreturn(struct pt_regs regs)
 {
        void __user *sc = sp_to_sc(PT_REGS_SP(&current->thread.regs));
        void __user *mask = sp_to_mask(PT_REGS_SP(&current->thread.regs));
@@ -261,7 +283,7 @@ long sys_sigreturn(struct pt_regs regs)
        return(PT_REGS_SYSCALL_RET(&current->thread.regs));
 }
 
-long sys_rt_sigreturn(struct pt_regs regs)
+int sys_rt_sigreturn(struct pt_regs regs)
 {
        unsigned long sp = PT_REGS_SP(&current->thread.regs);
        struct ucontext __user *uc = sp_to_uc(sp);