From 5a1dc78a38bfb04159a08cd493e5b3d844939e6c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 14 May 2012 14:57:28 +0900 Subject: sh: Support thread fault code encoding. This provides a simple interface modelled after sparc64/m32r to encode the error code in the upper byte of thread_info for finer-grained handling in the page fault path. Signed-off-by: Paul Mundt --- arch/sh/mm/fault_32.c | 68 +++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 32 deletions(-) (limited to 'arch/sh/mm') diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index 889e83b..a469b95 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -211,7 +211,7 @@ show_fault_oops(struct pt_regs *regs, unsigned long address) } static noinline void -no_context(struct pt_regs *regs, unsigned long writeaccess, +no_context(struct pt_regs *regs, unsigned long error_code, unsigned long address) { /* Are we prepared to handle this kernel fault? */ @@ -229,13 +229,13 @@ no_context(struct pt_regs *regs, unsigned long writeaccess, show_fault_oops(regs, address); - die("Oops", regs, writeaccess); + die("Oops", regs, error_code); bust_spinlocks(0); do_exit(SIGKILL); } static void -__bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess, +__bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code, unsigned long address, int si_code) { struct task_struct *tsk = current; @@ -252,18 +252,18 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess, return; } - no_context(regs, writeaccess, address); + no_context(regs, error_code, address); } static noinline void -bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess, +bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code, unsigned long address) { - __bad_area_nosemaphore(regs, writeaccess, address, SEGV_MAPERR); + __bad_area_nosemaphore(regs, error_code, address, SEGV_MAPERR); } static void -__bad_area(struct pt_regs *regs, unsigned long writeaccess, +__bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address, int si_code) { struct mm_struct *mm = current->mm; @@ -274,20 +274,20 @@ __bad_area(struct pt_regs *regs, unsigned long writeaccess, */ up_read(&mm->mmap_sem); - __bad_area_nosemaphore(regs, writeaccess, address, si_code); + __bad_area_nosemaphore(regs, error_code, address, si_code); } static noinline void -bad_area(struct pt_regs *regs, unsigned long writeaccess, unsigned long address) +bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address) { - __bad_area(regs, writeaccess, address, SEGV_MAPERR); + __bad_area(regs, error_code, address, SEGV_MAPERR); } static noinline void -bad_area_access_error(struct pt_regs *regs, unsigned long writeaccess, +bad_area_access_error(struct pt_regs *regs, unsigned long error_code, unsigned long address) { - __bad_area(regs, writeaccess, address, SEGV_ACCERR); + __bad_area(regs, error_code, address, SEGV_ACCERR); } static void out_of_memory(void) @@ -302,7 +302,7 @@ static void out_of_memory(void) } static void -do_sigbus(struct pt_regs *regs, unsigned long writeaccess, unsigned long address) +do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address) { struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; @@ -311,13 +311,13 @@ do_sigbus(struct pt_regs *regs, unsigned long writeaccess, unsigned long address /* Kernel mode? Handle exceptions or die: */ if (!user_mode(regs)) - no_context(regs, writeaccess, address); + no_context(regs, error_code, address); force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk); } static noinline int -mm_fault_error(struct pt_regs *regs, unsigned long writeaccess, +mm_fault_error(struct pt_regs *regs, unsigned long error_code, unsigned long address, unsigned int fault) { /* @@ -328,7 +328,7 @@ mm_fault_error(struct pt_regs *regs, unsigned long writeaccess, if (!(fault & VM_FAULT_RETRY)) up_read(¤t->mm->mmap_sem); if (!user_mode(regs)) - no_context(regs, writeaccess, address); + no_context(regs, error_code, address); return 1; } @@ -339,14 +339,14 @@ mm_fault_error(struct pt_regs *regs, unsigned long writeaccess, /* Kernel mode? Handle exceptions or die: */ if (!user_mode(regs)) { up_read(¤t->mm->mmap_sem); - no_context(regs, writeaccess, address); + no_context(regs, error_code, address); return 1; } out_of_memory(); } else { if (fault & VM_FAULT_SIGBUS) - do_sigbus(regs, writeaccess, address); + do_sigbus(regs, error_code, address); else BUG(); } @@ -381,7 +381,7 @@ static int fault_in_kernel_space(unsigned long address) * routines. */ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, - unsigned long writeaccess, + unsigned long error_code, unsigned long address) { unsigned long vec; @@ -389,8 +389,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, struct mm_struct *mm; struct vm_area_struct * vma; int fault; + int write = error_code & FAULT_CODE_WRITE; unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | - (writeaccess ? FAULT_FLAG_WRITE : 0)); + (write ? FAULT_FLAG_WRITE : 0)); tsk = current; mm = tsk->mm; @@ -411,7 +412,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, if (notify_page_fault(regs, vec)) return; - bad_area_nosemaphore(regs, writeaccess, address); + bad_area_nosemaphore(regs, error_code, address); return; } @@ -429,7 +430,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, * in an atomic region then we must not take the fault: */ if (unlikely(in_atomic() || !mm)) { - bad_area_nosemaphore(regs, writeaccess, address); + bad_area_nosemaphore(regs, error_code, address); return; } @@ -438,17 +439,17 @@ retry: vma = find_vma(mm, address); if (unlikely(!vma)) { - bad_area(regs, writeaccess, address); + bad_area(regs, error_code, address); return; } if (likely(vma->vm_start <= address)) goto good_area; if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) { - bad_area(regs, writeaccess, address); + bad_area(regs, error_code, address); return; } if (unlikely(expand_stack(vma, address))) { - bad_area(regs, writeaccess, address); + bad_area(regs, error_code, address); return; } @@ -457,11 +458,13 @@ retry: * we can handle it.. */ good_area: - if (unlikely(access_error(writeaccess, vma))) { - bad_area_access_error(regs, writeaccess, address); + if (unlikely(access_error(error_code, vma))) { + bad_area_access_error(regs, error_code, address); return; } + set_thread_fault_code(error_code); + /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo @@ -470,7 +473,7 @@ good_area: fault = handle_mm_fault(mm, vma, address, flags); if (unlikely(fault & (VM_FAULT_RETRY | VM_FAULT_ERROR))) - if (mm_fault_error(regs, writeaccess, address, fault)) + if (mm_fault_error(regs, error_code, address, fault)) return; if (flags & FAULT_FLAG_ALLOW_RETRY) { @@ -502,7 +505,7 @@ good_area: * Called with interrupts disabled. */ asmlinkage int __kprobes -handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess, +handle_tlbmiss(struct pt_regs *regs, unsigned long error_code, unsigned long address) { pgd_t *pgd; @@ -535,10 +538,10 @@ handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess, entry = *pte; if (unlikely(pte_none(entry) || pte_not_present(entry))) return 1; - if (unlikely(writeaccess && !pte_write(entry))) + if (unlikely(error_code && !pte_write(entry))) return 1; - if (writeaccess) + if (error_code) entry = pte_mkdirty(entry); entry = pte_mkyoung(entry); @@ -550,10 +553,11 @@ handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess, * the case of an initial page write exception, so we need to * flush it in order to avoid potential TLB entry duplication. */ - if (writeaccess == 2) + if (error_code == FAULT_CODE_INITIAL) local_flush_tlb_one(get_asid(), address & PAGE_MASK); #endif + set_thread_fault_code(error_code); update_mmu_cache(NULL, address, pte); return 0; -- cgit v1.1