diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 3a1f95a8f7307de38ea69336921d8afe7e66876c..ec590d41c383c1f355a6011a5d5fc2150d5fcf8b 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1566,17 +1566,14 @@ void user_instruction_dump (unsigned int *pc) printk("\n"); } -void show_trace_task(struct task_struct *tsk) +void show_trace_raw(struct thread_info *tp, unsigned long ksp) { - unsigned long pc, fp; - unsigned long thread_base = (unsigned long) tsk->thread_info; + unsigned long pc, fp, thread_base; struct reg_window *rw; int count = 0; - if (!tsk) - return; - - fp = tsk->thread_info->ksp + STACK_BIAS; + fp = ksp + STACK_BIAS; + thread_base = (unsigned long) tp; do { /* Bogus frame pointer? */ if (fp < (thread_base + sizeof(struct thread_info)) || @@ -1590,6 +1587,13 @@ void show_trace_task(struct task_struct *tsk) printk("\n"); } +void show_trace_task(struct task_struct *tsk) +{ + if (tsk) + show_trace_raw(tsk->thread_info, + tsk->thread_info->ksp); +} + void die_if_kernel(char *str, struct pt_regs *regs) { extern void __show_regs(struct pt_regs * regs); diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 3798f6d6781bbecc2d98c58806becb959943e225..f34184be0c71c1cd13e86b952852a1521abea28d 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -130,8 +130,8 @@ unsigned long __init prom_probe_memory (void) return tally; } -void unhandled_fault(unsigned long address, struct task_struct *tsk, - struct pt_regs *regs) +static void unhandled_fault(unsigned long address, struct task_struct *tsk, + struct pt_regs *regs) { if ((unsigned long) address < PAGE_SIZE) { printk(KERN_ALERT "Unable to handle kernel NULL " @@ -148,6 +148,19 @@ void unhandled_fault(unsigned long address, struct task_struct *tsk, die_if_kernel("Oops", regs); } +extern void show_trace_raw(struct thread_info *, unsigned long); + +static void bad_kernel_pc(struct pt_regs *regs) +{ + unsigned long ksp; + + printk(KERN_CRIT "OOPS: Bogus kernel PC [%016lx] in fault handler\n", + regs->tpc); + __asm__("mov %%sp, %0" : "=r" (ksp)); + show_trace_raw(current_thread_info(), ksp); + unhandled_fault(regs->tpc, current, regs); +} + /* * We now make sure that mmap_sem is held in all paths that call * this. Additionally, to prevent kswapd from ripping ptes from @@ -215,7 +228,7 @@ static inline unsigned int get_fault_insn(struct pt_regs *regs, unsigned int ins if (!regs->tpc || (regs->tpc & 0x3)) return 0; if (regs->tstate & TSTATE_PRIV) { - insn = *(unsigned int *)regs->tpc; + insn = *(unsigned int *) regs->tpc; } else { insn = get_user_insn(regs->tpc); } @@ -306,6 +319,20 @@ asmlinkage void do_sparc64_fault(struct pt_regs *regs) (fault_code & FAULT_CODE_DTLB)) BUG(); + if (regs->tstate & TSTATE_PRIV) { + unsigned long tpc = regs->tpc; + extern unsigned int _etext; + + /* Sanity check the PC. */ + if ((tpc >= KERNBASE && tpc < (unsigned long) &_etext) || + (tpc >= MODULES_VADDR && tpc < MODULES_END)) { + /* Valid, no problems... */ + } else { + bad_kernel_pc(regs); + return; + } + } + /* * If we're in an interrupt or have no user * context, we must not take the fault.. @@ -314,7 +341,8 @@ asmlinkage void do_sparc64_fault(struct pt_regs *regs) goto intr_or_no_mm; if (test_thread_flag(TIF_32BIT)) { - regs->tpc &= 0xffffffff; + if (!(regs->tstate & TSTATE_PRIV)) + regs->tpc &= 0xffffffff; address &= 0xffffffff; }