Merge branch 'drm-forlinus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[pandora-kernel.git] / arch / powerpc / kernel / signal_32.c
index 5a2eba6..177bba7 100644 (file)
@@ -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);
 }
 
@@ -256,8 +270,10 @@ long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7,
        while (1) {
                current->state = TASK_INTERRUPTIBLE;
                schedule();
-               if (do_signal(&saveset, regs))
-                       sigreturn_exit(regs);
+               if (do_signal(&saveset, regs)) {
+                       set_thread_flag(TIF_RESTOREALL);
+                       return 0;
+               }
        }
 }
 
@@ -292,8 +308,10 @@ long sys_rt_sigsuspend(
        while (1) {
                current->state = TASK_INTERRUPTIBLE;
                schedule();
-               if (do_signal(&saveset, regs))
-                       sigreturn_exit(regs);
+               if (do_signal(&saveset, regs)) {
+                       set_thread_flag(TIF_RESTOREALL);
+                       return 0;
+               }
        }
 }
 
@@ -391,9 +409,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);
 
@@ -482,6 +497,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);
@@ -523,18 +547,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;
 }
 
@@ -828,12 +840,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:
@@ -911,8 +917,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;
 }
 
@@ -945,12 +951,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);
@@ -1041,9 +1046,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;
 }
@@ -1107,12 +1110,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;
 
@@ -1160,12 +1157,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);