aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill@shutemov.name>2009-09-25 08:40:49 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-10-02 17:34:32 -0400
commitd25ef8b86e6a58f5476bf6e4a8da730b335f68fa (patch)
tree2d7773ed145ace0f93d7a11ee9f738a4df4be14f /arch
parent4fb2847437d871fe579f820ceb18031db3359901 (diff)
ARM: 5728/1: Proper prefetch abort handling on ARMv6 and ARMv7
Currently, on ARMv6 and ARMv7, if an application tries to execute code (or garbage) on non-executable page it hangs. It caused by incorrect prefetch abort handling. Now every prefetch abort processes as a translation fault. To fix this we have to analyze instruction fault status register to figure out reason why we've got the abort and process it accordingly. To make IFSR different from DFSR we set bit 31 which is reserved in both IFSR and DFSR. This patch also tries to protect from future hangs on unexpected exceptions. An application will be killed if unexpected exception type was received. Signed-off-by: Kirill A. Shutemov <kirill@shutemov.name> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mm/fault.c51
1 files changed, 50 insertions, 1 deletions
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index fd2375f84994..ae0e25f5a70e 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -519,9 +519,58 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
519 arm_notify_die("", regs, &info, fsr, 0); 519 arm_notify_die("", regs, &info, fsr, 0);
520} 520}
521 521
522
523static struct fsr_info ifsr_info[] = {
524 { do_bad, SIGBUS, 0, "unknown 0" },
525 { do_bad, SIGBUS, 0, "unknown 1" },
526 { do_bad, SIGBUS, 0, "debug event" },
527 { do_bad, SIGSEGV, SEGV_ACCERR, "section access flag fault" },
528 { do_bad, SIGBUS, 0, "unknown 4" },
529 { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" },
530 { do_bad, SIGSEGV, SEGV_ACCERR, "page access flag fault" },
531 { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" },
532 { do_bad, SIGBUS, 0, "external abort on non-linefetch" },
533 { do_bad, SIGSEGV, SEGV_ACCERR, "section domain fault" },
534 { do_bad, SIGBUS, 0, "unknown 10" },
535 { do_bad, SIGSEGV, SEGV_ACCERR, "page domain fault" },
536 { do_bad, SIGBUS, 0, "external abort on translation" },
537 { do_sect_fault, SIGSEGV, SEGV_ACCERR, "section permission fault" },
538 { do_bad, SIGBUS, 0, "external abort on translation" },
539 { do_page_fault, SIGSEGV, SEGV_ACCERR, "page permission fault" },
540 { do_bad, SIGBUS, 0, "unknown 16" },
541 { do_bad, SIGBUS, 0, "unknown 17" },
542 { do_bad, SIGBUS, 0, "unknown 18" },
543 { do_bad, SIGBUS, 0, "unknown 19" },
544 { do_bad, SIGBUS, 0, "unknown 20" },
545 { do_bad, SIGBUS, 0, "unknown 21" },
546 { do_bad, SIGBUS, 0, "unknown 22" },
547 { do_bad, SIGBUS, 0, "unknown 23" },
548 { do_bad, SIGBUS, 0, "unknown 24" },
549 { do_bad, SIGBUS, 0, "unknown 25" },
550 { do_bad, SIGBUS, 0, "unknown 26" },
551 { do_bad, SIGBUS, 0, "unknown 27" },
552 { do_bad, SIGBUS, 0, "unknown 28" },
553 { do_bad, SIGBUS, 0, "unknown 29" },
554 { do_bad, SIGBUS, 0, "unknown 30" },
555 { do_bad, SIGBUS, 0, "unknown 31" },
556};
557
522asmlinkage void __exception 558asmlinkage void __exception
523do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) 559do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
524{ 560{
525 do_translation_fault(addr, FSR_LNX_PF, regs); 561 const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
562 struct siginfo info;
563
564 if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
565 return;
566
567 printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
568 inf->name, ifsr, addr);
569
570 info.si_signo = inf->sig;
571 info.si_errno = 0;
572 info.si_code = inf->code;
573 info.si_addr = (void __user *)addr;
574 arm_notify_die("", regs, &info, ifsr, 0);
526} 575}
527 576