vm: add VM_FAULT_SIGSEGV handling support
[firefly-linux-kernel-4.4.55.git] / arch / powerpc / kernel / signal_32.c
index 95068bf569adc17cf51d7e32bd650daaba234b17..81f929f026f2ded9d5199ebeab8406c0ffa152b2 100644 (file)
@@ -407,7 +407,8 @@ inline unsigned long copy_transact_fpr_from_user(struct task_struct *task,
  * altivec/spe instructions at some point.
  */
 static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
-               int sigret, int ctx_has_vsx_region)
+                         struct mcontext __user *tm_frame, int sigret,
+                         int ctx_has_vsx_region)
 {
        unsigned long msr = regs->msr;
 
@@ -441,6 +442,12 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
 #endif /* CONFIG_ALTIVEC */
        if (copy_fpr_to_user(&frame->mc_fregs, current))
                return 1;
+
+       /*
+        * Clear the MSR VSX bit to indicate there is no valid state attached
+        * to this context, except in the specific case below where we set it.
+        */
+       msr &= ~MSR_VSX;
 #ifdef CONFIG_VSX
        /*
         * Copy VSR 0-31 upper half from thread_struct to local
@@ -475,6 +482,12 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
 
        if (__put_user(msr, &frame->mc_gregs[PT_MSR]))
                return 1;
+       /* We need to write 0 the MSR top 32 bits in the tm frame so that we
+        * can check it on the restore to see if TM is active
+        */
+       if (tm_frame && __put_user(0, &tm_frame->mc_gregs[PT_MSR]))
+               return 1;
+
        if (sigret) {
                /* Set up the sigreturn trampoline: li r0,sigret; sc */
                if (__put_user(0x38000000UL + sigret, &frame->tramp[0])
@@ -503,12 +516,6 @@ static int save_tm_user_regs(struct pt_regs *regs,
 {
        unsigned long msr = regs->msr;
 
-       /* tm_reclaim rolls back all reg states, updating thread.ckpt_regs,
-        * thread.transact_fpr[], thread.transact_vr[], etc.
-        */
-       tm_enable();
-       tm_reclaim(&current->thread, msr, TM_CAUSE_SIGNAL);
-
        /* Make sure floating point registers are stored in regs */
        flush_fp_to_thread(current);
 
@@ -753,7 +760,7 @@ static long restore_tm_user_regs(struct pt_regs *regs,
                                 struct mcontext __user *tm_sr)
 {
        long err;
-       unsigned long msr;
+       unsigned long msr, msr_hi;
 #ifdef CONFIG_VSX
        int i;
 #endif
@@ -856,10 +863,15 @@ static long restore_tm_user_regs(struct pt_regs *regs,
         * transactional versions should be loaded.
         */
        tm_enable();
+       /* Make sure the transaction is marked as failed */
+       current->thread.tm_texasr |= TEXASR_FS;
        /* This loads the checkpointed FP/VEC state, if used */
        tm_recheckpoint(&current->thread, msr);
-       /* The task has moved into TM state S, so ensure MSR reflects this */
-       regs->msr = (regs->msr & ~MSR_TS_MASK) | MSR_TS_S;
+       /* Get the top half of the MSR */
+       if (__get_user(msr_hi, &tm_sr->mc_gregs[PT_MSR]))
+               return 1;
+       /* Pull in MSR TM from user context */
+       regs->msr = (regs->msr & ~MSR_TS_MASK) | ((msr_hi<<32) & MSR_TS_MASK);
 
        /* This loads the speculative FP/VEC state, if used */
        if (msr & MSR_FP) {
@@ -958,6 +970,7 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
 {
        struct rt_sigframe __user *rt_sf;
        struct mcontext __user *frame;
+       struct mcontext __user *tm_frame = NULL;
        void __user *addr;
        unsigned long newsp = 0;
        int sigret;
@@ -965,7 +978,7 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
 
        /* Set up Signal Frame */
        /* Put a Real Time Context onto stack */
-       rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf), 1);
+       rt_sf = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*rt_sf), 1);
        addr = rt_sf;
        if (unlikely(rt_sf == NULL))
                goto badframe;
@@ -991,23 +1004,24 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
        }
 
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       tm_frame = &rt_sf->uc_transact.uc_mcontext;
        if (MSR_TM_ACTIVE(regs->msr)) {
-               if (save_tm_user_regs(regs, &rt_sf->uc.uc_mcontext,
-                                     &rt_sf->uc_transact.uc_mcontext, sigret))
+               if (save_tm_user_regs(regs, frame, tm_frame, sigret))
                        goto badframe;
        }
        else
 #endif
-               if (save_user_regs(regs, frame, sigret, 1))
+       {
+               if (save_user_regs(regs, frame, tm_frame, sigret, 1))
                        goto badframe;
+       }
        regs->link = tramp;
 
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
        if (MSR_TM_ACTIVE(regs->msr)) {
                if (__put_user((unsigned long)&rt_sf->uc_transact,
                               &rt_sf->uc.uc_link)
-                   || __put_user(to_user_ptr(&rt_sf->uc_transact.uc_mcontext),
-                                 &rt_sf->uc_transact.uc_regs))
+                   || __put_user((unsigned long)tm_frame, &rt_sf->uc_transact.uc_regs))
                        goto badframe;
        }
        else
@@ -1176,7 +1190,7 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
                mctx = (struct mcontext __user *)
                        ((unsigned long) &old_ctx->uc_mcontext & ~0xfUL);
                if (!access_ok(VERIFY_WRITE, old_ctx, ctx_size)
-                   || save_user_regs(regs, mctx, 0, ctx_has_vsx_region)
+                   || save_user_regs(regs, mctx, NULL, 0, ctx_has_vsx_region)
                    || put_sigset_t(&old_ctx->uc_sigmask, &current->blocked)
                    || __put_user(to_user_ptr(mctx), &old_ctx->uc_regs))
                        return -EFAULT;
@@ -1239,7 +1253,7 @@ long sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
                if (__get_user(msr_hi, &mcp->mc_gregs[PT_MSR]))
                        goto bad;
 
-               if (MSR_TM_SUSPENDED(msr_hi<<32)) {
+               if (MSR_TM_ACTIVE(msr_hi<<32)) {
                        /* We only recheckpoint on return if we're
                         * transaction.
                         */
@@ -1398,12 +1412,13 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
 {
        struct sigcontext __user *sc;
        struct sigframe __user *frame;
+       struct mcontext __user *tm_mctx = NULL;
        unsigned long newsp = 0;
        int sigret;
        unsigned long tramp;
 
        /* Set up Signal Frame */
-       frame = get_sigframe(ka, regs, sizeof(*frame), 1);
+       frame = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*frame), 1);
        if (unlikely(frame == NULL))
                goto badframe;
        sc = (struct sigcontext __user *) &frame->sctx;
@@ -1431,6 +1446,7 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
        }
 
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       tm_mctx = &frame->mctx_transact;
        if (MSR_TM_ACTIVE(regs->msr)) {
                if (save_tm_user_regs(regs, &frame->mctx, &frame->mctx_transact,
                                      sigret))
@@ -1438,8 +1454,10 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
        }
        else
 #endif
-               if (save_user_regs(regs, &frame->mctx, sigret, 1))
+       {
+               if (save_user_regs(regs, &frame->mctx, tm_mctx, sigret, 1))
                        goto badframe;
+       }
 
        regs->link = tramp;
 
@@ -1487,16 +1505,22 @@ badframe:
 long sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
                       struct pt_regs *regs)
 {
+       struct sigframe __user *sf;
        struct sigcontext __user *sc;
        struct sigcontext sigctx;
        struct mcontext __user *sr;
        void __user *addr;
        sigset_t set;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       struct mcontext __user *mcp, *tm_mcp;
+       unsigned long msr_hi;
+#endif
 
        /* Always make any pending restarted system calls return -EINTR */
        current_thread_info()->restart_block.fn = do_no_restart_syscall;
 
-       sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
+       sf = (struct sigframe __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
+       sc = &sf->sctx;
        addr = sc;
        if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
                goto badframe;
@@ -1513,11 +1537,25 @@ long sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
 #endif
        set_current_blocked(&set);
 
-       sr = (struct mcontext __user *)from_user_ptr(sigctx.regs);
-       addr = sr;
-       if (!access_ok(VERIFY_READ, sr, sizeof(*sr))
-           || restore_user_regs(regs, sr, 1))
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       mcp = (struct mcontext __user *)&sf->mctx;
+       tm_mcp = (struct mcontext __user *)&sf->mctx_transact;
+       if (__get_user(msr_hi, &tm_mcp->mc_gregs[PT_MSR]))
                goto badframe;
+       if (MSR_TM_ACTIVE(msr_hi<<32)) {
+               if (!cpu_has_feature(CPU_FTR_TM))
+                       goto badframe;
+               if (restore_tm_user_regs(regs, mcp, tm_mcp))
+                       goto badframe;
+       } else
+#endif
+       {
+               sr = (struct mcontext __user *)from_user_ptr(sigctx.regs);
+               addr = sr;
+               if (!access_ok(VERIFY_READ, sr, sizeof(*sr))
+                   || restore_user_regs(regs, sr, 1))
+                       goto badframe;
+       }
 
        set_thread_flag(TIF_RESTOREALL);
        return 0;