Commit a5d17cd7 authored by David S. Miller's avatar David S. Miller

[SPARC64]: Trap kernel bogus program counter at fault time.

parent 3ec85d14
......@@ -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);
......
......@@ -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;
}
......
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