* Copyright (C) 2004 David S. Miller <davem@davemloft.net>
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
-#include <linux/module.h>
#include <asm/kdebug.h>
#include <asm/signal.h>
#include <asm/cacheflush.h>
-#include <asm/uaccess.h>
/* We do not have hardware single-stepping on sparc64.
* So we implement software single-stepping with breakpoint
flushi(p->addr);
}
-static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+static inline void save_previous_kprobe(struct kprobe_ctlblk *kcb)
{
kcb->prev_kprobe.kp = kprobe_running();
kcb->prev_kprobe.status = kcb->kprobe_status;
kcb->prev_kprobe.orig_tstate_pil = kcb->kprobe_orig_tstate_pil;
}
-static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+static inline void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{
__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
kcb->kprobe_status = kcb->prev_kprobe.status;
kcb->kprobe_orig_tstate_pil = kcb->prev_kprobe.orig_tstate_pil;
}
-static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
+static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb)
{
__get_cpu_var(current_kprobe) = p;
kcb->kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL);
}
-static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs,
+static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb)
{
regs->tstate |= TSTATE_PIL;
kcb->kprobe_orig_tstate_pil);
}
-static int __kprobes post_kprobe_handler(struct pt_regs *regs)
+static inline int post_kprobe_handler(struct pt_regs *regs)
{
struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
return 1;
}
-static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
{
struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
- 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 tpc points back to the probe address
- * and allow the page fault handler to continue as a
- * normal page fault.
- */
- regs->tpc = (unsigned long)cur->addr;
- regs->tnpc = kcb->kprobe_orig_tnpc;
- regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
- kcb->kprobe_orig_tstate_pil);
- 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 (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+ return 1;
- entry = search_exception_tables(regs->tpc);
- if (entry) {
- regs->tpc = entry->fixup;
- regs->tnpc = regs->tpc + 4;
- return 1;
- }
+ if (kcb->kprobe_status & KPROBE_HIT_SS) {
+ resume_execution(cur, regs, kcb);
- /*
- * fixup_exception() could not handle it,
- * Let do_page_fault() fix it.
- */
- break;
- default:
- break;
+ reset_current_kprobe();
+ preempt_enable_no_resched();
}
-
return 0;
}
struct die_args *args = (struct die_args *)data;
int ret = NOTIFY_DONE;
- if (args->regs && user_mode(args->regs))
- return ret;
-
switch (val) {
case DIE_DEBUG:
if (kprobe_handler(args->regs))