diff options
Diffstat (limited to 'arch/powerpc/kernel/kprobes.c')
-rw-r--r-- | arch/powerpc/kernel/kprobes.c | 71 |
1 files changed, 60 insertions, 11 deletions
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index cfab48566db1..ad7a90212204 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c | |||
@@ -1,6 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Kernel Probes (KProbes) | 2 | * Kernel Probes (KProbes) |
3 | * arch/ppc64/kernel/kprobes.c | ||
4 | * | 3 | * |
5 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 5 | * it under the terms of the GNU General Public License as published by |
@@ -31,9 +30,11 @@ | |||
31 | #include <linux/kprobes.h> | 30 | #include <linux/kprobes.h> |
32 | #include <linux/ptrace.h> | 31 | #include <linux/ptrace.h> |
33 | #include <linux/preempt.h> | 32 | #include <linux/preempt.h> |
33 | #include <linux/module.h> | ||
34 | #include <asm/cacheflush.h> | 34 | #include <asm/cacheflush.h> |
35 | #include <asm/kdebug.h> | 35 | #include <asm/kdebug.h> |
36 | #include <asm/sstep.h> | 36 | #include <asm/sstep.h> |
37 | #include <asm/uaccess.h> | ||
37 | 38 | ||
38 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; | 39 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
39 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); | 40 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
@@ -82,9 +83,9 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) | |||
82 | 83 | ||
83 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 84 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
84 | { | 85 | { |
85 | down(&kprobe_mutex); | 86 | mutex_lock(&kprobe_mutex); |
86 | free_insn_slot(p->ainsn.insn); | 87 | free_insn_slot(p->ainsn.insn); |
87 | up(&kprobe_mutex); | 88 | mutex_unlock(&kprobe_mutex); |
88 | } | 89 | } |
89 | 90 | ||
90 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 91 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
@@ -373,17 +374,62 @@ static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) | |||
373 | { | 374 | { |
374 | struct kprobe *cur = kprobe_running(); | 375 | struct kprobe *cur = kprobe_running(); |
375 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 376 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
376 | 377 | const struct exception_table_entry *entry; | |
377 | if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) | 378 | |
378 | return 1; | 379 | switch(kcb->kprobe_status) { |
379 | 380 | case KPROBE_HIT_SS: | |
380 | if (kcb->kprobe_status & KPROBE_HIT_SS) { | 381 | case KPROBE_REENTER: |
381 | resume_execution(cur, regs); | 382 | /* |
383 | * We are here because the instruction being single | ||
384 | * stepped caused a page fault. We reset the current | ||
385 | * kprobe and the nip points back to the probe address | ||
386 | * and allow the page fault handler to continue as a | ||
387 | * normal page fault. | ||
388 | */ | ||
389 | regs->nip = (unsigned long)cur->addr; | ||
382 | regs->msr &= ~MSR_SE; | 390 | regs->msr &= ~MSR_SE; |
383 | regs->msr |= kcb->kprobe_saved_msr; | 391 | regs->msr |= kcb->kprobe_saved_msr; |
384 | 392 | if (kcb->kprobe_status == KPROBE_REENTER) | |
385 | reset_current_kprobe(); | 393 | restore_previous_kprobe(kcb); |
394 | else | ||
395 | reset_current_kprobe(); | ||
386 | preempt_enable_no_resched(); | 396 | preempt_enable_no_resched(); |
397 | break; | ||
398 | case KPROBE_HIT_ACTIVE: | ||
399 | case KPROBE_HIT_SSDONE: | ||
400 | /* | ||
401 | * We increment the nmissed count for accounting, | ||
402 | * we can also use npre/npostfault count for accouting | ||
403 | * these specific fault cases. | ||
404 | */ | ||
405 | kprobes_inc_nmissed_count(cur); | ||
406 | |||
407 | /* | ||
408 | * We come here because instructions in the pre/post | ||
409 | * handler caused the page_fault, this could happen | ||
410 | * if handler tries to access user space by | ||
411 | * copy_from_user(), get_user() etc. Let the | ||
412 | * user-specified handler try to fix it first. | ||
413 | */ | ||
414 | if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) | ||
415 | return 1; | ||
416 | |||
417 | /* | ||
418 | * In case the user-specified fault handler returned | ||
419 | * zero, try to fix up. | ||
420 | */ | ||
421 | if ((entry = search_exception_tables(regs->nip)) != NULL) { | ||
422 | regs->nip = entry->fixup; | ||
423 | return 1; | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * fixup_exception() could not handle it, | ||
428 | * Let do_page_fault() fix it. | ||
429 | */ | ||
430 | break; | ||
431 | default: | ||
432 | break; | ||
387 | } | 433 | } |
388 | return 0; | 434 | return 0; |
389 | } | 435 | } |
@@ -397,6 +443,9 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, | |||
397 | struct die_args *args = (struct die_args *)data; | 443 | struct die_args *args = (struct die_args *)data; |
398 | int ret = NOTIFY_DONE; | 444 | int ret = NOTIFY_DONE; |
399 | 445 | ||
446 | if (args->regs && user_mode(args->regs)) | ||
447 | return ret; | ||
448 | |||
400 | switch (val) { | 449 | switch (val) { |
401 | case DIE_BPT: | 450 | case DIE_BPT: |
402 | if (kprobe_handler(args->regs)) | 451 | if (kprobe_handler(args->regs)) |