diff options
Diffstat (limited to 'arch/s390/kernel/kprobes.c')
-rw-r--r-- | arch/s390/kernel/kprobes.c | 114 |
1 files changed, 71 insertions, 43 deletions
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 4efd5dbfd72d..2a19f4154f2d 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
@@ -238,25 +238,44 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, | |||
238 | regs->gprs[14] = (unsigned long)&kretprobe_trampoline; | 238 | regs->gprs[14] = (unsigned long)&kretprobe_trampoline; |
239 | } | 239 | } |
240 | 240 | ||
241 | static void __kprobes kprobe_reenter_check(struct kprobe_ctlblk *kcb, | ||
242 | struct kprobe *p) | ||
243 | { | ||
244 | switch (kcb->kprobe_status) { | ||
245 | case KPROBE_HIT_SSDONE: | ||
246 | case KPROBE_HIT_ACTIVE: | ||
247 | kprobes_inc_nmissed_count(p); | ||
248 | break; | ||
249 | case KPROBE_HIT_SS: | ||
250 | case KPROBE_REENTER: | ||
251 | default: | ||
252 | /* | ||
253 | * A kprobe on the code path to single step an instruction | ||
254 | * is a BUG. The code path resides in the .kprobes.text | ||
255 | * section and is executed with interrupts disabled. | ||
256 | */ | ||
257 | printk(KERN_EMERG "Invalid kprobe detected at %p.\n", p->addr); | ||
258 | dump_kprobe(p); | ||
259 | BUG(); | ||
260 | } | ||
261 | } | ||
262 | |||
241 | static int __kprobes kprobe_handler(struct pt_regs *regs) | 263 | static int __kprobes kprobe_handler(struct pt_regs *regs) |
242 | { | 264 | { |
243 | struct kprobe *p; | ||
244 | int ret = 0; | ||
245 | unsigned long *addr = (unsigned long *) | ||
246 | ((regs->psw.addr & PSW_ADDR_INSN) - 2); | ||
247 | struct kprobe_ctlblk *kcb; | 265 | struct kprobe_ctlblk *kcb; |
266 | struct kprobe *p; | ||
248 | 267 | ||
249 | /* | 268 | /* |
250 | * We don't want to be preempted for the entire | 269 | * We want to disable preemption for the entire duration of kprobe |
251 | * duration of kprobe processing | 270 | * processing. That includes the calls to the pre/post handlers |
271 | * and single stepping the kprobe instruction. | ||
252 | */ | 272 | */ |
253 | preempt_disable(); | 273 | preempt_disable(); |
254 | kcb = get_kprobe_ctlblk(); | 274 | kcb = get_kprobe_ctlblk(); |
275 | p = get_kprobe((void *)((regs->psw.addr & PSW_ADDR_INSN) - 2)); | ||
255 | 276 | ||
256 | /* Check we're not actually recursing */ | 277 | if (p) { |
257 | if (kprobe_running()) { | 278 | if (kprobe_running()) { |
258 | p = get_kprobe(addr); | ||
259 | if (p) { | ||
260 | /* | 279 | /* |
261 | * We have hit a kprobe while another is still | 280 | * We have hit a kprobe while another is still |
262 | * active. This can happen in the pre and post | 281 | * active. This can happen in the pre and post |
@@ -266,45 +285,54 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) | |||
266 | * push_kprobe and pop_kprobe saves and restores | 285 | * push_kprobe and pop_kprobe saves and restores |
267 | * the currently active kprobe. | 286 | * the currently active kprobe. |
268 | */ | 287 | */ |
288 | kprobe_reenter_check(kcb, p); | ||
269 | push_kprobe(kcb, p); | 289 | push_kprobe(kcb, p); |
270 | kprobes_inc_nmissed_count(p); | ||
271 | enable_singlestep(kcb, regs, | ||
272 | (unsigned long) p->ainsn.insn); | ||
273 | kcb->kprobe_status = KPROBE_REENTER; | 290 | kcb->kprobe_status = KPROBE_REENTER; |
274 | return 1; | ||
275 | } else { | 291 | } else { |
276 | p = __get_cpu_var(current_kprobe); | 292 | /* |
277 | if (p->break_handler && p->break_handler(p, regs)) { | 293 | * If we have no pre-handler or it returned 0, we |
278 | goto ss_probe; | 294 | * continue with single stepping. If we have a |
279 | } | 295 | * pre-handler and it returned non-zero, it prepped |
296 | * for calling the break_handler below on re-entry | ||
297 | * for jprobe processing, so get out doing nothing | ||
298 | * more here. | ||
299 | */ | ||
300 | push_kprobe(kcb, p); | ||
301 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; | ||
302 | if (p->pre_handler && p->pre_handler(p, regs)) | ||
303 | return 1; | ||
304 | kcb->kprobe_status = KPROBE_HIT_SS; | ||
280 | } | 305 | } |
281 | goto no_kprobe; | 306 | enable_singlestep(kcb, regs, (unsigned long) p->ainsn.insn); |
282 | } | ||
283 | |||
284 | p = get_kprobe(addr); | ||
285 | if (!p) | ||
286 | /* | ||
287 | * No kprobe at this address. The fault has not been | ||
288 | * caused by a kprobe breakpoint. The race of breakpoint | ||
289 | * vs. kprobe remove does not exist because on s390 we | ||
290 | * use stop_machine to arm/disarm the breakpoints. | ||
291 | */ | ||
292 | goto no_kprobe; | ||
293 | |||
294 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; | ||
295 | push_kprobe(kcb, p); | ||
296 | if (p->pre_handler && p->pre_handler(p, regs)) | ||
297 | /* handler has already set things up, so skip ss setup */ | ||
298 | return 1; | 307 | return 1; |
299 | 308 | } else if (kprobe_running()) { | |
300 | ss_probe: | 309 | p = __get_cpu_var(current_kprobe); |
301 | enable_singlestep(kcb, regs, (unsigned long) p->ainsn.insn); | 310 | if (p->break_handler && p->break_handler(p, regs)) { |
302 | kcb->kprobe_status = KPROBE_HIT_SS; | 311 | /* |
303 | return 1; | 312 | * Continuation after the jprobe completed and |
304 | 313 | * caused the jprobe_return trap. The jprobe | |
305 | no_kprobe: | 314 | * break_handler "returns" to the original |
315 | * function that still has the kprobe breakpoint | ||
316 | * installed. We continue with single stepping. | ||
317 | */ | ||
318 | kcb->kprobe_status = KPROBE_HIT_SS; | ||
319 | enable_singlestep(kcb, regs, | ||
320 | (unsigned long) p->ainsn.insn); | ||
321 | return 1; | ||
322 | } /* else: | ||
323 | * No kprobe at this address and the current kprobe | ||
324 | * has no break handler (no jprobe!). The kernel just | ||
325 | * exploded, let the standard trap handler pick up the | ||
326 | * pieces. | ||
327 | */ | ||
328 | } /* else: | ||
329 | * No kprobe at this address and no active kprobe. The trap has | ||
330 | * not been caused by a kprobe breakpoint. The race of breakpoint | ||
331 | * vs. kprobe remove does not exist because on s390 as we use | ||
332 | * stop_machine to arm/disarm the breakpoints. | ||
333 | */ | ||
306 | preempt_enable_no_resched(); | 334 | preempt_enable_no_resched(); |
307 | return ret; | 335 | return 0; |
308 | } | 336 | } |
309 | 337 | ||
310 | /* | 338 | /* |