diff options
Diffstat (limited to 'arch/x86/mm/fault.c')
-rw-r--r-- | arch/x86/mm/fault.c | 62 |
1 files changed, 31 insertions, 31 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index e4440d0abf81..621afb6343dc 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -240,7 +240,8 @@ void dump_pagetable(unsigned long address) | |||
240 | pud = pud_offset(pgd, address); | 240 | pud = pud_offset(pgd, address); |
241 | if (bad_address(pud)) goto bad; | 241 | if (bad_address(pud)) goto bad; |
242 | printk("PUD %lx ", pud_val(*pud)); | 242 | printk("PUD %lx ", pud_val(*pud)); |
243 | if (!pud_present(*pud)) goto ret; | 243 | if (!pud_present(*pud) || pud_large(*pud)) |
244 | goto ret; | ||
244 | 245 | ||
245 | pmd = pmd_offset(pud, address); | 246 | pmd = pmd_offset(pud, address); |
246 | if (bad_address(pmd)) goto bad; | 247 | if (bad_address(pmd)) goto bad; |
@@ -427,6 +428,16 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs, | |||
427 | } | 428 | } |
428 | #endif | 429 | #endif |
429 | 430 | ||
431 | static int spurious_fault_check(unsigned long error_code, pte_t *pte) | ||
432 | { | ||
433 | if ((error_code & PF_WRITE) && !pte_write(*pte)) | ||
434 | return 0; | ||
435 | if ((error_code & PF_INSTR) && !pte_exec(*pte)) | ||
436 | return 0; | ||
437 | |||
438 | return 1; | ||
439 | } | ||
440 | |||
430 | /* | 441 | /* |
431 | * Handle a spurious fault caused by a stale TLB entry. This allows | 442 | * Handle a spurious fault caused by a stale TLB entry. This allows |
432 | * us to lazily refresh the TLB when increasing the permissions of a | 443 | * us to lazily refresh the TLB when increasing the permissions of a |
@@ -456,20 +467,21 @@ static int spurious_fault(unsigned long address, | |||
456 | if (!pud_present(*pud)) | 467 | if (!pud_present(*pud)) |
457 | return 0; | 468 | return 0; |
458 | 469 | ||
470 | if (pud_large(*pud)) | ||
471 | return spurious_fault_check(error_code, (pte_t *) pud); | ||
472 | |||
459 | pmd = pmd_offset(pud, address); | 473 | pmd = pmd_offset(pud, address); |
460 | if (!pmd_present(*pmd)) | 474 | if (!pmd_present(*pmd)) |
461 | return 0; | 475 | return 0; |
462 | 476 | ||
477 | if (pmd_large(*pmd)) | ||
478 | return spurious_fault_check(error_code, (pte_t *) pmd); | ||
479 | |||
463 | pte = pte_offset_kernel(pmd, address); | 480 | pte = pte_offset_kernel(pmd, address); |
464 | if (!pte_present(*pte)) | 481 | if (!pte_present(*pte)) |
465 | return 0; | 482 | return 0; |
466 | 483 | ||
467 | if ((error_code & PF_WRITE) && !pte_write(*pte)) | 484 | return spurious_fault_check(error_code, pte); |
468 | return 0; | ||
469 | if ((error_code & PF_INSTR) && !pte_exec(*pte)) | ||
470 | return 0; | ||
471 | |||
472 | return 1; | ||
473 | } | 485 | } |
474 | 486 | ||
475 | /* | 487 | /* |
@@ -508,6 +520,10 @@ static int vmalloc_fault(unsigned long address) | |||
508 | pmd_t *pmd, *pmd_ref; | 520 | pmd_t *pmd, *pmd_ref; |
509 | pte_t *pte, *pte_ref; | 521 | pte_t *pte, *pte_ref; |
510 | 522 | ||
523 | /* Make sure we are in vmalloc area */ | ||
524 | if (!(address >= VMALLOC_START && address < VMALLOC_END)) | ||
525 | return -1; | ||
526 | |||
511 | /* Copy kernel mappings over when needed. This can also | 527 | /* Copy kernel mappings over when needed. This can also |
512 | happen within a race in page table update. In the later | 528 | happen within a race in page table update. In the later |
513 | case just flush. */ | 529 | case just flush. */ |
@@ -603,6 +619,9 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
603 | */ | 619 | */ |
604 | #ifdef CONFIG_X86_32 | 620 | #ifdef CONFIG_X86_32 |
605 | if (unlikely(address >= TASK_SIZE)) { | 621 | if (unlikely(address >= TASK_SIZE)) { |
622 | #else | ||
623 | if (unlikely(address >= TASK_SIZE64)) { | ||
624 | #endif | ||
606 | if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) && | 625 | if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) && |
607 | vmalloc_fault(address) >= 0) | 626 | vmalloc_fault(address) >= 0) |
608 | return; | 627 | return; |
@@ -618,6 +637,8 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
618 | goto bad_area_nosemaphore; | 637 | goto bad_area_nosemaphore; |
619 | } | 638 | } |
620 | 639 | ||
640 | |||
641 | #ifdef CONFIG_X86_32 | ||
621 | /* It's safe to allow irq's after cr2 has been saved and the vmalloc | 642 | /* It's safe to allow irq's after cr2 has been saved and the vmalloc |
622 | fault has been handled. */ | 643 | fault has been handled. */ |
623 | if (regs->flags & (X86_EFLAGS_IF|VM_MASK)) | 644 | if (regs->flags & (X86_EFLAGS_IF|VM_MASK)) |
@@ -630,28 +651,6 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
630 | if (in_atomic() || !mm) | 651 | if (in_atomic() || !mm) |
631 | goto bad_area_nosemaphore; | 652 | goto bad_area_nosemaphore; |
632 | #else /* CONFIG_X86_64 */ | 653 | #else /* CONFIG_X86_64 */ |
633 | if (unlikely(address >= TASK_SIZE64)) { | ||
634 | /* | ||
635 | * Don't check for the module range here: its PML4 | ||
636 | * is always initialized because it's shared with the main | ||
637 | * kernel text. Only vmalloc may need PML4 syncups. | ||
638 | */ | ||
639 | if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) && | ||
640 | ((address >= VMALLOC_START && address < VMALLOC_END))) { | ||
641 | if (vmalloc_fault(address) >= 0) | ||
642 | return; | ||
643 | } | ||
644 | |||
645 | /* Can handle a stale RO->RW TLB */ | ||
646 | if (spurious_fault(address, error_code)) | ||
647 | return; | ||
648 | |||
649 | /* | ||
650 | * Don't take the mm semaphore here. If we fixup a prefetch | ||
651 | * fault we could otherwise deadlock. | ||
652 | */ | ||
653 | goto bad_area_nosemaphore; | ||
654 | } | ||
655 | if (likely(regs->flags & X86_EFLAGS_IF)) | 654 | if (likely(regs->flags & X86_EFLAGS_IF)) |
656 | local_irq_enable(); | 655 | local_irq_enable(); |
657 | 656 | ||
@@ -959,11 +958,12 @@ void vmalloc_sync_all(void) | |||
959 | for (address = start; address <= VMALLOC_END; address += PGDIR_SIZE) { | 958 | for (address = start; address <= VMALLOC_END; address += PGDIR_SIZE) { |
960 | if (!test_bit(pgd_index(address), insync)) { | 959 | if (!test_bit(pgd_index(address), insync)) { |
961 | const pgd_t *pgd_ref = pgd_offset_k(address); | 960 | const pgd_t *pgd_ref = pgd_offset_k(address); |
961 | unsigned long flags; | ||
962 | struct page *page; | 962 | struct page *page; |
963 | 963 | ||
964 | if (pgd_none(*pgd_ref)) | 964 | if (pgd_none(*pgd_ref)) |
965 | continue; | 965 | continue; |
966 | spin_lock(&pgd_lock); | 966 | spin_lock_irqsave(&pgd_lock, flags); |
967 | list_for_each_entry(page, &pgd_list, lru) { | 967 | list_for_each_entry(page, &pgd_list, lru) { |
968 | pgd_t *pgd; | 968 | pgd_t *pgd; |
969 | pgd = (pgd_t *)page_address(page) + pgd_index(address); | 969 | pgd = (pgd_t *)page_address(page) + pgd_index(address); |
@@ -972,7 +972,7 @@ void vmalloc_sync_all(void) | |||
972 | else | 972 | else |
973 | BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref)); | 973 | BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref)); |
974 | } | 974 | } |
975 | spin_unlock(&pgd_lock); | 975 | spin_unlock_irqrestore(&pgd_lock, flags); |
976 | set_bit(pgd_index(address), insync); | 976 | set_bit(pgd_index(address), insync); |
977 | } | 977 | } |
978 | if (address == start) | 978 | if (address == start) |