]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - arch/arm/mm/fault.c
ARM: 5728/1: Proper prefetch abort handling on ARMv6 and ARMv7
[linux-2.6.git] / arch / arm / mm / fault.c
index fd2375f849941f7a91727e70e84ba73804d06c35..ae0e25f5a70eb57987c131f5fbfd4fe795df3302 100644 (file)
@@ -519,9 +519,58 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        arm_notify_die("", regs, &info, fsr, 0);
 }
 
+
+static struct fsr_info ifsr_info[] = {
+       { do_bad,               SIGBUS,  0,             "unknown 0"                        },
+       { do_bad,               SIGBUS,  0,             "unknown 1"                        },
+       { do_bad,               SIGBUS,  0,             "debug event"                      },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section access flag fault"        },
+       { do_bad,               SIGBUS,  0,             "unknown 4"                        },
+       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page access flag fault"           },
+       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
+       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
+       { do_bad,               SIGBUS,  0,             "unknown 10"                       },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
+       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
+       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
+       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
+       { do_bad,               SIGBUS,  0,             "unknown 16"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 17"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 18"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 19"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 20"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 21"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 22"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 23"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 24"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 25"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 26"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 27"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 28"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 29"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 30"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 31"                       },
+};
+
 asmlinkage void __exception
 do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
 {
-       do_translation_fault(addr, FSR_LNX_PF, regs);
+       const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
+       struct siginfo info;
+
+       if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
+               return;
+
+       printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
+               inf->name, ifsr, addr);
+
+       info.si_signo = inf->sig;
+       info.si_errno = 0;
+       info.si_code  = inf->code;
+       info.si_addr  = (void __user *)addr;
+       arm_notify_die("", regs, &info, ifsr, 0);
 }