]> err.no Git - linux-2.6/blobdiff - arch/sparc64/mm/fault.c
Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfashe...
[linux-2.6] / arch / sparc64 / mm / fault.c
index 6e002aacb961832113ba30d522d437460ee20612..17123e9ecf78dcdb8547f6dc51b9be8900a59bca 100644 (file)
 #include <linux/signal.h>
 #include <linux/mm.h>
 #include <linux/module.h>
-#include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kprobes.h>
+#include <linux/kallsyms.h>
+#include <linux/kdebug.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/asi.h>
 #include <asm/lsu.h>
 #include <asm/sections.h>
-#include <asm/kdebug.h>
 #include <asm/mmu_context.h>
 
+#ifdef CONFIG_KPROBES
+static inline int notify_page_fault(struct pt_regs *regs)
+{
+       int ret = 0;
+
+       /* kprobe_running() needs smp_processor_id() */
+       if (!user_mode(regs)) {
+               preempt_disable();
+               if (kprobe_running() && kprobe_fault_handler(regs, 0))
+                       ret = 1;
+               preempt_enable();
+       }
+       return ret;
+}
+#else
+static inline int notify_page_fault(struct pt_regs *regs)
+{
+       return 0;
+}
+#endif
+
 /*
  * To debug kernel to catch accesses to certain virtual/physical addresses.
  * Mode = 0 selects physical watchpoints, mode = 1 selects virtual watchpoints.
@@ -86,9 +107,6 @@ static void __kprobes unhandled_fault(unsigned long address,
        printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %016lx\n",
               (tsk->mm ? (unsigned long) tsk->mm->pgd :
                          (unsigned long) tsk->active_mm->pgd));
-       if (notify_die(DIE_GPF, "general protection fault", regs,
-                      0, 0, SIGSEGV) == NOTIFY_STOP)
-               return;
        die_if_kernel("Oops", regs);
 }
 
@@ -98,6 +116,8 @@ static void bad_kernel_pc(struct pt_regs *regs, unsigned long vaddr)
 
        printk(KERN_CRIT "OOPS: Bogus kernel PC [%016lx] in fault handler\n",
               regs->tpc);
+       printk(KERN_CRIT "OOPS: RPC [%016lx]\n", regs->u_regs[15]);
+       print_symbol("RPC: <%s>\n", regs->u_regs[15]);
        printk(KERN_CRIT "OOPS: Fault was to vaddr[%lx]\n", vaddr);
        __asm__("mov %%sp, %0" : "=r" (ksp));
        show_stack(current, ksp);
@@ -258,13 +278,12 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
        unsigned int insn = 0;
-       int si_code, fault_code;
+       int si_code, fault_code, fault;
        unsigned long address, mm_rss;
 
        fault_code = get_thread_fault_code();
 
-       if (notify_die(DIE_PAGE_FAULT, "page_fault", regs,
-                      fault_code, 0, SIGSEGV) == NOTIFY_STOP)
+       if (notify_page_fault(regs))
                return;
 
        si_code = SEGV_MAPERR;
@@ -396,20 +415,18 @@ good_area:
                        goto bad_area;
        }
 
-       switch (handle_mm_fault(mm, vma, address, (fault_code & FAULT_CODE_WRITE))) {
-       case VM_FAULT_MINOR:
-               current->min_flt++;
-               break;
-       case VM_FAULT_MAJOR:
-               current->maj_flt++;
-               break;
-       case VM_FAULT_SIGBUS:
-               goto do_sigbus;
-       case VM_FAULT_OOM:
-               goto out_of_memory;
-       default:
+       fault = handle_mm_fault(mm, vma, address, (fault_code & FAULT_CODE_WRITE));
+       if (unlikely(fault & VM_FAULT_ERROR)) {
+               if (fault & VM_FAULT_OOM)
+                       goto out_of_memory;
+               else if (fault & VM_FAULT_SIGBUS)
+                       goto do_sigbus;
                BUG();
        }
+       if (fault & VM_FAULT_MAJOR)
+               current->maj_flt++;
+       else
+               current->min_flt++;
 
        up_read(&mm->mmap_sem);