diff options
-rw-r--r-- | arch/sparc64/kernel/kprobes.c | 62 |
1 files changed, 49 insertions, 13 deletions
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index d67195ba3fa2..bdac631cf011 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c | |||
@@ -65,19 +65,40 @@ void arch_remove_kprobe(struct kprobe *p) | |||
65 | { | 65 | { |
66 | } | 66 | } |
67 | 67 | ||
68 | /* kprobe_status settings */ | ||
69 | #define KPROBE_HIT_ACTIVE 0x00000001 | ||
70 | #define KPROBE_HIT_SS 0x00000002 | ||
71 | |||
72 | static struct kprobe *current_kprobe; | 68 | static struct kprobe *current_kprobe; |
73 | static unsigned long current_kprobe_orig_tnpc; | 69 | static unsigned long current_kprobe_orig_tnpc; |
74 | static unsigned long current_kprobe_orig_tstate_pil; | 70 | static unsigned long current_kprobe_orig_tstate_pil; |
75 | static unsigned int kprobe_status; | 71 | static unsigned int kprobe_status; |
72 | static struct kprobe *kprobe_prev; | ||
73 | static unsigned long kprobe_orig_tnpc_prev; | ||
74 | static unsigned long kprobe_orig_tstate_pil_prev; | ||
75 | static unsigned int kprobe_status_prev; | ||
76 | 76 | ||
77 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 77 | static inline void save_previous_kprobe(void) |
78 | { | ||
79 | kprobe_status_prev = kprobe_status; | ||
80 | kprobe_orig_tnpc_prev = current_kprobe_orig_tnpc; | ||
81 | kprobe_orig_tstate_pil_prev = current_kprobe_orig_tstate_pil; | ||
82 | kprobe_prev = current_kprobe; | ||
83 | } | ||
84 | |||
85 | static inline void restore_previous_kprobe(void) | ||
86 | { | ||
87 | kprobe_status = kprobe_status_prev; | ||
88 | current_kprobe_orig_tnpc = kprobe_orig_tnpc_prev; | ||
89 | current_kprobe_orig_tstate_pil = kprobe_orig_tstate_pil_prev; | ||
90 | current_kprobe = kprobe_prev; | ||
91 | } | ||
92 | |||
93 | static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs) | ||
78 | { | 94 | { |
79 | current_kprobe_orig_tnpc = regs->tnpc; | 95 | current_kprobe_orig_tnpc = regs->tnpc; |
80 | current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL); | 96 | current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL); |
97 | current_kprobe = p; | ||
98 | } | ||
99 | |||
100 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | ||
101 | { | ||
81 | regs->tstate |= TSTATE_PIL; | 102 | regs->tstate |= TSTATE_PIL; |
82 | 103 | ||
83 | /*single step inline, if it a breakpoint instruction*/ | 104 | /*single step inline, if it a breakpoint instruction*/ |
@@ -110,12 +131,18 @@ static int kprobe_handler(struct pt_regs *regs) | |||
110 | unlock_kprobes(); | 131 | unlock_kprobes(); |
111 | goto no_kprobe; | 132 | goto no_kprobe; |
112 | } | 133 | } |
113 | arch_disarm_kprobe(p); | 134 | /* We have reentered the kprobe_handler(), since |
114 | regs->tpc = (unsigned long) p->addr; | 135 | * another probe was hit while within the handler. |
115 | regs->tnpc = current_kprobe_orig_tnpc; | 136 | * We here save the original kprobes variables and |
116 | regs->tstate = ((regs->tstate & ~TSTATE_PIL) | | 137 | * just single step on the instruction of the new probe |
117 | current_kprobe_orig_tstate_pil); | 138 | * without calling any user handlers. |
118 | ret = 1; | 139 | */ |
140 | save_previous_kprobe(); | ||
141 | set_current_kprobe(p, regs); | ||
142 | p->nmissed++; | ||
143 | kprobe_status = KPROBE_REENTER; | ||
144 | prepare_singlestep(p, regs); | ||
145 | return 1; | ||
119 | } else { | 146 | } else { |
120 | p = current_kprobe; | 147 | p = current_kprobe; |
121 | if (p->break_handler && p->break_handler(p, regs)) | 148 | if (p->break_handler && p->break_handler(p, regs)) |
@@ -143,8 +170,8 @@ static int kprobe_handler(struct pt_regs *regs) | |||
143 | goto no_kprobe; | 170 | goto no_kprobe; |
144 | } | 171 | } |
145 | 172 | ||
173 | set_current_kprobe(p, regs); | ||
146 | kprobe_status = KPROBE_HIT_ACTIVE; | 174 | kprobe_status = KPROBE_HIT_ACTIVE; |
147 | current_kprobe = p; | ||
148 | if (p->pre_handler && p->pre_handler(p, regs)) | 175 | if (p->pre_handler && p->pre_handler(p, regs)) |
149 | return 1; | 176 | return 1; |
150 | 177 | ||
@@ -250,12 +277,20 @@ static inline int post_kprobe_handler(struct pt_regs *regs) | |||
250 | if (!kprobe_running()) | 277 | if (!kprobe_running()) |
251 | return 0; | 278 | return 0; |
252 | 279 | ||
253 | if (current_kprobe->post_handler) | 280 | if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { |
281 | kprobe_status = KPROBE_HIT_SSDONE; | ||
254 | current_kprobe->post_handler(current_kprobe, regs, 0); | 282 | current_kprobe->post_handler(current_kprobe, regs, 0); |
283 | } | ||
255 | 284 | ||
256 | resume_execution(current_kprobe, regs); | 285 | resume_execution(current_kprobe, regs); |
257 | 286 | ||
287 | /*Restore back the original saved kprobes variables and continue. */ | ||
288 | if (kprobe_status == KPROBE_REENTER) { | ||
289 | restore_previous_kprobe(); | ||
290 | goto out; | ||
291 | } | ||
258 | unlock_kprobes(); | 292 | unlock_kprobes(); |
293 | out: | ||
259 | preempt_enable_no_resched(); | 294 | preempt_enable_no_resched(); |
260 | 295 | ||
261 | return 1; | 296 | return 1; |
@@ -397,3 +432,4 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | |||
397 | } | 432 | } |
398 | return 0; | 433 | return 0; |
399 | } | 434 | } |
435 | |||