powerpc/booke: Add kprobes support for booke style processors
authorKumar Gala <galak@kernel.crashing.org>
Thu, 26 Jun 2008 07:01:37 +0000 (02:01 -0500)
committerKumar Gala <galak@kernel.crashing.org>
Thu, 26 Jun 2008 08:35:46 +0000 (03:35 -0500)
This patch is based on work done by Madhvesh. R. Sulibhavi back in
March 2007.

We refactor some of the single step handling since it differs between
"classic" and "booke" powerpc cores.

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Documentation/kprobes.txt
arch/powerpc/kernel/kprobes.c
arch/powerpc/kernel/traps.c

index 6877e71871132b92d60a603fe444f93af53f0aa1..a79633d702bff23230bb0d76cb68565e078b4fb2 100644 (file)
@@ -172,6 +172,7 @@ architectures:
 - ia64 (Does not support probes on instruction slot1.)
 - sparc64 (Return probes not yet implemented.)
 - arm
+- ppc
 
 3. Configuring Kprobes
 
index 74693d91731ff441de8c4db172608c1de65b635e..4ba2af125450ea895583efc086a5ea4c5d5724cf 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)
@@ -100,7 +108,11 @@ static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
         * possible we'd get the single step reported for an exception handler
         * like Decrementer or External Interrupt */
        regs->msr &= ~MSR_EE;
-       regs->msr |= MSR_SE;
+       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
@@ -163,7 +175,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;
                        }
@@ -404,10 +417,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;
@@ -430,7 +443,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);
index 4b5b7ff4f78bb0e4564718fd7836f3a1542dae9f..b463d48145a496833e4bd9132011ba00b61532ae 100644 (file)
@@ -1030,21 +1030,29 @@ void SoftwareEmulation(struct pt_regs *regs)
 
 #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
 
-void DebugException(struct pt_regs *regs, unsigned long debug_status)
+void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
 {
        if (debug_status & DBSR_IC) {   /* instruction completion */
                regs->msr &= ~MSR_DE;
+
+               /* Disable instruction completion */
+               mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_IC);
+               /* Clear the instruction completion event */
+               mtspr(SPRN_DBSR, DBSR_IC);
+
+               if (notify_die(DIE_SSTEP, "single_step", regs, 5,
+                              5, SIGTRAP) == NOTIFY_STOP) {
+                       return;
+               }
+
+               if (debugger_sstep(regs))
+                       return;
+
                if (user_mode(regs)) {
                        current->thread.dbcr0 &= ~DBCR0_IC;
-               } else {
-                       /* Disable instruction completion */
-                       mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_IC);
-                       /* Clear the instruction completion event */
-                       mtspr(SPRN_DBSR, DBSR_IC);
-                       if (debugger_sstep(regs))
-                               return;
                }
-               _exception(SIGTRAP, regs, TRAP_TRACE, 0);
+
+               _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
        }
 }
 #endif /* CONFIG_4xx || CONFIG_BOOKE */