linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / powerpc / kernel / kprobes.c
index cd65c36..cfab485 100644 (file)
@@ -1,5 +1,6 @@
 /*
  *  Kernel Probes (KProbes)
+ *  arch/ppc64/kernel/kprobes.c
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *             for PPC64
  */
 
+#include <linux/config.h>
 #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);
@@ -61,8 +61,6 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        if (!ret) {
                memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
                p->opcode = *p->addr;
-               flush_icache_range((unsigned long)p->ainsn.insn,
-                       (unsigned long)p->ainsn.insn + sizeof(kprobe_opcode_t));
        }
 
        return ret;
@@ -84,39 +82,39 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
 
 void __kprobes arch_remove_kprobe(struct kprobe *p)
 {
-       mutex_lock(&kprobe_mutex);
+       down(&kprobe_mutex);
        free_insn_slot(p->ainsn.insn);
-       mutex_unlock(&kprobe_mutex);
+       up(&kprobe_mutex);
 }
 
-static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
 {
+       kprobe_opcode_t insn = *p->ainsn.insn;
+
        regs->msr |= MSR_SE;
 
-       /*
-        * On powerpc we should single step on the original
-        * instruction even if the probed insn is a trap
-        * variant as values in regs could play a part in
-        * if the trap is taken or not
-        */
-       regs->nip = (unsigned long)p->ainsn.insn;
+       /* single step inline if it is a trap variant */
+       if (is_trap(insn))
+               regs->nip = (unsigned long)p->addr;
+       else
+               regs->nip = (unsigned long)p->ainsn.insn;
 }
 
-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.saved_msr = kcb->kprobe_saved_msr;
 }
 
-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_saved_msr = kcb->prev_kprobe.saved_msr;
 }
 
-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;
@@ -142,7 +140,7 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe *rp,
        }
 }
 
-static int __kprobes kprobe_handler(struct pt_regs *regs)
+static inline int kprobe_handler(struct pt_regs *regs)
 {
        struct kprobe *p;
        int ret = 0;
@@ -335,7 +333,7 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
                regs->nip = (unsigned long)p->addr + 4;
 }
 
-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();
@@ -371,66 +369,21 @@ out:
        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 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;
+
+       if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+               return 1;
+
+       if (kcb->kprobe_status & KPROBE_HIT_SS) {
+               resume_execution(cur, regs);
                regs->msr &= ~MSR_SE;
                regs->msr |= kcb->kprobe_saved_msr;
-               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;
+               reset_current_kprobe();
+               preempt_enable_no_resched();
        }
        return 0;
 }
@@ -444,9 +397,6 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
        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_BPT:
                if (kprobe_handler(args->regs))