summaryrefslogtreecommitdiffstats
path: root/arch/arm/mm/fault-armv.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mm/fault-armv.c')
-rw-r--r--arch/arm/mm/fault-armv.c145
1 files changed, 89 insertions, 56 deletions
diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c
index f184b4e68..a6a45f091 100644
--- a/arch/arm/mm/fault-armv.c
+++ b/arch/arm/mm/fault-armv.c
@@ -27,27 +27,9 @@
#include <asm/pgtable.h>
#include <asm/unaligned.h>
-#define FAULT_CODE_READ 0x02
-
-#define DO_COW(m) (!((m) & FAULT_CODE_READ))
-#define READ_FAULT(m) ((m) & FAULT_CODE_READ)
-
extern void die_if_kernel(const char *str, struct pt_regs *regs, int err);
-
-#include "fault-common.c"
-
-#ifdef DEBUG
-static int sp_valid(unsigned long *sp)
-{
- unsigned long addr = (unsigned long) sp;
-
- if (addr >= 0xb0000000 && addr < 0xd0000000)
- return 1;
- if (addr >= 0x03ff0000 && addr < 0x04000000)
- return 1;
- return 0;
-}
-#endif
+extern void show_pte(struct mm_struct *mm, unsigned long addr);
+extern int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs);
#ifdef CONFIG_ALIGNMENT_TRAP
/*
@@ -178,6 +160,15 @@ do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
eaddr -= offset.un;
}
+ /*
+ * This is a "hint" - we already have eaddr worked out by the
+ * processor for us.
+ */
+ if (addr != eaddr)
+ printk(KERN_ERR "LDRHSTRH: PC = %08lx, instr = %08x, "
+ "addr = %08lx, eaddr = %08lx\n",
+ instruction_pointer(regs), instr, addr, eaddr);
+
if (LDST_L_BIT(instr))
regs->uregs[rd] = get_unaligned((unsigned short *)eaddr);
else
@@ -253,8 +244,15 @@ do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
}
}
-if (addr != eaddr)
-printk("PC = %08lx, instr = %08x, addr = %08lx, eaddr = %08lx\n", instruction_pointer(regs), instr, addr, eaddr);
+ /*
+ * This is a "hint" - we already have eaddr worked out by the
+ * processor for us.
+ */
+ if (addr != eaddr)
+ printk(KERN_ERR "LDRSTR: PC = %08lx, instr = %08x, "
+ "addr = %08lx, eaddr = %08lx\n",
+ instruction_pointer(regs), instr, addr, eaddr);
+
if (LDST_L_BIT(instr)) {
regs->uregs[rd] = get_unaligned((unsigned long *)eaddr);
if (rd == 15)
@@ -284,6 +282,15 @@ printk("PC = %08lx, instr = %08x, addr = %08lx, eaddr = %08lx\n", instruction_po
if (!LDST_U_BIT(instr))
eaddr -= nr_regs;
+ /*
+ * This is a "hint" - we already have eaddr worked out by the
+ * processor for us.
+ */
+ if (addr != eaddr)
+ printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08x, "
+ "addr = %08lx, eaddr = %08lx\n",
+ instruction_pointer(regs), instr, addr, eaddr);
+
if ((LDST_U_BIT(instr) == 0 && LDST_P_BIT(instr) == 0) ||
(LDST_U_BIT(instr) && LDST_P_BIT(instr)))
eaddr += 4;
@@ -322,39 +329,39 @@ printk("PC = %08lx, instr = %08x, addr = %08lx, eaddr = %08lx\n", instruction_po
#endif
-#ifdef CONFIG_DEBUG_USER
-
+/*
+ * Some section permission faults need to be handled gracefully, for
+ * instance, when they happen due to a __{get,put}_user during an oops).
+ * In this case, we should return an error to the __{get,put}_user caller
+ * instead of causing another oops. We should also fixup this fault as
+ * the user could pass a pointer to a section to the kernel.
+ */
static int
do_sect_fault(unsigned long addr, int error_code, struct pt_regs *regs)
{
+ unsigned long fixup;
+
if (user_mode(regs)) {
+#ifdef CONFIG_DEBUG_USER
printk("%s: permission fault on section, "
"address=0x%08lx, code %d\n",
current->comm, addr, error_code);
+#endif
+ goto fail;
+ }
+
+ fixup = search_exception_table(instruction_pointer(regs));
+ if (fixup != 0) {
#ifdef DEBUG
- {
- unsigned int i, j;
- unsigned long *sp;
-
- sp = (unsigned long *) (regs->ARM_sp - 128);
- for (j = 0; j < 20 && sp_valid(sp); j++) {
- printk("%p: ", sp);
- for (i = 0; i < 8 && sp_valid(sp); i += 1, sp++)
- printk("%08lx ", *sp);
- printk("\n");
- }
- show_regs(regs);
- c_backtrace(regs->ARM_fp, regs->ARM_cpsr);
- }
+ printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n",
+ tsk->comm, regs->ARM_pc, addr, fixup);
#endif
+ regs->ARM_pc = fixup;
+ return 0;
}
+fail:
return 1; /* not fixed up */
}
-#else
-
-#define do_sect_fault NULL
-
-#endif
static const struct fsr_info {
int (*fn)(unsigned long addr, int error_code, struct pt_regs *regs);
@@ -382,18 +389,19 @@ static const struct fsr_info {
/*
* Currently dropped down to debug level
*/
-#define BUG_PROC_MSG \
- KERN_DEBUG "Weird data abort (%08X).\n" \
- KERN_DEBUG "Please see http://www.arm.linux.org.uk/state.html for " \
- "more information\n"
-
asmlinkage void
do_DataAbort(unsigned long addr, int error_code, struct pt_regs *regs, int fsr)
{
const struct fsr_info *inf = fsr_info + (fsr & 15);
+#if defined(CONFIG_CPU_SA110) || defined(CONFIG_CPU_SA1100)
if (addr == regs->ARM_pc)
- goto weirdness;
+ goto sa1_weirdness;
+#endif
+#if defined(CONFIG_CPU_ARM720T) && defined(CONFIG_ALIGNMENT_TRAP)
+ if (addr & 3 && (fsr & 13) != 1)
+ goto arm720_weirdness;
+#endif
if (!inf->fn)
goto bad;
@@ -409,26 +417,51 @@ bad:
die_if_kernel("Oops", regs, 0);
return;
-weirdness:
+#if defined(CONFIG_CPU_SA110) || defined(CONFIG_CPU_SA1100)
+sa1_weirdness:
if (user_mode(regs)) {
static int first = 1;
if (first)
- /*
- * I want statistical information on this problem,
- * but we don't want to hastle the users too much.
- */
- printk(BUG_PROC_MSG, fsr);
+ printk(KERN_DEBUG "Weird data abort detected\n");
first = 0;
return;
}
if (!inf->fn || inf->fn(addr, error_code, regs))
goto bad;
+ return;
+#endif
+#if defined(CONFIG_CPU_ARM720T) && defined(CONFIG_ALIGNMENT_TRAP)
+arm720_weirdness:
+ if (!user_mode(regs)) {
+ unsigned long instr;
+
+ instr = *(unsigned long *)instruction_pointer(regs);
+
+ if ((instr & 0x04400000) != 0x04400000) {
+ static int first = 1;
+ if (first)
+ printk("Mis-reported alignment fault at "
+ "0x%08lx, fsr 0x%02x, code 0x%02x, "
+ "PC = 0x%08lx, instr = 0x%08lx\n",
+ addr, fsr, error_code, regs->ARM_pc,
+ instr);
+ first = 0;
+ cpu_tlb_invalidate_all();
+ cpu_cache_clean_invalidate_all();
+ return;
+ }
+ }
+
+ if (!inf->fn || inf->fn(addr, error_code, regs))
+ goto bad;
+ return;
+#endif
}
asmlinkage int
do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
{
- do_page_fault(addr, FAULT_CODE_READ, regs);
+ do_page_fault(addr, 0, regs);
return 1;
}