diff options
Diffstat (limited to 'arch/arm/mm/fault.c')
-rw-r--r-- | arch/arm/mm/fault.c | 169 |
1 files changed, 58 insertions, 111 deletions
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index aa33949fef60..bb7eac381a8e 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 | ||
@@ -231,7 +220,7 @@ static inline bool access_error(unsigned int fsr, struct vm_area_struct *vma) | |||
231 | 220 | ||
232 | static int __kprobes | 221 | static int __kprobes |
233 | __do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, | 222 | __do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, |
234 | struct task_struct *tsk) | 223 | unsigned int flags, struct task_struct *tsk) |
235 | { | 224 | { |
236 | struct vm_area_struct *vma; | 225 | struct vm_area_struct *vma; |
237 | int fault; | 226 | int fault; |
@@ -253,18 +242,7 @@ good_area: | |||
253 | goto out; | 242 | goto out; |
254 | } | 243 | } |
255 | 244 | ||
256 | /* | 245 | return handle_mm_fault(mm, vma, addr & PAGE_MASK, flags); |
257 | * If for any reason at all we couldn't handle the fault, make | ||
258 | * sure we exit gracefully rather than endlessly redo the fault. | ||
259 | */ | ||
260 | fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, (fsr & FSR_WRITE) ? FAULT_FLAG_WRITE : 0); | ||
261 | if (unlikely(fault & VM_FAULT_ERROR)) | ||
262 | return fault; | ||
263 | if (fault & VM_FAULT_MAJOR) | ||
264 | tsk->maj_flt++; | ||
265 | else | ||
266 | tsk->min_flt++; | ||
267 | return fault; | ||
268 | 246 | ||
269 | check_stack: | 247 | check_stack: |
270 | if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) | 248 | if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) |
@@ -279,6 +257,9 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
279 | struct task_struct *tsk; | 257 | struct task_struct *tsk; |
280 | struct mm_struct *mm; | 258 | struct mm_struct *mm; |
281 | int fault, sig, code; | 259 | int fault, sig, code; |
260 | int write = fsr & FSR_WRITE; | ||
261 | unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | | ||
262 | (write ? FAULT_FLAG_WRITE : 0); | ||
282 | 263 | ||
283 | if (notify_page_fault(regs, fsr)) | 264 | if (notify_page_fault(regs, fsr)) |
284 | return 0; | 265 | return 0; |
@@ -305,6 +286,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
305 | if (!down_read_trylock(&mm->mmap_sem)) { | 286 | if (!down_read_trylock(&mm->mmap_sem)) { |
306 | if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) | 287 | if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) |
307 | goto no_context; | 288 | goto no_context; |
289 | retry: | ||
308 | down_read(&mm->mmap_sem); | 290 | down_read(&mm->mmap_sem); |
309 | } else { | 291 | } else { |
310 | /* | 292 | /* |
@@ -320,14 +302,41 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
320 | #endif | 302 | #endif |
321 | } | 303 | } |
322 | 304 | ||
323 | fault = __do_page_fault(mm, addr, fsr, tsk); | 305 | fault = __do_page_fault(mm, addr, fsr, flags, tsk); |
324 | up_read(&mm->mmap_sem); | 306 | |
307 | /* If we need to retry but a fatal signal is pending, handle the | ||
308 | * signal first. We do not need to release the mmap_sem because | ||
309 | * it would already be released in __lock_page_or_retry in | ||
310 | * mm/filemap.c. */ | ||
311 | if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) | ||
312 | return 0; | ||
313 | |||
314 | /* | ||
315 | * Major/minor page fault accounting is only done on the | ||
316 | * initial attempt. If we go through a retry, it is extremely | ||
317 | * likely that the page will be found in page cache at that point. | ||
318 | */ | ||
325 | 319 | ||
326 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); | 320 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); |
327 | if (fault & VM_FAULT_MAJOR) | 321 | if (flags & FAULT_FLAG_ALLOW_RETRY) { |
328 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, addr); | 322 | if (fault & VM_FAULT_MAJOR) { |
329 | else if (fault & VM_FAULT_MINOR) | 323 | tsk->maj_flt++; |
330 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, addr); | 324 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, |
325 | regs, addr); | ||
326 | } else { | ||
327 | tsk->min_flt++; | ||
328 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, | ||
329 | regs, addr); | ||
330 | } | ||
331 | if (fault & VM_FAULT_RETRY) { | ||
332 | /* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk | ||
333 | * of starvation. */ | ||
334 | flags &= ~FAULT_FLAG_ALLOW_RETRY; | ||
335 | goto retry; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | up_read(&mm->mmap_sem); | ||
331 | 340 | ||
332 | /* | 341 | /* |
333 | * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR | 342 | * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR |
@@ -441,6 +450,12 @@ do_translation_fault(unsigned long addr, unsigned int fsr, | |||
441 | pmd = pmd_offset(pud, addr); | 450 | pmd = pmd_offset(pud, addr); |
442 | pmd_k = pmd_offset(pud_k, addr); | 451 | pmd_k = pmd_offset(pud_k, addr); |
443 | 452 | ||
453 | #ifdef CONFIG_ARM_LPAE | ||
454 | /* | ||
455 | * Only one hardware entry per PMD with LPAE. | ||
456 | */ | ||
457 | index = 0; | ||
458 | #else | ||
444 | /* | 459 | /* |
445 | * On ARM one Linux PGD entry contains two hardware entries (see page | 460 | * On ARM one Linux PGD entry contains two hardware entries (see page |
446 | * tables layout in pgtable.h). We normally guarantee that we always | 461 | * tables layout in pgtable.h). We normally guarantee that we always |
@@ -450,6 +465,7 @@ do_translation_fault(unsigned long addr, unsigned int fsr, | |||
450 | * for the first of pair. | 465 | * for the first of pair. |
451 | */ | 466 | */ |
452 | index = (addr >> SECTION_SHIFT) & 1; | 467 | index = (addr >> SECTION_SHIFT) & 1; |
468 | #endif | ||
453 | if (pmd_none(pmd_k[index])) | 469 | if (pmd_none(pmd_k[index])) |
454 | goto bad_area; | 470 | goto bad_area; |
455 | 471 | ||
@@ -489,55 +505,20 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
489 | return 1; | 505 | return 1; |
490 | } | 506 | } |
491 | 507 | ||
492 | static struct fsr_info { | 508 | struct fsr_info { |
493 | int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); | 509 | int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); |
494 | int sig; | 510 | int sig; |
495 | int code; | 511 | int code; |
496 | const char *name; | 512 | 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 | }; | 513 | }; |
540 | 514 | ||
515 | /* FSR definition */ | ||
516 | #ifdef CONFIG_ARM_LPAE | ||
517 | #include "fsr-3level.c" | ||
518 | #else | ||
519 | #include "fsr-2level.c" | ||
520 | #endif | ||
521 | |||
541 | void __init | 522 | void __init |
542 | hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), | 523 | hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), |
543 | int sig, int code, const char *name) | 524 | int sig, int code, const char *name) |
@@ -573,42 +554,6 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
573 | arm_notify_die("", regs, &info, fsr, 0); | 554 | arm_notify_die("", regs, &info, fsr, 0); |
574 | } | 555 | } |
575 | 556 | ||
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 | 557 | void __init |
613 | hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), | 558 | hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), |
614 | int sig, int code, const char *name) | 559 | int sig, int code, const char *name) |
@@ -641,6 +586,7 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) | |||
641 | arm_notify_die("", regs, &info, ifsr, 0); | 586 | arm_notify_die("", regs, &info, ifsr, 0); |
642 | } | 587 | } |
643 | 588 | ||
589 | #ifndef CONFIG_ARM_LPAE | ||
644 | static int __init exceptions_init(void) | 590 | static int __init exceptions_init(void) |
645 | { | 591 | { |
646 | if (cpu_architecture() >= CPU_ARCH_ARMv6) { | 592 | if (cpu_architecture() >= CPU_ARCH_ARMv6) { |
@@ -663,3 +609,4 @@ static int __init exceptions_init(void) | |||
663 | } | 609 | } |
664 | 610 | ||
665 | arch_initcall(exceptions_init); | 611 | arch_initcall(exceptions_init); |
612 | #endif | ||