arm: krait: Conditional execution abort handler
On certain early samples of the Krait processor, certain
thumb instructions may cause data aborts even if these
instructions fail their condition code checks. Add a
handler to ignore data aborts generated by such
instructions.
Signed-off-by: Stepan Moskovchenko <stepanm@codeaurora.org>
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index b6850fe..7475c2e 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -23,6 +23,7 @@
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
+#include <asm/cputype.h>
#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP)
#include <asm/io.h>
#include <mach/msm_iomap.h>
@@ -597,6 +598,75 @@
fsr_info[nr].name = name;
}
+#ifdef CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER
+static int krait_tbb_fixup(unsigned int fsr, struct pt_regs *regs)
+{
+ int base_cond, cond = 0;
+ unsigned int p1, cpsr_z, cpsr_c, cpsr_n, cpsr_v;
+
+ if ((read_cpuid_id() & 0xFFFFFFFC) != 0x510F04D0)
+ return 0;
+
+ if (!thumb_mode(regs))
+ return 0;
+
+ /* If ITSTATE is 0, return quickly */
+ if ((regs->ARM_cpsr & PSR_IT_MASK) == 0)
+ return 0;
+
+ cpsr_n = (regs->ARM_cpsr & PSR_N_BIT) ? 1 : 0;
+ cpsr_z = (regs->ARM_cpsr & PSR_Z_BIT) ? 1 : 0;
+ cpsr_c = (regs->ARM_cpsr & PSR_C_BIT) ? 1 : 0;
+ cpsr_v = (regs->ARM_cpsr & PSR_V_BIT) ? 1 : 0;
+
+ p1 = (regs->ARM_cpsr & BIT(12)) ? 1 : 0;
+
+ base_cond = (regs->ARM_cpsr >> 13) & 0x07;
+
+ switch (base_cond) {
+ case 0x0: /* equal */
+ cond = cpsr_z;
+ break;
+
+ case 0x1: /* carry set */
+ cond = cpsr_c;
+ break;
+
+ case 0x2: /* minus / negative */
+ cond = cpsr_n;
+ break;
+
+ case 0x3: /* overflow */
+ cond = cpsr_v;
+ break;
+
+ case 0x4: /* unsigned higher */
+ cond = (cpsr_c == 1) && (cpsr_z == 0);
+ break;
+
+ case 0x5: /* signed greater / equal */
+ cond = (cpsr_n == cpsr_v);
+ break;
+
+ case 0x6: /* signed greater */
+ cond = (cpsr_z == 0) && (cpsr_n == cpsr_v);
+ break;
+
+ case 0x7: /* always */
+ cond = 1;
+ break;
+ };
+
+ if (cond == p1) {
+ pr_debug("Conditional abort fixup, PC=%08x, base=%d, cond=%d\n",
+ (unsigned int) regs->ARM_pc, base_cond, cond);
+ regs->ARM_pc += 2;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
/*
* Dispatch a data abort to the relevant handler.
*/
@@ -611,6 +681,11 @@
return;
#endif
+#ifdef CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER
+ if (krait_tbb_fixup(fsr, regs))
+ return;
+#endif
+
if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
return;