mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-17 01:04:19 +08:00
sparc64: Fix top-level fault handling bugs.
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d037d16372
commit
70ffc6ebae
@ -96,26 +96,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;
|
|
||||||
pmdp = pmd_offset(pudp, tpc);
|
|
||||||
if (pmd_none(*pmdp))
|
|
||||||
goto outret;
|
|
||||||
|
|
||||||
/* 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;
|
goto out;
|
||||||
|
|
||||||
|
/* This disables preemption for us as well. */
|
||||||
|
local_irq_disable();
|
||||||
|
|
||||||
|
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);
|
||||||
|
pte = *ptep;
|
||||||
|
if (pte_present(pte)) {
|
||||||
pa = (pte_pfn(pte) << PAGE_SHIFT);
|
pa = (pte_pfn(pte) << PAGE_SHIFT);
|
||||||
pa += (tpc & ~PAGE_MASK);
|
pa += (tpc & ~PAGE_MASK);
|
||||||
|
|
||||||
@ -123,11 +135,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +166,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,
|
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;
|
||||||
@ -161,10 +175,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;
|
||||||
|
|
||||||
@ -239,7 +261,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +547,7 @@ do_sigbus:
|
|||||||
* 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)
|
||||||
|
Loading…
Reference in New Issue
Block a user