fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / sh / kernel / signal.c
index b475c4d..379c88b 100644 (file)
@@ -8,7 +8,6 @@
  *  SuperH version:  Copyright (C) 1999, 2000  Niibe Yutaka & Kaz Kojima
  *
  */
-
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/unistd.h>
 #include <linux/stddef.h>
 #include <linux/tty.h>
+#include <linux/elf.h>
 #include <linux/personality.h>
 #include <linux/binfmts.h>
+#include <linux/freezer.h>
 
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/cacheflush.h>
 
-#define DEBUG_SIG 0
-
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
-
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
 asmlinkage int
 sys_sigsuspend(old_sigset_t mask,
               unsigned long r5, unsigned long r6, unsigned long r7,
-              struct pt_regs regs)
+              struct pt_regs __regs)
 {
-       sigset_t saveset;
-
        mask &= _BLOCKABLE;
        spin_lock_irq(&current->sighand->siglock);
-       saveset = current->blocked;
+       current->saved_sigmask = current->blocked;
        siginitset(&current->blocked, mask);
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       regs.regs[0] = -EINTR;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (do_signal(&regs, &saveset))
-                       return -EINTR;
-       }
+       current->state = TASK_INTERRUPTIBLE;
+       schedule();
+       set_thread_flag(TIF_RESTORE_SIGMASK);
+       return -ERESTARTNOHAND;
 }
 
 asmlinkage int
-sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize,
-                 unsigned long r6, unsigned long r7,
-                 struct pt_regs regs)
-{
-       sigset_t saveset, newset;
-
-       /* XXX: Don't preclude handling different sized sigset_t's.  */
-       if (sigsetsize != sizeof(sigset_t))
-               return -EINVAL;
-
-       if (copy_from_user(&newset, unewset, sizeof(newset)))
-               return -EFAULT;
-       sigdelsetmask(&newset, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       saveset = current->blocked;
-       current->blocked = newset;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-
-       regs.regs[0] = -EINTR;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (do_signal(&regs, &saveset))
-                       return -EINTR;
-       }
-}
-
-asmlinkage int 
 sys_sigaction(int sig, const struct old_sigaction __user *act,
              struct old_sigaction __user *oact)
 {
@@ -125,9 +88,11 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
 asmlinkage int
 sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
                unsigned long r6, unsigned long r7,
-               struct pt_regs regs)
+               struct pt_regs __regs)
 {
-       return do_sigaltstack(uss, uoss, regs.regs[15]);
+       struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
+
+       return do_sigaltstack(uss, uoss, regs->regs[15]);
 }
 
 
@@ -136,7 +101,11 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
  */
 
 #define MOVW(n)         (0x9300|((n)-2))       /* Move mem word at PC+n to R3 */
-#define TRAP16  0xc310                 /* Syscall w/no args (NR in R3) */
+#if defined(CONFIG_CPU_SH2)
+#define TRAP_NOARG 0xc320              /* Syscall w/no args (NR in R3) */
+#else
+#define TRAP_NOARG 0xc310              /* Syscall w/no args (NR in R3) */
+#endif
 #define OR_R0_R0 0x200b                        /* or r0,r0 (insert to avoid hardware bug) */
 
 struct sigframe
@@ -232,9 +201,10 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *r0_p
 
 asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
                             unsigned long r6, unsigned long r7,
-                            struct pt_regs regs)
+                            struct pt_regs __regs)
 {
-       struct sigframe __user *frame = (struct sigframe __user *)regs.regs[15];
+       struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
+       struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15];
        sigset_t set;
        int r0;
 
@@ -254,7 +224,7 @@ asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       if (restore_sigcontext(&regs, &frame->sc, &r0))
+       if (restore_sigcontext(regs, &frame->sc, &r0))
                goto badframe;
        return r0;
 
@@ -265,9 +235,10 @@ badframe:
 
 asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
                                unsigned long r6, unsigned long r7,
-                               struct pt_regs regs)
+                               struct pt_regs __regs)
 {
-       struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs.regs[15];
+       struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
+       struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15];
        sigset_t set;
        stack_t st;
        int r0;
@@ -284,14 +255,14 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       if (restore_sigcontext(&regs, &frame->uc.uc_mcontext, &r0))
+       if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0))
                goto badframe;
 
        if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
                goto badframe;
        /* It is more difficult to avoid calling this function than to
           call it and ignore errors.  */
-       do_sigaltstack(&st, NULL, regs.regs[15]);
+       do_sigaltstack(&st, NULL, regs->regs[15]);
 
        return r0;
 
@@ -348,7 +319,12 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
        return (void __user *)((sp - frame_size) & -8ul);
 }
 
-static void setup_frame(int sig, struct k_sigaction *ka,
+/* These symbols are defined with the addresses in the vsyscall page.
+   See vsyscall-trapa.S.  */
+extern void __user __kernel_sigreturn;
+extern void __user __kernel_rt_sigreturn;
+
+static int setup_frame(int sig, struct k_sigaction *ka,
                        sigset_t *set, struct pt_regs *regs)
 {
        struct sigframe __user *frame;
@@ -368,19 +344,22 @@ static void setup_frame(int sig, struct k_sigaction *ka,
 
        err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
 
-       if (_NSIG_WORDS > 1) {
+       if (_NSIG_WORDS > 1)
                err |= __copy_to_user(frame->extramask, &set->sig[1],
                                      sizeof(frame->extramask));
-       }
 
        /* Set up to return from userspace.  If provided, use a stub
           already in userspace.  */
        if (ka->sa.sa_flags & SA_RESTORER) {
                regs->pr = (unsigned long) ka->sa.sa_restorer;
+#ifdef CONFIG_VSYSCALL
+       } else if (likely(current->mm->context.vdso)) {
+               regs->pr = VDSO_SYM(&__kernel_sigreturn);
+#endif
        } else {
                /* Generate return code (system call to sigreturn) */
                err |= __put_user(MOVW(7), &frame->retcode[0]);
-               err |= __put_user(TRAP16, &frame->retcode[1]);
+               err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
                err |= __put_user(OR_R0_R0, &frame->retcode[2]);
                err |= __put_user(OR_R0_R0, &frame->retcode[3]);
                err |= __put_user(OR_R0_R0, &frame->retcode[4]);
@@ -402,21 +381,22 @@ static void setup_frame(int sig, struct k_sigaction *ka,
 
        set_fs(USER_DS);
 
-#if DEBUG_SIG
-       printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
-               current->comm, current->pid, frame, regs->pc, regs->pr);
-#endif
+       pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
+                current->comm, current->pid, frame, regs->pc, regs->pr);
 
        flush_cache_sigtramp(regs->pr);
+
        if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode))
                flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES);
-       return;
+
+       return 0;
 
 give_sigsegv:
        force_sigsegv(sig, current);
+       return -EFAULT;
 }
 
-static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                           sigset_t *set, struct pt_regs *regs)
 {
        struct rt_sigframe __user *frame;
@@ -452,10 +432,14 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
           already in userspace.  */
        if (ka->sa.sa_flags & SA_RESTORER) {
                regs->pr = (unsigned long) ka->sa.sa_restorer;
+#ifdef CONFIG_VSYSCALL
+       } else if (likely(current->mm->context.vdso)) {
+               regs->pr = VDSO_SYM(&__kernel_rt_sigreturn);
+#endif
        } else {
                /* Generate return code (system call to rt_sigreturn) */
                err |= __put_user(MOVW(7), &frame->retcode[0]);
-               err |= __put_user(TRAP16, &frame->retcode[1]);
+               err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
                err |= __put_user(OR_R0_R0, &frame->retcode[2]);
                err |= __put_user(OR_R0_R0, &frame->retcode[3]);
                err |= __put_user(OR_R0_R0, &frame->retcode[4]);
@@ -477,28 +461,31 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
 
        set_fs(USER_DS);
 
-#if DEBUG_SIG
-       printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
-               current->comm, current->pid, frame, regs->pc, regs->pr);
-#endif
+       pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
+                current->comm, current->pid, frame, regs->pc, regs->pr);
 
        flush_cache_sigtramp(regs->pr);
+
        if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode))
                flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES);
-       return;
+
+       return 0;
 
 give_sigsegv:
        force_sigsegv(sig, current);
+       return -EFAULT;
 }
 
 /*
  * OK, we're invoking a handler
  */
 
-static void
+static int
 handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
              sigset_t *oldset, struct pt_regs *regs)
 {
+       int ret;
+
        /* Are we from a system call? */
        if (regs->tra >= 0) {
                /* If so, check system call restarting.. */
@@ -539,19 +526,23 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
 
        /* Set up the stack frame */
        if (ka->sa.sa_flags & SA_SIGINFO)
-               setup_rt_frame(sig, ka, info, oldset, regs);
+               ret = setup_rt_frame(sig, ka, info, oldset, regs);
        else
-               setup_frame(sig, ka, oldset, regs);
+               ret = setup_frame(sig, ka, oldset, regs);
 
        if (ka->sa.sa_flags & SA_ONESHOT)
                ka->sa.sa_handler = SIG_DFL;
 
-       spin_lock_irq(&current->sighand->siglock);
-       sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
-       if (!(ka->sa.sa_flags & SA_NODEFER))
-               sigaddset(&current->blocked,sig);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       if (ret == 0) {
+               spin_lock_irq(&current->sighand->siglock);
+               sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+               if (!(ka->sa.sa_flags & SA_NODEFER))
+                       sigaddset(&current->blocked,sig);
+               recalc_sigpending();
+               spin_unlock_irq(&current->sighand->siglock);
+       }
+
+       return ret;
 }
 
 /*
@@ -563,11 +554,12 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-int do_signal(struct pt_regs *regs, sigset_t *oldset)
+static void do_signal(struct pt_regs *regs, unsigned int save_r0)
 {
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
+       sigset_t *oldset;
 
        /*
         * We want the common case to go fast, which
@@ -576,19 +568,27 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
         * if so.
         */
        if (!user_mode(regs))
-               return 1;
+               return;
 
        if (try_to_freeze())
                goto no_signal;
 
-       if (!oldset)
+       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+               oldset = &current->saved_sigmask;
+       else
                oldset = &current->blocked;
 
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               handle_signal(signr, &ka, &info, oldset, regs);
-               return 1;
+               if (handle_signal(signr, &ka, &info, oldset, regs) == 0) {
+                       /* a signal was successfully delivered; the saved
+                        * sigmask will have been stored in the signal frame,
+                        * and will be restored by sigreturn, so we can simply
+                        * clear the TIF_RESTORE_SIGMASK flag */
+                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+                               clear_thread_flag(TIF_RESTORE_SIGMASK);
+               }
        }
 
  no_signal:
@@ -597,10 +597,27 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
                /* Restart the system call - no handlers present */
                if (regs->regs[0] == -ERESTARTNOHAND ||
                    regs->regs[0] == -ERESTARTSYS ||
-                   regs->regs[0] == -ERESTARTNOINTR ||
-                   regs->regs[0] == -ERESTART_RESTARTBLOCK) {
+                   regs->regs[0] == -ERESTARTNOINTR) {
+                       regs->regs[0] = save_r0;
+                       regs->pc -= 2;
+               } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) {
                        regs->pc -= 2;
+                       regs->regs[3] = __NR_restart_syscall;
                }
        }
-       return 0;
+
+       /* if there's no signal to deliver, we just put the saved sigmask
+        * back */
+       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+               clear_thread_flag(TIF_RESTORE_SIGMASK);
+               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+       }
+}
+
+asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0,
+                                __u32 thread_info_flags)
+{
+       /* deal with pending signal delivery */
+       if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+               do_signal(regs, save_r0);
 }