Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[powerpc.git] / arch / powerpc / kernel / signal_32.c
index a7c4515..c6d0595 100644 (file)
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
 #include <asm/sigcontext.h>
+#include <asm/vdso.h>
 #ifdef CONFIG_PPC64
 #include "ppc32.h"
 #include <asm/unistd.h>
-#include <asm/vdso.h>
 #else
 #include <asm/ucontext.h>
 #include <asm/pgtable.h>
@@ -76,7 +76,6 @@
  * registers from *regs.  This is what we need
  * to do when a signal has been delivered.
  */
-#define sigreturn_exit(regs)   return 0
 
 #define GP_REGS_SIZE   min(sizeof(elf_gregset_t32), sizeof(struct pt_regs32))
 #undef __SIGNAL_FRAMESIZE
@@ -156,9 +155,17 @@ static inline int save_general_regs(struct pt_regs *regs,
        elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
        int i;
 
-       for (i = 0; i <= PT_RESULT; i ++)
+       if (!FULL_REGS(regs)) {
+               set_thread_flag(TIF_SAVE_NVGPRS);
+               current_thread_info()->nvgprs_frame = frame->mc_gregs;
+       }
+
+       for (i = 0; i <= PT_RESULT; i ++) {
+               if (i == 14 && !FULL_REGS(regs))
+                       i = 32;
                if (__put_user((unsigned int)gregs[i], &frame->mc_gregs[i]))
                        return -EFAULT;
+       }
        return 0;
 }
 
@@ -179,8 +186,6 @@ static inline int restore_general_regs(struct pt_regs *regs,
 
 #else /* CONFIG_PPC64 */
 
-extern void sigreturn_exit(struct pt_regs *);
-
 #define GP_REGS_SIZE   min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
 
 static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set)
@@ -214,6 +219,15 @@ static inline int get_old_sigaction(struct k_sigaction *new_ka,
 static inline int save_general_regs(struct pt_regs *regs,
                struct mcontext __user *frame)
 {
+       if (!FULL_REGS(regs)) {
+               /* Zero out the unsaved GPRs to avoid information
+                  leak, and set TIF_SAVE_NVGPRS to ensure that the
+                  registers do actually get saved later. */
+               memset(&regs->gpr[14], 0, 18 * sizeof(unsigned long));
+               current_thread_info()->nvgprs_frame = &frame->mc_gregs;
+               set_thread_flag(TIF_SAVE_NVGPRS);
+       }
+
        return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE);
 }
 
@@ -238,63 +252,19 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs);
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
-long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7,
-              struct pt_regs *regs)
+long sys_sigsuspend(old_sigset_t mask)
 {
-       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->result = -EINTR;
-       regs->gpr[3] = EINTR;
-       regs->ccr |= 0x10000000;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (do_signal(&saveset, regs))
-                       sigreturn_exit(regs);
-       }
-}
-
-long sys_rt_sigsuspend(
-#ifdef CONFIG_PPC64
-               compat_sigset_t __user *unewset,
-#else
-               sigset_t __user *unewset,
-#endif
-               size_t sigsetsize, int p3, int p4,
-               int p6, int p7, 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 (get_sigset_t(&newset, unewset))
-               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->result = -EINTR;
-       regs->gpr[3] = EINTR;
-       regs->ccr |= 0x10000000;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (do_signal(&saveset, regs))
-                       sigreturn_exit(regs);
-       }
+       current->state = TASK_INTERRUPTIBLE;
+       schedule();
+       set_thread_flag(TIF_RESTORE_SIGMASK);
+       return -ERESTARTNOHAND;
 }
 
 #ifdef CONFIG_PPC32
@@ -391,9 +361,6 @@ struct rt_sigframe {
 static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
                int sigret)
 {
-#ifdef CONFIG_PPC32
-       CHECK_FULL_REGS(regs);
-#endif
        /* Make sure floating point registers are stored in regs */
        flush_fp_to_thread(current);
 
@@ -403,8 +370,6 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
                    ELF_NFPREG * sizeof(double)))
                return 1;
 
-       current->thread.fpscr.val = 0;  /* turn off all fp exceptions */
-
 #ifdef CONFIG_ALTIVEC
        /* save altivec registers */
        if (current->thread.used_vr) {
@@ -484,6 +449,15 @@ static long restore_user_regs(struct pt_regs *regs,
        if (err)
                return 1;
 
+       /*
+        * Do this before updating the thread state in
+        * current->thread.fpr/vr/evr.  That way, if we get preempted
+        * and another task grabs the FPU/Altivec/SPE, it won't be
+        * tempted to save the current CPU state into the thread_struct
+        * and corrupt what we are writing there.
+        */
+       discard_lazy_cpu_state();
+
        /* force the process to reload the FP registers from
           current->thread when it next does FP instructions */
        regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1);
@@ -525,18 +499,6 @@ static long restore_user_regs(struct pt_regs *regs,
                return 1;
 #endif /* CONFIG_SPE */
 
-#ifndef CONFIG_SMP
-       preempt_disable();
-       if (last_task_used_math == current)
-               last_task_used_math = NULL;
-       if (last_task_used_altivec == current)
-               last_task_used_altivec = NULL;
-#ifdef CONFIG_SPE
-       if (last_task_used_spe == current)
-               last_task_used_spe = NULL;
-#endif
-       preempt_enable();
-#endif
        return 0;
 }
 
@@ -809,18 +771,18 @@ static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka,
 
        /* Save user registers on the stack */
        frame = &rt_sf->uc.uc_mcontext;
-#ifdef CONFIG_PPC64
        if (vdso32_rt_sigtramp && current->thread.vdso_base) {
                if (save_user_regs(regs, frame, 0))
                        goto badframe;
                regs->link = current->thread.vdso_base + vdso32_rt_sigtramp;
-       } else
-#endif
-       {
+       } else {
                if (save_user_regs(regs, frame, __NR_rt_sigreturn))
                        goto badframe;
                regs->link = (unsigned long) frame->tramp;
        }
+
+       current->thread.fpscr.val = 0;  /* turn off all fp exceptions */
+
        if (put_user(regs->gpr[1], (u32 __user *)newsp))
                goto badframe;
        regs->gpr[1] = newsp;
@@ -830,12 +792,6 @@ static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka,
        regs->gpr[6] = (unsigned long) rt_sf;
        regs->nip = (unsigned long) ka->sa.sa_handler;
        regs->trap = 0;
-#ifdef CONFIG_PPC64
-       regs->result = 0;
-
-       if (test_thread_flag(TIF_SINGLESTEP))
-               ptrace_notify(SIGTRAP);
-#endif
        return 1;
 
 badframe:
@@ -913,8 +869,8 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
         */
        if (do_setcontext(new_ctx, regs, 0))
                do_exit(SIGSEGV);
-       sigreturn_exit(regs);
-       /* doesn't actually return back to here */
+
+       set_thread_flag(TIF_RESTOREALL);
        return 0;
 }
 
@@ -947,12 +903,11 @@ long sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
         * nobody does any...
         */
        compat_sys_sigaltstack((u32)(u64)&rt_sf->uc.uc_stack, 0, 0, 0, 0, 0, regs);
-       return (int)regs->result;
 #else
        do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]);
-       sigreturn_exit(regs);           /* doesn't return here */
-       return 0;
 #endif
+       set_thread_flag(TIF_RESTOREALL);
+       return 0;
 
  bad:
        force_sig(SIGSEGV, current);
@@ -1043,9 +998,7 @@ int sys_debug_setcontext(struct ucontext __user *ctx,
         */
        do_sigaltstack(&ctx->uc_stack, NULL, regs->gpr[1]);
 
-       sigreturn_exit(regs);
-       /* doesn't actually return back to here */
-
+       set_thread_flag(TIF_RESTOREALL);
  out:
        return 0;
 }
@@ -1090,19 +1043,18 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
            || __put_user(sig, &sc->signal))
                goto badframe;
 
-#ifdef CONFIG_PPC64
        if (vdso32_sigtramp && current->thread.vdso_base) {
                if (save_user_regs(regs, &frame->mctx, 0))
                        goto badframe;
                regs->link = current->thread.vdso_base + vdso32_sigtramp;
-       } else
-#endif
-       {
+       } else {
                if (save_user_regs(regs, &frame->mctx, __NR_sigreturn))
                        goto badframe;
                regs->link = (unsigned long) frame->mctx.tramp;
        }
 
+       current->thread.fpscr.val = 0;  /* turn off all fp exceptions */
+
        if (put_user(regs->gpr[1], (u32 __user *)newsp))
                goto badframe;
        regs->gpr[1] = newsp;
@@ -1110,12 +1062,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
        regs->gpr[4] = (unsigned long) sc;
        regs->nip = (unsigned long) ka->sa.sa_handler;
        regs->trap = 0;
-#ifdef CONFIG_PPC64
-       regs->result = 0;
-
-       if (test_thread_flag(TIF_SINGLESTEP))
-               ptrace_notify(SIGTRAP);
-#endif
 
        return 1;
 
@@ -1163,12 +1109,8 @@ long sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
            || restore_user_regs(regs, sr, 1))
                goto badframe;
 
-#ifdef CONFIG_PPC64
-       return (int)regs->result;
-#else
-       sigreturn_exit(regs);           /* doesn't return */
+       set_thread_flag(TIF_RESTOREALL);
        return 0;
-#endif
 
 badframe:
        force_sig(SIGSEGV, current);
@@ -1184,7 +1126,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
 {
        siginfo_t info;
        struct k_sigaction ka;
-       unsigned int frame, newsp;
+       unsigned int newsp;
        int signr, ret;
 
 #ifdef CONFIG_PPC32
@@ -1195,11 +1137,11 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
        }
 #endif
 
-       if (!oldset)
+       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+               oldset = &current->saved_sigmask;
+       else if (!oldset)
                oldset = &current->blocked;
 
-       newsp = frame = 0;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
 #ifdef CONFIG_PPC32
 no_signal:
@@ -1229,8 +1171,14 @@ no_signal:
                }
        }
 
-       if (signr == 0)
+       if (signr == 0) {
+               /* No signal to deliver -- 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);
+               }
                return 0;               /* no signals delivered */
+       }
 
        if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size
            && !on_sig_stack(regs->gpr[1]))
@@ -1263,6 +1211,10 @@ no_signal:
                        sigaddset(&current->blocked, signr);
                recalc_sigpending();
                spin_unlock_irq(&current->sighand->siglock);
+               /* A signal was successfully delivered; the saved sigmask is in
+                  its frame, and we can clear the TIF_RESTORE_SIGMASK flag */
+               if (test_thread_flag(TIF_RESTORE_SIGMASK))
+                       clear_thread_flag(TIF_RESTORE_SIGMASK);
        }
 
        return ret;