diff options
Diffstat (limited to 'arch/sparc/mm/fault_64.c')
| -rw-r--r-- | arch/sparc/mm/fault_64.c | 116 | 
1 files changed, 63 insertions, 53 deletions
diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c index 2ebec263d68..587cd056512 100644 --- a/arch/sparc/mm/fault_64.c +++ b/arch/sparc/mm/fault_64.c @@ -21,6 +21,7 @@  #include <linux/kprobes.h>  #include <linux/kdebug.h>  #include <linux/percpu.h> +#include <linux/context_tracking.h>  #include <asm/page.h>  #include <asm/pgtable.h> @@ -31,6 +32,7 @@  #include <asm/lsu.h>  #include <asm/sections.h>  #include <asm/mmu_context.h> +#include <asm/setup.h>  int show_unhandled_signals = 1; @@ -95,38 +97,51 @@ static unsigned int get_user_insn(unsigned long tpc)  	pte_t *ptep, pte;  	unsigned long pa;  	u32 insn = 0; -	unsigned long pstate; -	if (pgd_none(*pgdp)) -		goto outret; +	if (pgd_none(*pgdp) || unlikely(pgd_bad(*pgdp))) +		goto out;  	pudp = pud_offset(pgdp, tpc); -	if (pud_none(*pudp)) -		goto outret; -	pmdp = pmd_offset(pudp, tpc); -	if (pmd_none(*pmdp)) -		goto outret; +	if (pud_none(*pudp) || unlikely(pud_bad(*pudp))) +		goto out;  	/* This disables preemption for us as well. */ -	__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); -	__asm__ __volatile__("wrpr %0, %1, %%pstate" -				: : "r" (pstate), "i" (PSTATE_IE)); -	ptep = pte_offset_map(pmdp, tpc); -	pte = *ptep; -	if (!pte_present(pte)) -		goto out; +	local_irq_disable(); -	pa  = (pte_pfn(pte) << PAGE_SHIFT); -	pa += (tpc & ~PAGE_MASK); +	pmdp = pmd_offset(pudp, tpc); +	if (pmd_none(*pmdp) || unlikely(pmd_bad(*pmdp))) +		goto out_irq_enable; + +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +	if (pmd_trans_huge(*pmdp)) { +		if (pmd_trans_splitting(*pmdp)) +			goto out_irq_enable; -	/* Use phys bypass so we don't pollute dtlb/dcache. */ -	__asm__ __volatile__("lduwa [%1] %2, %0" -			     : "=r" (insn) -			     : "r" (pa), "i" (ASI_PHYS_USE_EC)); +		pa  = pmd_pfn(*pmdp) << PAGE_SHIFT; +		pa += tpc & ~HPAGE_MASK; +		/* Use phys bypass so we don't pollute dtlb/dcache. */ +		__asm__ __volatile__("lduwa [%1] %2, %0" +				     : "=r" (insn) +				     : "r" (pa), "i" (ASI_PHYS_USE_EC)); +	} else +#endif +	{ +		ptep = pte_offset_map(pmdp, tpc); +		pte = *ptep; +		if (pte_present(pte)) { +			pa  = (pte_pfn(pte) << PAGE_SHIFT); +			pa += (tpc & ~PAGE_MASK); + +			/* Use phys bypass so we don't pollute dtlb/dcache. */ +			__asm__ __volatile__("lduwa [%1] %2, %0" +					     : "=r" (insn) +					     : "r" (pa), "i" (ASI_PHYS_USE_EC)); +		} +		pte_unmap(ptep); +	} +out_irq_enable: +	local_irq_enable();  out: -	pte_unmap(ptep); -	__asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); -outret:  	return insn;  } @@ -152,7 +167,8 @@ show_signal_msg(struct pt_regs *regs, int sig, int code,  }  static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, -			     unsigned int insn, int fault_code) +			     unsigned long fault_addr, unsigned int insn, +			     int fault_code)  {  	unsigned long addr;  	siginfo_t info; @@ -160,10 +176,18 @@ static void do_fault_siginfo(int code, int sig, struct pt_regs *regs,  	info.si_code = code;  	info.si_signo = sig;  	info.si_errno = 0; -	if (fault_code & FAULT_CODE_ITLB) +	if (fault_code & FAULT_CODE_ITLB) {  		addr = regs->tpc; -	else -		addr = compute_effective_address(regs, insn, 0); +	} else { +		/* If we were able to probe the faulting instruction, use it +		 * to compute a precise fault address.  Otherwise use the fault +		 * time provided address which may only have page granularity. +		 */ +		if (insn) +			addr = compute_effective_address(regs, insn, 0); +		else +			addr = fault_addr; +	}  	info.si_addr = (void __user *) addr;  	info.si_trapno = 0; @@ -173,9 +197,6 @@ static void do_fault_siginfo(int code, int sig, struct pt_regs *regs,  	force_sig_info(sig, &info, current);  } -extern int handle_ldf_stq(u32, struct pt_regs *); -extern int handle_ld_nf(u32, struct pt_regs *); -  static unsigned int get_fault_insn(struct pt_regs *regs, unsigned int insn)  {  	if (!insn) { @@ -238,7 +259,7 @@ static void __kprobes do_kernel_fault(struct pt_regs *regs, int si_code,  		/* The si_code was set to make clear whether  		 * this was a SEGV_MAPERR or SEGV_ACCERR fault.  		 */ -		do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code); +		do_fault_siginfo(si_code, SIGSEGV, regs, address, insn, fault_code);  		return;  	} @@ -258,20 +279,9 @@ static void noinline __kprobes bogus_32bit_fault_tpc(struct pt_regs *regs)  	show_regs(regs);  } -static void noinline __kprobes bogus_32bit_fault_address(struct pt_regs *regs, -							 unsigned long addr) -{ -	static int times; - -	if (times++ < 10) -		printk(KERN_ERR "FAULT[%s:%d]: 32-bit process " -		       "reports 64-bit fault address [%lx]\n", -		       current->comm, current->pid, addr); -	show_regs(regs); -} -  asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)  { +	enum ctx_state prev_state = exception_enter();  	struct mm_struct *mm = current->mm;  	struct vm_area_struct *vma;  	unsigned int insn = 0; @@ -282,7 +292,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)  	fault_code = get_thread_fault_code();  	if (notify_page_fault(regs)) -		return; +		goto exit_exception;  	si_code = SEGV_MAPERR;  	address = current_thread_info()->fault_address; @@ -298,10 +308,8 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)  				goto intr_or_no_mm;  			}  		} -		if (unlikely((address >> 32) != 0)) { -			bogus_32bit_fault_address(regs, address); +		if (unlikely((address >> 32) != 0))  			goto intr_or_no_mm; -		}  	}  	if (regs->tstate & TSTATE_PRIV) { @@ -313,7 +321,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)  			/* Valid, no problems... */  		} else {  			bad_kernel_pc(regs, address); -			return; +			goto exit_exception;  		}  	} else  		flags |= FAULT_FLAG_USER; @@ -430,7 +438,7 @@ good_area:  	fault = handle_mm_fault(mm, vma, address, flags);  	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) -		return; +		goto exit_exception;  	if (unlikely(fault & VM_FAULT_ERROR)) {  		if (fault & VM_FAULT_OOM) @@ -482,6 +490,8 @@ good_area:  	}  #endif +exit_exception: +	exception_exit(prev_state);  	return;  	/* @@ -494,7 +504,7 @@ bad_area:  handle_kernel_fault:  	do_kernel_fault(regs, si_code, fault_code, insn, address); -	return; +	goto exit_exception;  /*   * We ran out of memory, or some other thing happened to us that made @@ -505,7 +515,7 @@ out_of_memory:  	up_read(&mm->mmap_sem);  	if (!(regs->tstate & TSTATE_PRIV)) {  		pagefault_out_of_memory(); -		return; +		goto exit_exception;  	}  	goto handle_kernel_fault; @@ -521,7 +531,7 @@ do_sigbus:  	 * Send a sigbus, regardless of whether we were in kernel  	 * or user mode.  	 */ -	do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code); +	do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, address, insn, fault_code);  	/* Kernel mode? Handle exceptions or die */  	if (regs->tstate & TSTATE_PRIV)  | 
