sparc64: Handle additional cases of no fault loads
[sfrench/cifs-2.6.git] / arch / sparc / kernel / traps_64.c
index ad31af1dd726575986c87bba514b345f291342ec..c74f2dffcc13f5cc77109c6476807fb332a871c6 100644 (file)
@@ -265,6 +265,45 @@ void sun4v_insn_access_exception_tl1(struct pt_regs *regs, unsigned long addr, u
        sun4v_insn_access_exception(regs, addr, type_ctx);
 }
 
+bool is_no_fault_exception(struct pt_regs *regs)
+{
+       unsigned char asi;
+       u32 insn;
+
+       if (get_user(insn, (u32 __user *)regs->tpc) == -EFAULT)
+               return false;
+
+       /*
+        * Must do a little instruction decoding here in order to
+        * decide on a course of action. The bits of interest are:
+        *  insn[31:30] = op, where 3 indicates the load/store group
+        *  insn[24:19] = op3, which identifies individual opcodes
+        *  insn[13] indicates an immediate offset
+        *  op3[4]=1 identifies alternate space instructions
+        *  op3[5:4]=3 identifies floating point instructions
+        *  op3[2]=1 identifies stores
+        * See "Opcode Maps" in the appendix of any Sparc V9
+        * architecture spec for full details.
+        */
+       if ((insn & 0xc0800000) == 0xc0800000) {    /* op=3, op3[4]=1   */
+               if (insn & 0x2000)                  /* immediate offset */
+                       asi = (regs->tstate >> 24); /* saved %asi       */
+               else
+                       asi = (insn >> 5);          /* immediate asi    */
+               if ((asi & 0xf2) == ASI_PNF) {
+                       if (insn & 0x1000000) {     /* op3[5:4]=3       */
+                               handle_ldf_stq(insn, regs);
+                               return true;
+                       } else if (insn & 0x200000) { /* op3[2], stores */
+                               return false;
+                       }
+                       handle_ld_nf(insn, regs);
+                       return true;
+               }
+       }
+       return false;
+}
+
 void spitfire_data_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
 {
        enum ctx_state prev_state = exception_enter();
@@ -296,6 +335,9 @@ void spitfire_data_access_exception(struct pt_regs *regs, unsigned long sfsr, un
                die_if_kernel("Dax", regs);
        }
 
+       if (is_no_fault_exception(regs))
+               return;
+
        info.si_signo = SIGSEGV;
        info.si_errno = 0;
        info.si_code = SEGV_MAPERR;
@@ -352,6 +394,9 @@ void sun4v_data_access_exception(struct pt_regs *regs, unsigned long addr, unsig
                regs->tpc &= 0xffffffff;
                regs->tnpc &= 0xffffffff;
        }
+       if (is_no_fault_exception(regs))
+               return;
+
        info.si_signo = SIGSEGV;
        info.si_errno = 0;
        info.si_code = SEGV_MAPERR;
@@ -2575,6 +2620,9 @@ void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned lo
                kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
                goto out;
        }
+       if (is_no_fault_exception(regs))
+               return;
+
        info.si_signo = SIGBUS;
        info.si_errno = 0;
        info.si_code = BUS_ADRALN;
@@ -2597,6 +2645,9 @@ void sun4v_do_mna(struct pt_regs *regs, unsigned long addr, unsigned long type_c
                kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
                return;
        }
+       if (is_no_fault_exception(regs))
+               return;
+
        info.si_signo = SIGBUS;
        info.si_errno = 0;
        info.si_code = BUS_ADRALN;