diff options
Diffstat (limited to 'arch/arm64/mm/fault.c')
-rw-r--r-- | arch/arm64/mm/fault.c | 61 |
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 | ||
386 | static vm_fault_t __do_page_fault(struct mm_struct *mm, unsigned long addr, | 386 | static 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 | */ |
404 | good_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 | |||
416 | check_stack: | ||
417 | if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) | ||
418 | goto good_area; | ||
419 | out: | ||
420 | return fault; | ||
421 | } | 412 | } |
422 | 413 | ||
423 | static bool is_el0_instruction_abort(unsigned int esr) | 414 | static 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 | */ | ||
423 | static bool is_write_abort(unsigned int esr) | ||
424 | { | ||
425 | return (esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM); | ||
426 | } | ||
427 | |||
428 | static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, | 428 | static 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 | } |