diff options
Diffstat (limited to 'arch/ppc64/kernel')
-rw-r--r-- | arch/ppc64/kernel/kprobes.c | 46 |
1 files changed, 38 insertions, 8 deletions
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c index 8c0920a6d03e..782ce3efa2c1 100644 --- a/arch/ppc64/kernel/kprobes.c +++ b/arch/ppc64/kernel/kprobes.c | |||
@@ -36,12 +36,10 @@ | |||
36 | #include <asm/kdebug.h> | 36 | #include <asm/kdebug.h> |
37 | #include <asm/sstep.h> | 37 | #include <asm/sstep.h> |
38 | 38 | ||
39 | /* kprobe_status settings */ | ||
40 | #define KPROBE_HIT_ACTIVE 0x00000001 | ||
41 | #define KPROBE_HIT_SS 0x00000002 | ||
42 | |||
43 | static struct kprobe *current_kprobe; | 39 | static struct kprobe *current_kprobe; |
44 | static unsigned long kprobe_status, kprobe_saved_msr; | 40 | static unsigned long kprobe_status, kprobe_saved_msr; |
41 | static struct kprobe *kprobe_prev; | ||
42 | static unsigned long kprobe_status_prev, kprobe_saved_msr_prev; | ||
45 | static struct pt_regs jprobe_saved_regs; | 43 | static struct pt_regs jprobe_saved_regs; |
46 | 44 | ||
47 | int arch_prepare_kprobe(struct kprobe *p) | 45 | int arch_prepare_kprobe(struct kprobe *p) |
@@ -93,6 +91,20 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | |||
93 | regs->nip = (unsigned long)&p->ainsn.insn; | 91 | regs->nip = (unsigned long)&p->ainsn.insn; |
94 | } | 92 | } |
95 | 93 | ||
94 | static inline void save_previous_kprobe(void) | ||
95 | { | ||
96 | kprobe_prev = current_kprobe; | ||
97 | kprobe_status_prev = kprobe_status; | ||
98 | kprobe_saved_msr_prev = kprobe_saved_msr; | ||
99 | } | ||
100 | |||
101 | static inline void restore_previous_kprobe(void) | ||
102 | { | ||
103 | current_kprobe = kprobe_prev; | ||
104 | kprobe_status = kprobe_status_prev; | ||
105 | kprobe_saved_msr = kprobe_saved_msr_prev; | ||
106 | } | ||
107 | |||
96 | static inline int kprobe_handler(struct pt_regs *regs) | 108 | static inline int kprobe_handler(struct pt_regs *regs) |
97 | { | 109 | { |
98 | struct kprobe *p; | 110 | struct kprobe *p; |
@@ -111,9 +123,19 @@ static inline int kprobe_handler(struct pt_regs *regs) | |||
111 | unlock_kprobes(); | 123 | unlock_kprobes(); |
112 | goto no_kprobe; | 124 | goto no_kprobe; |
113 | } | 125 | } |
114 | arch_disarm_kprobe(p); | 126 | /* We have reentered the kprobe_handler(), since |
115 | regs->nip = (unsigned long)p->addr; | 127 | * another probe was hit while within the handler. |
116 | ret = 1; | 128 | * We here save the original kprobes variables and |
129 | * just single step on the instruction of the new probe | ||
130 | * without calling any user handlers. | ||
131 | */ | ||
132 | save_previous_kprobe(); | ||
133 | current_kprobe = p; | ||
134 | kprobe_saved_msr = regs->msr; | ||
135 | p->nmissed++; | ||
136 | prepare_singlestep(p, regs); | ||
137 | kprobe_status = KPROBE_REENTER; | ||
138 | return 1; | ||
117 | } else { | 139 | } else { |
118 | p = current_kprobe; | 140 | p = current_kprobe; |
119 | if (p->break_handler && p->break_handler(p, regs)) { | 141 | if (p->break_handler && p->break_handler(p, regs)) { |
@@ -195,13 +217,21 @@ static inline int post_kprobe_handler(struct pt_regs *regs) | |||
195 | if (!kprobe_running()) | 217 | if (!kprobe_running()) |
196 | return 0; | 218 | return 0; |
197 | 219 | ||
198 | if (current_kprobe->post_handler) | 220 | if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { |
221 | kprobe_status = KPROBE_HIT_SSDONE; | ||
199 | current_kprobe->post_handler(current_kprobe, regs, 0); | 222 | current_kprobe->post_handler(current_kprobe, regs, 0); |
223 | } | ||
200 | 224 | ||
201 | resume_execution(current_kprobe, regs); | 225 | resume_execution(current_kprobe, regs); |
202 | regs->msr |= kprobe_saved_msr; | 226 | regs->msr |= kprobe_saved_msr; |
203 | 227 | ||
228 | /*Restore back the original saved kprobes variables and continue. */ | ||
229 | if (kprobe_status == KPROBE_REENTER) { | ||
230 | restore_previous_kprobe(); | ||
231 | goto out; | ||
232 | } | ||
204 | unlock_kprobes(); | 233 | unlock_kprobes(); |
234 | out: | ||
205 | preempt_enable_no_resched(); | 235 | preempt_enable_no_resched(); |
206 | 236 | ||
207 | /* | 237 | /* |