diff options
Diffstat (limited to 'arch/arm/mm/fault.c')
-rw-r--r-- | arch/arm/mm/fault.c | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index cbfb2edcf7d1..23b0b03af5ea 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c | |||
@@ -413,7 +413,16 @@ do_translation_fault(unsigned long addr, unsigned int fsr, | |||
413 | pmd_k = pmd_offset(pgd_k, addr); | 413 | pmd_k = pmd_offset(pgd_k, addr); |
414 | pmd = pmd_offset(pgd, addr); | 414 | pmd = pmd_offset(pgd, addr); |
415 | 415 | ||
416 | if (pmd_none(*pmd_k)) | 416 | /* |
417 | * On ARM one Linux PGD entry contains two hardware entries (see page | ||
418 | * tables layout in pgtable.h). We normally guarantee that we always | ||
419 | * fill both L1 entries. But create_mapping() doesn't follow the rule. | ||
420 | * It can create inidividual L1 entries, so here we have to call | ||
421 | * pmd_none() check for the entry really corresponded to address, not | ||
422 | * for the first of pair. | ||
423 | */ | ||
424 | index = (addr >> SECTION_SHIFT) & 1; | ||
425 | if (pmd_none(pmd_k[index])) | ||
417 | goto bad_area; | 426 | goto bad_area; |
418 | 427 | ||
419 | copy_pmd(pmd, pmd_k); | 428 | copy_pmd(pmd, pmd_k); |
@@ -463,15 +472,10 @@ static struct fsr_info { | |||
463 | * defines these to be "precise" aborts. | 472 | * defines these to be "precise" aborts. |
464 | */ | 473 | */ |
465 | { do_bad, SIGSEGV, 0, "vector exception" }, | 474 | { do_bad, SIGSEGV, 0, "vector exception" }, |
466 | { do_bad, SIGILL, BUS_ADRALN, "alignment exception" }, | 475 | { do_bad, SIGBUS, BUS_ADRALN, "alignment exception" }, |
467 | { do_bad, SIGKILL, 0, "terminal exception" }, | 476 | { do_bad, SIGKILL, 0, "terminal exception" }, |
468 | { do_bad, SIGILL, BUS_ADRALN, "alignment exception" }, | 477 | { do_bad, SIGBUS, BUS_ADRALN, "alignment exception" }, |
469 | /* Do we need runtime check ? */ | ||
470 | #if __LINUX_ARM_ARCH__ < 6 | ||
471 | { do_bad, SIGBUS, 0, "external abort on linefetch" }, | 478 | { do_bad, SIGBUS, 0, "external abort on linefetch" }, |
472 | #else | ||
473 | { do_translation_fault, SIGSEGV, SEGV_MAPERR, "I-cache maintenance fault" }, | ||
474 | #endif | ||
475 | { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" }, | 479 | { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" }, |
476 | { do_bad, SIGBUS, 0, "external abort on linefetch" }, | 480 | { do_bad, SIGBUS, 0, "external abort on linefetch" }, |
477 | { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" }, | 481 | { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" }, |
@@ -508,13 +512,15 @@ static struct fsr_info { | |||
508 | 512 | ||
509 | void __init | 513 | void __init |
510 | hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), | 514 | hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), |
511 | int sig, const char *name) | 515 | int sig, int code, const char *name) |
512 | { | 516 | { |
513 | if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) { | 517 | if (nr < 0 || nr >= ARRAY_SIZE(fsr_info)) |
514 | fsr_info[nr].fn = fn; | 518 | BUG(); |
515 | fsr_info[nr].sig = sig; | 519 | |
516 | fsr_info[nr].name = name; | 520 | fsr_info[nr].fn = fn; |
517 | } | 521 | fsr_info[nr].sig = sig; |
522 | fsr_info[nr].code = code; | ||
523 | fsr_info[nr].name = name; | ||
518 | } | 524 | } |
519 | 525 | ||
520 | /* | 526 | /* |
@@ -594,3 +600,25 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) | |||
594 | arm_notify_die("", regs, &info, ifsr, 0); | 600 | arm_notify_die("", regs, &info, ifsr, 0); |
595 | } | 601 | } |
596 | 602 | ||
603 | static int __init exceptions_init(void) | ||
604 | { | ||
605 | if (cpu_architecture() >= CPU_ARCH_ARMv6) { | ||
606 | hook_fault_code(4, do_translation_fault, SIGSEGV, SEGV_MAPERR, | ||
607 | "I-cache maintenance fault"); | ||
608 | } | ||
609 | |||
610 | if (cpu_architecture() >= CPU_ARCH_ARMv7) { | ||
611 | /* | ||
612 | * TODO: Access flag faults introduced in ARMv6K. | ||
613 | * Runtime check for 'K' extension is needed | ||
614 | */ | ||
615 | hook_fault_code(3, do_bad, SIGSEGV, SEGV_MAPERR, | ||
616 | "section access flag fault"); | ||
617 | hook_fault_code(6, do_bad, SIGSEGV, SEGV_MAPERR, | ||
618 | "section access flag fault"); | ||
619 | } | ||
620 | |||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | arch_initcall(exceptions_init); | ||