Commit 86982cf4 authored by David S. Miller's avatar David S. Miller Committed by Greg Kroah-Hartman

sparc64: Fix top-level fault handling bugs.

[ Upstream commit 70ffc6eb ]

Make get_user_insn() able to cope with huge PMDs.

Next, make do_fault_siginfo() more robust when get_user_insn() can't
actually fetch the instruction.  In particular, use the MMU announced
fault address when that happens, instead of calling
compute_effective_address() and computing garbage.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d2671e68
...@@ -95,26 +95,38 @@ static unsigned int get_user_insn(unsigned long tpc) ...@@ -95,26 +95,38 @@ static unsigned int get_user_insn(unsigned long tpc)
pte_t *ptep, pte; pte_t *ptep, pte;
unsigned long pa; unsigned long pa;
u32 insn = 0; u32 insn = 0;
unsigned long pstate;
if (pgd_none(*pgdp)) if (pgd_none(*pgdp) || unlikely(pgd_bad(*pgdp)))
goto outret; goto out;
pudp = pud_offset(pgdp, tpc); pudp = pud_offset(pgdp, tpc);
if (pud_none(*pudp)) if (pud_none(*pudp) || unlikely(pud_bad(*pudp)))
goto outret; goto out;
pmdp = pmd_offset(pudp, tpc);
if (pmd_none(*pmdp))
goto outret;
/* This disables preemption for us as well. */ /* This disables preemption for us as well. */
__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); local_irq_disable();
__asm__ __volatile__("wrpr %0, %1, %%pstate"
: : "r" (pstate), "i" (PSTATE_IE)); 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;
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); ptep = pte_offset_map(pmdp, tpc);
pte = *ptep; pte = *ptep;
if (!pte_present(pte)) if (pte_present(pte)) {
goto out;
pa = (pte_pfn(pte) << PAGE_SHIFT); pa = (pte_pfn(pte) << PAGE_SHIFT);
pa += (tpc & ~PAGE_MASK); pa += (tpc & ~PAGE_MASK);
...@@ -122,11 +134,12 @@ static unsigned int get_user_insn(unsigned long tpc) ...@@ -122,11 +134,12 @@ static unsigned int get_user_insn(unsigned long tpc)
__asm__ __volatile__("lduwa [%1] %2, %0" __asm__ __volatile__("lduwa [%1] %2, %0"
: "=r" (insn) : "=r" (insn)
: "r" (pa), "i" (ASI_PHYS_USE_EC)); : "r" (pa), "i" (ASI_PHYS_USE_EC));
}
out:
pte_unmap(ptep); pte_unmap(ptep);
__asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); }
outret: out_irq_enable:
local_irq_enable();
out:
return insn; return insn;
} }
...@@ -154,7 +167,8 @@ show_signal_msg(struct pt_regs *regs, int sig, int code, ...@@ -154,7 +167,8 @@ show_signal_msg(struct pt_regs *regs, int sig, int code,
extern unsigned long compute_effective_address(struct pt_regs *, unsigned int, unsigned int); extern unsigned long compute_effective_address(struct pt_regs *, unsigned int, unsigned int);
static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, 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; unsigned long addr;
siginfo_t info; siginfo_t info;
...@@ -162,10 +176,18 @@ static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, ...@@ -162,10 +176,18 @@ static void do_fault_siginfo(int code, int sig, struct pt_regs *regs,
info.si_code = code; info.si_code = code;
info.si_signo = sig; info.si_signo = sig;
info.si_errno = 0; info.si_errno = 0;
if (fault_code & FAULT_CODE_ITLB) if (fault_code & FAULT_CODE_ITLB) {
addr = regs->tpc; addr = regs->tpc;
else } 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); addr = compute_effective_address(regs, insn, 0);
else
addr = fault_addr;
}
info.si_addr = (void __user *) addr; info.si_addr = (void __user *) addr;
info.si_trapno = 0; info.si_trapno = 0;
...@@ -240,7 +262,7 @@ static void __kprobes do_kernel_fault(struct pt_regs *regs, int si_code, ...@@ -240,7 +262,7 @@ static void __kprobes do_kernel_fault(struct pt_regs *regs, int si_code,
/* The si_code was set to make clear whether /* The si_code was set to make clear whether
* this was a SEGV_MAPERR or SEGV_ACCERR fault. * 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; return;
} }
...@@ -515,7 +537,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) ...@@ -515,7 +537,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
* Send a sigbus, regardless of whether we were in kernel * Send a sigbus, regardless of whether we were in kernel
* or user mode. * 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 */ /* Kernel mode? Handle exceptions or die */
if (regs->tstate & TSTATE_PRIV) if (regs->tstate & TSTATE_PRIV)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment