|
@@ -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;
|