aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/kprobes.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel/kprobes.c')
-rw-r--r--arch/powerpc/kernel/kprobes.c36
1 files changed, 29 insertions, 7 deletions
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
index c176c513566b..4ba2af125450 100644
--- a/arch/powerpc/kernel/kprobes.c
+++ b/arch/powerpc/kernel/kprobes.c
@@ -34,6 +34,13 @@
34#include <asm/cacheflush.h> 34#include <asm/cacheflush.h>
35#include <asm/sstep.h> 35#include <asm/sstep.h>
36#include <asm/uaccess.h> 36#include <asm/uaccess.h>
37#include <asm/system.h>
38
39#ifdef CONFIG_BOOKE
40#define MSR_SINGLESTEP (MSR_DE)
41#else
42#define MSR_SINGLESTEP (MSR_SE)
43#endif
37 44
38DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; 45DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
39DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); 46DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
@@ -53,7 +60,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
53 ret = -EINVAL; 60 ret = -EINVAL;
54 } 61 }
55 62
56 /* insn must be on a special executable page on ppc64 */ 63 /* insn must be on a special executable page on ppc64. This is
64 * not explicitly required on ppc32 (right now), but it doesn't hurt */
57 if (!ret) { 65 if (!ret) {
58 p->ainsn.insn = get_insn_slot(); 66 p->ainsn.insn = get_insn_slot();
59 if (!p->ainsn.insn) 67 if (!p->ainsn.insn)
@@ -95,7 +103,16 @@ void __kprobes arch_remove_kprobe(struct kprobe *p)
95 103
96static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) 104static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
97{ 105{
98 regs->msr |= MSR_SE; 106 /* We turn off async exceptions to ensure that the single step will
107 * be for the instruction we have the kprobe on, if we dont its
108 * possible we'd get the single step reported for an exception handler
109 * like Decrementer or External Interrupt */
110 regs->msr &= ~MSR_EE;
111 regs->msr |= MSR_SINGLESTEP;
112#ifdef CONFIG_BOOKE
113 regs->msr &= ~MSR_CE;
114 mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC | DBCR0_IDM);
115#endif
99 116
100 /* 117 /*
101 * On powerpc we should single step on the original 118 * On powerpc we should single step on the original
@@ -158,7 +175,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
158 kprobe_opcode_t insn = *p->ainsn.insn; 175 kprobe_opcode_t insn = *p->ainsn.insn;
159 if (kcb->kprobe_status == KPROBE_HIT_SS && 176 if (kcb->kprobe_status == KPROBE_HIT_SS &&
160 is_trap(insn)) { 177 is_trap(insn)) {
161 regs->msr &= ~MSR_SE; 178 /* Turn off 'trace' bits */
179 regs->msr &= ~MSR_SINGLESTEP;
162 regs->msr |= kcb->kprobe_saved_msr; 180 regs->msr |= kcb->kprobe_saved_msr;
163 goto no_kprobe; 181 goto no_kprobe;
164 } 182 }
@@ -376,6 +394,10 @@ static int __kprobes post_kprobe_handler(struct pt_regs *regs)
376 if (!cur) 394 if (!cur)
377 return 0; 395 return 0;
378 396
397 /* make sure we got here for instruction we have a kprobe on */
398 if (((unsigned long)cur->ainsn.insn + 4) != regs->nip)
399 return 0;
400
379 if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) { 401 if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
380 kcb->kprobe_status = KPROBE_HIT_SSDONE; 402 kcb->kprobe_status = KPROBE_HIT_SSDONE;
381 cur->post_handler(cur, regs, 0); 403 cur->post_handler(cur, regs, 0);
@@ -395,10 +417,10 @@ out:
395 417
396 /* 418 /*
397 * if somebody else is singlestepping across a probe point, msr 419 * if somebody else is singlestepping across a probe point, msr
398 * will have SE set, in which case, continue the remaining processing 420 * will have DE/SE set, in which case, continue the remaining processing
399 * of do_debug, as if this is not a probe hit. 421 * of do_debug, as if this is not a probe hit.
400 */ 422 */
401 if (regs->msr & MSR_SE) 423 if (regs->msr & MSR_SINGLESTEP)
402 return 0; 424 return 0;
403 425
404 return 1; 426 return 1;
@@ -421,7 +443,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
421 * normal page fault. 443 * normal page fault.
422 */ 444 */
423 regs->nip = (unsigned long)cur->addr; 445 regs->nip = (unsigned long)cur->addr;
424 regs->msr &= ~MSR_SE; 446 regs->msr &= ~MSR_SINGLESTEP; /* Turn off 'trace' bits */
425 regs->msr |= kcb->kprobe_saved_msr; 447 regs->msr |= kcb->kprobe_saved_msr;
426 if (kcb->kprobe_status == KPROBE_REENTER) 448 if (kcb->kprobe_status == KPROBE_REENTER)
427 restore_previous_kprobe(kcb); 449 restore_previous_kprobe(kcb);
@@ -498,7 +520,7 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
498#ifdef CONFIG_PPC64 520#ifdef CONFIG_PPC64
499unsigned long arch_deref_entry_point(void *entry) 521unsigned long arch_deref_entry_point(void *entry)
500{ 522{
501 return (unsigned long)(((func_descr_t *)entry)->entry); 523 return ((func_descr_t *)entry)->entry;
502} 524}
503#endif 525#endif
504 526