]> git.hungrycats.org Git - linux/commitdiff
[SPARC64]: Trap kernel bogus program counter at fault time.
authorDavid S. Miller <davem@nuts.ninka.net>
Thu, 19 Sep 2002 12:35:10 +0000 (05:35 -0700)
committerDavid S. Miller <davem@nuts.ninka.net>
Thu, 19 Sep 2002 12:35:10 +0000 (05:35 -0700)
arch/sparc64/kernel/traps.c
arch/sparc64/mm/fault.c

index 3a1f95a8f7307de38ea69336921d8afe7e66876c..ec590d41c383c1f355a6011a5d5fc2150d5fcf8b 100644 (file)
@@ -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);
index 3798f6d6781bbecc2d98c58806becb959943e225..f34184be0c71c1cd13e86b952852a1521abea28d 100644 (file)
@@ -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;
        }