Merge git://git.infradead.org/~dwmw2/random-2.6
[pandora-kernel.git] / arch / powerpc / kernel / kprobes.c
index 5338e48..de79915 100644 (file)
 #include <asm/cacheflush.h>
 #include <asm/sstep.h>
 #include <asm/uaccess.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_BOOKE
+#define MSR_SINGLESTEP (MSR_DE)
+#else
+#define MSR_SINGLESTEP (MSR_SE)
+#endif
 
 DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
@@ -53,7 +60,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
                ret = -EINVAL;
        }
 
-       /* insn must be on a special executable page on ppc64 */
+       /* insn must be on a special executable page on ppc64.  This is
+        * not explicitly required on ppc32 (right now), but it doesn't hurt */
        if (!ret) {
                p->ainsn.insn = get_insn_slot();
                if (!p->ainsn.insn)
@@ -95,7 +103,16 @@ void __kprobes arch_remove_kprobe(struct kprobe *p)
 
 static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
 {
-       regs->msr |= MSR_SE;
+       /* We turn off async exceptions to ensure that the single step will
+        * be for the instruction we have the kprobe on, if we dont its
+        * possible we'd get the single step reported for an exception handler
+        * like Decrementer or External Interrupt */
+       regs->msr &= ~MSR_EE;
+       regs->msr |= MSR_SINGLESTEP;
+#ifdef CONFIG_BOOKE
+       regs->msr &= ~MSR_CE;
+       mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC | DBCR0_IDM);
+#endif
 
        /*
         * On powerpc we should single step on the original
@@ -127,7 +144,6 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
        kcb->kprobe_saved_msr = regs->msr;
 }
 
-/* Called with kretprobe_lock held */
 void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
                                      struct pt_regs *regs)
 {
@@ -158,7 +174,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
                        kprobe_opcode_t insn = *p->ainsn.insn;
                        if (kcb->kprobe_status == KPROBE_HIT_SS &&
                                        is_trap(insn)) {
-                               regs->msr &= ~MSR_SE;
+                               /* Turn off 'trace' bits */
+                               regs->msr &= ~MSR_SINGLESTEP;
                                regs->msr |= kcb->kprobe_saved_msr;
                                goto no_kprobe;
                        }
@@ -274,7 +291,7 @@ no_kprobe:
  *     - When the probed function returns, this probe
  *             causes the handlers to fire
  */
-void kretprobe_trampoline_holder(void)
+static void __used kretprobe_trampoline_holder(void)
 {
        asm volatile(".global kretprobe_trampoline\n"
                        "kretprobe_trampoline:\n"
@@ -284,7 +301,8 @@ void kretprobe_trampoline_holder(void)
 /*
  * Called when the probe at kretprobe trampoline is hit
  */
-int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
+static int __kprobes trampoline_probe_handler(struct kprobe *p,
+                                               struct pt_regs *regs)
 {
        struct kretprobe_instance *ri = NULL;
        struct hlist_head *head, empty_rp;
@@ -293,8 +311,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
        unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
 
        INIT_HLIST_HEAD(&empty_rp);
-       spin_lock_irqsave(&kretprobe_lock, flags);
-       head = kretprobe_inst_table_head(current);
+       kretprobe_hash_lock(current, &head, &flags);
 
        /*
         * It is possible to have multiple instances associated with a given
@@ -333,7 +350,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
        regs->nip = orig_ret_address;
 
        reset_current_kprobe();
-       spin_unlock_irqrestore(&kretprobe_lock, flags);
+       kretprobe_hash_unlock(current, &flags);
        preempt_enable_no_resched();
 
        hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
@@ -375,6 +392,10 @@ static int __kprobes post_kprobe_handler(struct pt_regs *regs)
        if (!cur)
                return 0;
 
+       /* make sure we got here for instruction we have a kprobe on */
+       if (((unsigned long)cur->ainsn.insn + 4) != regs->nip)
+               return 0;
+
        if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
                kcb->kprobe_status = KPROBE_HIT_SSDONE;
                cur->post_handler(cur, regs, 0);
@@ -394,10 +415,10 @@ out:
 
        /*
         * if somebody else is singlestepping across a probe point, msr
-        * will have SE set, in which case, continue the remaining processing
+        * will have DE/SE set, in which case, continue the remaining processing
         * of do_debug, as if this is not a probe hit.
         */
-       if (regs->msr & MSR_SE)
+       if (regs->msr & MSR_SINGLESTEP)
                return 0;
 
        return 1;
@@ -420,7 +441,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
                 * normal page fault.
                 */
                regs->nip = (unsigned long)cur->addr;
-               regs->msr &= ~MSR_SE;
+               regs->msr &= ~MSR_SINGLESTEP; /* Turn off 'trace' bits */
                regs->msr |= kcb->kprobe_saved_msr;
                if (kcb->kprobe_status == KPROBE_REENTER)
                        restore_previous_kprobe(kcb);
@@ -497,7 +518,7 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
 #ifdef CONFIG_PPC64
 unsigned long arch_deref_entry_point(void *entry)
 {
-       return (unsigned long)(((func_descr_t *)entry)->entry);
+       return ((func_descr_t *)entry)->entry;
 }
 #endif
 
@@ -517,12 +538,12 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
        return 1;
 }
 
-void __kprobes jprobe_return(void)
+void __used __kprobes jprobe_return(void)
 {
        asm volatile("trap" ::: "memory");
 }
 
-void __kprobes jprobe_return_end(void)
+static void __used __kprobes jprobe_return_end(void)
 {
 };