aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/mm/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/mm/fault.c')
-rw-r--r--arch/arm64/mm/fault.c61
1 files changed, 30 insertions, 31 deletions
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 2d115016feb4..c8c61b1eb479 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -384,40 +384,31 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
384#define VM_FAULT_BADACCESS 0x020000 384#define VM_FAULT_BADACCESS 0x020000
385 385
386static vm_fault_t __do_page_fault(struct mm_struct *mm, unsigned long addr, 386static vm_fault_t __do_page_fault(struct mm_struct *mm, unsigned long addr,
387 unsigned int mm_flags, unsigned long vm_flags, 387 unsigned int mm_flags, unsigned long vm_flags)
388 struct task_struct *tsk)
389{ 388{
390 struct vm_area_struct *vma; 389 struct vm_area_struct *vma = find_vma(mm, addr);
391 vm_fault_t fault;
392 390
393 vma = find_vma(mm, addr);
394 fault = VM_FAULT_BADMAP;
395 if (unlikely(!vma)) 391 if (unlikely(!vma))
396 goto out; 392 return VM_FAULT_BADMAP;
397 if (unlikely(vma->vm_start > addr))
398 goto check_stack;
399 393
400 /* 394 /*
401 * Ok, we have a good vm_area for this memory access, so we can handle 395 * Ok, we have a good vm_area for this memory access, so we can handle
402 * it. 396 * it.
403 */ 397 */
404good_area: 398 if (unlikely(vma->vm_start > addr)) {
399 if (!(vma->vm_flags & VM_GROWSDOWN))
400 return VM_FAULT_BADMAP;
401 if (expand_stack(vma, addr))
402 return VM_FAULT_BADMAP;
403 }
404
405 /* 405 /*
406 * Check that the permissions on the VMA allow for the fault which 406 * Check that the permissions on the VMA allow for the fault which
407 * occurred. 407 * occurred.
408 */ 408 */
409 if (!(vma->vm_flags & vm_flags)) { 409 if (!(vma->vm_flags & vm_flags))
410 fault = VM_FAULT_BADACCESS; 410 return VM_FAULT_BADACCESS;
411 goto out;
412 }
413
414 return handle_mm_fault(vma, addr & PAGE_MASK, mm_flags); 411 return handle_mm_fault(vma, addr & PAGE_MASK, mm_flags);
415
416check_stack:
417 if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
418 goto good_area;
419out:
420 return fault;
421} 412}
422 413
423static bool is_el0_instruction_abort(unsigned int esr) 414static bool is_el0_instruction_abort(unsigned int esr)
@@ -425,12 +416,20 @@ static bool is_el0_instruction_abort(unsigned int esr)
425 return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_LOW; 416 return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_LOW;
426} 417}
427 418
419/*
420 * Note: not valid for EL1 DC IVAC, but we never use that such that it
421 * should fault. EL0 cannot issue DC IVAC (undef).
422 */
423static bool is_write_abort(unsigned int esr)
424{
425 return (esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM);
426}
427
428static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, 428static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
429 struct pt_regs *regs) 429 struct pt_regs *regs)
430{ 430{
431 const struct fault_info *inf; 431 const struct fault_info *inf;
432 struct task_struct *tsk; 432 struct mm_struct *mm = current->mm;
433 struct mm_struct *mm;
434 vm_fault_t fault, major = 0; 433 vm_fault_t fault, major = 0;
435 unsigned long vm_flags = VM_READ | VM_WRITE; 434 unsigned long vm_flags = VM_READ | VM_WRITE;
436 unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; 435 unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
@@ -438,9 +437,6 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
438 if (notify_page_fault(regs, esr)) 437 if (notify_page_fault(regs, esr))
439 return 0; 438 return 0;
440 439
441 tsk = current;
442 mm = tsk->mm;
443
444 /* 440 /*
445 * If we're in an interrupt or have no user context, we must not take 441 * If we're in an interrupt or have no user context, we must not take
446 * the fault. 442 * the fault.
@@ -453,7 +449,8 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
453 449
454 if (is_el0_instruction_abort(esr)) { 450 if (is_el0_instruction_abort(esr)) {
455 vm_flags = VM_EXEC; 451 vm_flags = VM_EXEC;
456 } else if ((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) { 452 mm_flags |= FAULT_FLAG_INSTRUCTION;
453 } else if (is_write_abort(esr)) {
457 vm_flags = VM_WRITE; 454 vm_flags = VM_WRITE;
458 mm_flags |= FAULT_FLAG_WRITE; 455 mm_flags |= FAULT_FLAG_WRITE;
459 } 456 }
@@ -492,12 +489,14 @@ retry:
492 */ 489 */
493 might_sleep(); 490 might_sleep();
494#ifdef CONFIG_DEBUG_VM 491#ifdef CONFIG_DEBUG_VM
495 if (!user_mode(regs) && !search_exception_tables(regs->pc)) 492 if (!user_mode(regs) && !search_exception_tables(regs->pc)) {
493 up_read(&mm->mmap_sem);
496 goto no_context; 494 goto no_context;
495 }
497#endif 496#endif
498 } 497 }
499 498
500 fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk); 499 fault = __do_page_fault(mm, addr, mm_flags, vm_flags);
501 major |= fault & VM_FAULT_MAJOR; 500 major |= fault & VM_FAULT_MAJOR;
502 501
503 if (fault & VM_FAULT_RETRY) { 502 if (fault & VM_FAULT_RETRY) {
@@ -537,11 +536,11 @@ retry:
537 * that point. 536 * that point.
538 */ 537 */
539 if (major) { 538 if (major) {
540 tsk->maj_flt++; 539 current->maj_flt++;
541 perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, 540 perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs,
542 addr); 541 addr);
543 } else { 542 } else {
544 tsk->min_flt++; 543 current->min_flt++;
545 perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, 544 perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs,
546 addr); 545 addr);
547 } 546 }