diff options
Diffstat (limited to 'arch/arm/mm/fault.c')
| -rw-r--r-- | arch/arm/mm/fault.c | 111 |
1 files changed, 19 insertions, 92 deletions
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index aa33949fef60..eb5520fc755f 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c | |||
| @@ -27,19 +27,6 @@ | |||
| 27 | 27 | ||
| 28 | #include "fault.h" | 28 | #include "fault.h" |
| 29 | 29 | ||
| 30 | /* | ||
| 31 | * Fault status register encodings. We steal bit 31 for our own purposes. | ||
| 32 | */ | ||
| 33 | #define FSR_LNX_PF (1 << 31) | ||
| 34 | #define FSR_WRITE (1 << 11) | ||
| 35 | #define FSR_FS4 (1 << 10) | ||
| 36 | #define FSR_FS3_0 (15) | ||
| 37 | |||
| 38 | static inline int fsr_fs(unsigned int fsr) | ||
| 39 | { | ||
| 40 | return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6; | ||
| 41 | } | ||
| 42 | |||
| 43 | #ifdef CONFIG_MMU | 30 | #ifdef CONFIG_MMU |
| 44 | 31 | ||
| 45 | #ifdef CONFIG_KPROBES | 32 | #ifdef CONFIG_KPROBES |
| @@ -123,8 +110,10 @@ void show_pte(struct mm_struct *mm, unsigned long addr) | |||
| 123 | 110 | ||
| 124 | pte = pte_offset_map(pmd, addr); | 111 | pte = pte_offset_map(pmd, addr); |
| 125 | printk(", *pte=%08llx", (long long)pte_val(*pte)); | 112 | printk(", *pte=%08llx", (long long)pte_val(*pte)); |
| 113 | #ifndef CONFIG_ARM_LPAE | ||
| 126 | printk(", *ppte=%08llx", | 114 | printk(", *ppte=%08llx", |
| 127 | (long long)pte_val(pte[PTE_HWTABLE_PTRS])); | 115 | (long long)pte_val(pte[PTE_HWTABLE_PTRS])); |
| 116 | #endif | ||
| 128 | pte_unmap(pte); | 117 | pte_unmap(pte); |
| 129 | } while(0); | 118 | } while(0); |
| 130 | 119 | ||
| @@ -441,6 +430,12 @@ do_translation_fault(unsigned long addr, unsigned int fsr, | |||
| 441 | pmd = pmd_offset(pud, addr); | 430 | pmd = pmd_offset(pud, addr); |
| 442 | pmd_k = pmd_offset(pud_k, addr); | 431 | pmd_k = pmd_offset(pud_k, addr); |
| 443 | 432 | ||
| 433 | #ifdef CONFIG_ARM_LPAE | ||
| 434 | /* | ||
| 435 | * Only one hardware entry per PMD with LPAE. | ||
| 436 | */ | ||
| 437 | index = 0; | ||
| 438 | #else | ||
| 444 | /* | 439 | /* |
| 445 | * On ARM one Linux PGD entry contains two hardware entries (see page | 440 | * On ARM one Linux PGD entry contains two hardware entries (see page |
| 446 | * tables layout in pgtable.h). We normally guarantee that we always | 441 | * tables layout in pgtable.h). We normally guarantee that we always |
| @@ -450,6 +445,7 @@ do_translation_fault(unsigned long addr, unsigned int fsr, | |||
| 450 | * for the first of pair. | 445 | * for the first of pair. |
| 451 | */ | 446 | */ |
| 452 | index = (addr >> SECTION_SHIFT) & 1; | 447 | index = (addr >> SECTION_SHIFT) & 1; |
| 448 | #endif | ||
| 453 | if (pmd_none(pmd_k[index])) | 449 | if (pmd_none(pmd_k[index])) |
| 454 | goto bad_area; | 450 | goto bad_area; |
| 455 | 451 | ||
| @@ -489,55 +485,20 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
| 489 | return 1; | 485 | return 1; |
| 490 | } | 486 | } |
| 491 | 487 | ||
| 492 | static struct fsr_info { | 488 | struct fsr_info { |
| 493 | int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); | 489 | int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); |
| 494 | int sig; | 490 | int sig; |
| 495 | int code; | 491 | int code; |
| 496 | const char *name; | 492 | const char *name; |
| 497 | } fsr_info[] = { | ||
| 498 | /* | ||
| 499 | * The following are the standard ARMv3 and ARMv4 aborts. ARMv5 | ||
| 500 | * defines these to be "precise" aborts. | ||
| 501 | */ | ||
| 502 | { do_bad, SIGSEGV, 0, "vector exception" }, | ||
| 503 | { do_bad, SIGBUS, BUS_ADRALN, "alignment exception" }, | ||
| 504 | { do_bad, SIGKILL, 0, "terminal exception" }, | ||
| 505 | { do_bad, SIGBUS, BUS_ADRALN, "alignment exception" }, | ||
| 506 | { do_bad, SIGBUS, 0, "external abort on linefetch" }, | ||
| 507 | { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" }, | ||
| 508 | { do_bad, SIGBUS, 0, "external abort on linefetch" }, | ||
| 509 | { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" }, | ||
| 510 | { do_bad, SIGBUS, 0, "external abort on non-linefetch" }, | ||
| 511 | { do_bad, SIGSEGV, SEGV_ACCERR, "section domain fault" }, | ||
| 512 | { do_bad, SIGBUS, 0, "external abort on non-linefetch" }, | ||
| 513 | { do_bad, SIGSEGV, SEGV_ACCERR, "page domain fault" }, | ||
| 514 | { do_bad, SIGBUS, 0, "external abort on translation" }, | ||
| 515 | { do_sect_fault, SIGSEGV, SEGV_ACCERR, "section permission fault" }, | ||
| 516 | { do_bad, SIGBUS, 0, "external abort on translation" }, | ||
| 517 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "page permission fault" }, | ||
| 518 | /* | ||
| 519 | * The following are "imprecise" aborts, which are signalled by bit | ||
| 520 | * 10 of the FSR, and may not be recoverable. These are only | ||
| 521 | * supported if the CPU abort handler supports bit 10. | ||
| 522 | */ | ||
| 523 | { do_bad, SIGBUS, 0, "unknown 16" }, | ||
| 524 | { do_bad, SIGBUS, 0, "unknown 17" }, | ||
| 525 | { do_bad, SIGBUS, 0, "unknown 18" }, | ||
| 526 | { do_bad, SIGBUS, 0, "unknown 19" }, | ||
| 527 | { do_bad, SIGBUS, 0, "lock abort" }, /* xscale */ | ||
| 528 | { do_bad, SIGBUS, 0, "unknown 21" }, | ||
| 529 | { do_bad, SIGBUS, BUS_OBJERR, "imprecise external abort" }, /* xscale */ | ||
| 530 | { do_bad, SIGBUS, 0, "unknown 23" }, | ||
| 531 | { do_bad, SIGBUS, 0, "dcache parity error" }, /* xscale */ | ||
| 532 | { do_bad, SIGBUS, 0, "unknown 25" }, | ||
| 533 | { do_bad, SIGBUS, 0, "unknown 26" }, | ||
| 534 | { do_bad, SIGBUS, 0, "unknown 27" }, | ||
| 535 | { do_bad, SIGBUS, 0, "unknown 28" }, | ||
| 536 | { do_bad, SIGBUS, 0, "unknown 29" }, | ||
| 537 | { do_bad, SIGBUS, 0, "unknown 30" }, | ||
| 538 | { do_bad, SIGBUS, 0, "unknown 31" } | ||
| 539 | }; | 493 | }; |
| 540 | 494 | ||
| 495 | /* FSR definition */ | ||
| 496 | #ifdef CONFIG_ARM_LPAE | ||
| 497 | #include "fsr-3level.c" | ||
| 498 | #else | ||
| 499 | #include "fsr-2level.c" | ||
| 500 | #endif | ||
| 501 | |||
| 541 | void __init | 502 | void __init |
| 542 | hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), | 503 | hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), |
| 543 | int sig, int code, const char *name) | 504 | int sig, int code, const char *name) |
| @@ -573,42 +534,6 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
| 573 | arm_notify_die("", regs, &info, fsr, 0); | 534 | arm_notify_die("", regs, &info, fsr, 0); |
| 574 | } | 535 | } |
| 575 | 536 | ||
| 576 | |||
| 577 | static struct fsr_info ifsr_info[] = { | ||
| 578 | { do_bad, SIGBUS, 0, "unknown 0" }, | ||
| 579 | { do_bad, SIGBUS, 0, "unknown 1" }, | ||
| 580 | { do_bad, SIGBUS, 0, "debug event" }, | ||
| 581 | { do_bad, SIGSEGV, SEGV_ACCERR, "section access flag fault" }, | ||
| 582 | { do_bad, SIGBUS, 0, "unknown 4" }, | ||
| 583 | { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" }, | ||
| 584 | { do_bad, SIGSEGV, SEGV_ACCERR, "page access flag fault" }, | ||
| 585 | { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" }, | ||
| 586 | { do_bad, SIGBUS, 0, "external abort on non-linefetch" }, | ||
| 587 | { do_bad, SIGSEGV, SEGV_ACCERR, "section domain fault" }, | ||
| 588 | { do_bad, SIGBUS, 0, "unknown 10" }, | ||
| 589 | { do_bad, SIGSEGV, SEGV_ACCERR, "page domain fault" }, | ||
| 590 | { do_bad, SIGBUS, 0, "external abort on translation" }, | ||
| 591 | { do_sect_fault, SIGSEGV, SEGV_ACCERR, "section permission fault" }, | ||
| 592 | { do_bad, SIGBUS, 0, "external abort on translation" }, | ||
| 593 | { do_page_fault, SIGSEGV, SEGV_ACCERR, "page permission fault" }, | ||
| 594 | { do_bad, SIGBUS, 0, "unknown 16" }, | ||
| 595 | { do_bad, SIGBUS, 0, "unknown 17" }, | ||
| 596 | { do_bad, SIGBUS, 0, "unknown 18" }, | ||
| 597 | { do_bad, SIGBUS, 0, "unknown 19" }, | ||
| 598 | { do_bad, SIGBUS, 0, "unknown 20" }, | ||
| 599 | { do_bad, SIGBUS, 0, "unknown 21" }, | ||
| 600 | { do_bad, SIGBUS, 0, "unknown 22" }, | ||
| 601 | { do_bad, SIGBUS, 0, "unknown 23" }, | ||
| 602 | { do_bad, SIGBUS, 0, "unknown 24" }, | ||
| 603 | { do_bad, SIGBUS, 0, "unknown 25" }, | ||
| 604 | { do_bad, SIGBUS, 0, "unknown 26" }, | ||
| 605 | { do_bad, SIGBUS, 0, "unknown 27" }, | ||
| 606 | { do_bad, SIGBUS, 0, "unknown 28" }, | ||
| 607 | { do_bad, SIGBUS, 0, "unknown 29" }, | ||
| 608 | { do_bad, SIGBUS, 0, "unknown 30" }, | ||
| 609 | { do_bad, SIGBUS, 0, "unknown 31" }, | ||
| 610 | }; | ||
| 611 | |||
| 612 | void __init | 537 | void __init |
| 613 | hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), | 538 | hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), |
| 614 | int sig, int code, const char *name) | 539 | int sig, int code, const char *name) |
| @@ -641,6 +566,7 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) | |||
| 641 | arm_notify_die("", regs, &info, ifsr, 0); | 566 | arm_notify_die("", regs, &info, ifsr, 0); |
| 642 | } | 567 | } |
| 643 | 568 | ||
| 569 | #ifndef CONFIG_ARM_LPAE | ||
| 644 | static int __init exceptions_init(void) | 570 | static int __init exceptions_init(void) |
| 645 | { | 571 | { |
| 646 | if (cpu_architecture() >= CPU_ARCH_ARMv6) { | 572 | if (cpu_architecture() >= CPU_ARCH_ARMv6) { |
| @@ -663,3 +589,4 @@ static int __init exceptions_init(void) | |||
| 663 | } | 589 | } |
| 664 | 590 | ||
| 665 | arch_initcall(exceptions_init); | 591 | arch_initcall(exceptions_init); |
| 592 | #endif | ||
