|
@@ -30,9 +30,11 @@
|
|
|
#include <linux/kprobes.h>
|
|
|
#include <linux/ptrace.h>
|
|
|
#include <linux/preempt.h>
|
|
|
+#include <linux/module.h>
|
|
|
#include <asm/cacheflush.h>
|
|
|
#include <asm/kdebug.h>
|
|
|
#include <asm/sstep.h>
|
|
|
+#include <asm/uaccess.h>
|
|
|
|
|
|
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
|
|
|
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
|
|
@@ -372,17 +374,62 @@ static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
|
|
|
{
|
|
|
struct kprobe *cur = kprobe_running();
|
|
|
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
|
|
-
|
|
|
- if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
|
|
|
- return 1;
|
|
|
-
|
|
|
- if (kcb->kprobe_status & KPROBE_HIT_SS) {
|
|
|
- resume_execution(cur, regs);
|
|
|
+ const struct exception_table_entry *entry;
|
|
|
+
|
|
|
+ switch(kcb->kprobe_status) {
|
|
|
+ case KPROBE_HIT_SS:
|
|
|
+ case KPROBE_REENTER:
|
|
|
+ /*
|
|
|
+ * We are here because the instruction being single
|
|
|
+ * stepped caused a page fault. We reset the current
|
|
|
+ * kprobe and the nip points back to the probe address
|
|
|
+ * and allow the page fault handler to continue as a
|
|
|
+ * normal page fault.
|
|
|
+ */
|
|
|
+ regs->nip = (unsigned long)cur->addr;
|
|
|
regs->msr &= ~MSR_SE;
|
|
|
regs->msr |= kcb->kprobe_saved_msr;
|
|
|
-
|
|
|
- reset_current_kprobe();
|
|
|
+ if (kcb->kprobe_status == KPROBE_REENTER)
|
|
|
+ restore_previous_kprobe(kcb);
|
|
|
+ else
|
|
|
+ reset_current_kprobe();
|
|
|
preempt_enable_no_resched();
|
|
|
+ break;
|
|
|
+ case KPROBE_HIT_ACTIVE:
|
|
|
+ case KPROBE_HIT_SSDONE:
|
|
|
+ /*
|
|
|
+ * We increment the nmissed count for accounting,
|
|
|
+ * we can also use npre/npostfault count for accouting
|
|
|
+ * these specific fault cases.
|
|
|
+ */
|
|
|
+ kprobes_inc_nmissed_count(cur);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We come here because instructions in the pre/post
|
|
|
+ * handler caused the page_fault, this could happen
|
|
|
+ * if handler tries to access user space by
|
|
|
+ * copy_from_user(), get_user() etc. Let the
|
|
|
+ * user-specified handler try to fix it first.
|
|
|
+ */
|
|
|
+ if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * In case the user-specified fault handler returned
|
|
|
+ * zero, try to fix up.
|
|
|
+ */
|
|
|
+ if ((entry = search_exception_tables(regs->nip)) != NULL) {
|
|
|
+ regs->nip = entry->fixup;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * fixup_exception() could not handle it,
|
|
|
+ * Let do_page_fault() fix it.
|
|
|
+ */
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|