diff options
Diffstat (limited to 'arch/powerpc/kernel/kprobes.c')
-rw-r--r-- | arch/powerpc/kernel/kprobes.c | 36 |
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 | ||
38 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; | 45 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
39 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); | 46 | DEFINE_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 | ||
96 | static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 104 | static 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 |
499 | unsigned long arch_deref_entry_point(void *entry) | 521 | unsigned 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 | ||