[IA64] restore_sigcontext is not preempt safe
[powerpc.git] / arch / ia64 / kernel / kprobes.c
index 5d53446..3aa3167 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <asm/pgtable.h>
 #include <asm/kdebug.h>
+#include <asm/sections.h>
 
 extern void jprobe_inst_return(void);
 
@@ -41,8 +42,8 @@ extern void jprobe_inst_return(void);
 #define KPROBE_HIT_ACTIVE      0x00000001
 #define KPROBE_HIT_SS          0x00000002
 
-static struct kprobe *current_kprobe;
-static unsigned long kprobe_status;
+static struct kprobe *current_kprobe, *kprobe_prev;
+static unsigned long kprobe_status, kprobe_status_prev;
 static struct pt_regs jprobe_saved_regs;
 
 enum instruction_type {A, I, M, F, B, L, X, u};
@@ -119,6 +120,83 @@ static void update_kprobe_inst_flag(uint template, uint  slot, uint major_opcode
        return;
 }
 
+/*
+ * In this function we check to see if the instruction
+ * on which we are inserting kprobe is supported.
+ * Returns 0 if supported
+ * Returns -EINVAL if unsupported
+ */
+static int unsupported_inst(uint template, uint  slot, uint major_opcode,
+       unsigned long kprobe_inst, struct kprobe *p)
+{
+       unsigned long addr = (unsigned long)p->addr;
+
+       if (bundle_encoding[template][slot] == I) {
+               switch (major_opcode) {
+                       case 0x0: //I_UNIT_MISC_OPCODE:
+                       /*
+                        * Check for Integer speculation instruction
+                        * - Bit 33-35 to be equal to 0x1
+                        */
+                       if (((kprobe_inst >> 33) & 0x7) == 1) {
+                               printk(KERN_WARNING
+                                       "Kprobes on speculation inst at <0x%lx> not supported\n",
+                                       addr);
+                               return -EINVAL;
+                       }
+
+                       /*
+                        * IP relative mov instruction
+                        *  - Bit 27-35 to be equal to 0x30
+                        */
+                       if (((kprobe_inst >> 27) & 0x1FF) == 0x30) {
+                               printk(KERN_WARNING
+                                       "Kprobes on \"mov r1=ip\" at <0x%lx> not supported\n",
+                                       addr);
+                               return -EINVAL;
+
+                       }
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * In this function we check to see if the instruction
+ * (qp) cmpx.crel.ctype p1,p2=r2,r3
+ * on which we are inserting kprobe is cmp instruction
+ * with ctype as unc.
+ */
+static uint is_cmp_ctype_unc_inst(uint template, uint slot, uint major_opcode,
+unsigned long kprobe_inst)
+{
+       cmp_inst_t cmp_inst;
+       uint ctype_unc = 0;
+
+       if (!((bundle_encoding[template][slot] == I) ||
+               (bundle_encoding[template][slot] == M)))
+               goto out;
+
+       if (!((major_opcode == 0xC) || (major_opcode == 0xD) ||
+               (major_opcode == 0xE)))
+               goto out;
+
+       cmp_inst.l = kprobe_inst;
+       if ((cmp_inst.f.x2 == 0) || (cmp_inst.f.x2 == 1)) {
+               /* Integere compare - Register Register (A6 type)*/
+               if ((cmp_inst.f.tb == 0) && (cmp_inst.f.ta == 0)
+                               &&(cmp_inst.f.c == 1))
+                       ctype_unc = 1;
+       } else if ((cmp_inst.f.x2 == 2)||(cmp_inst.f.x2 == 3)) {
+               /* Integere compare - Immediate Register (A8 type)*/
+               if ((cmp_inst.f.ta == 0) &&(cmp_inst.f.c == 1))
+                       ctype_unc = 1;
+       }
+out:
+       return ctype_unc;
+}
+
 /*
  * In this function we override the bundle with
  * the break instruction at the given slot.
@@ -131,9 +209,13 @@ static void prepare_break_inst(uint template, uint  slot, uint major_opcode,
 
        /*
         * Copy the original kprobe_inst qualifying predicate(qp)
-        * to the break instruction
+        * to the break instruction iff !is_cmp_ctype_unc_inst
+        * because for cmp instruction with ctype equal to unc,
+        * which is a special instruction always needs to be
+        * executed regradless of qp
         */
-       break_inst |= (0x3f & kprobe_inst);
+       if (!is_cmp_ctype_unc_inst(template, slot, major_opcode, kprobe_inst))
+               break_inst |= (0x3f & kprobe_inst);
 
        switch (slot) {
          case 0:
@@ -182,16 +264,141 @@ static inline void get_kprobe_inst(bundle_t *bundle, uint slot,
        }
 }
 
+/* Returns non-zero if the addr is in the Interrupt Vector Table */
+static inline int in_ivt_functions(unsigned long addr)
+{
+       return (addr >= (unsigned long)__start_ivt_text
+               && addr < (unsigned long)__end_ivt_text);
+}
+
 static int valid_kprobe_addr(int template, int slot, unsigned long addr)
 {
        if ((slot > 2) || ((bundle_encoding[template][1] == L) && slot > 1)) {
-               printk(KERN_WARNING "Attempting to insert unaligned kprobe at 0x%lx\n",
-                               addr);
+               printk(KERN_WARNING "Attempting to insert unaligned kprobe "
+                               "at 0x%lx\n", addr);
                return -EINVAL;
        }
+
+       if (in_ivt_functions(addr)) {
+               printk(KERN_WARNING "Kprobes can't be inserted inside "
+                               "IVT functions at 0x%lx\n", addr);
+               return -EINVAL;
+       }
+
+       if (slot == 1 && bundle_encoding[template][1] != L) {
+               printk(KERN_WARNING "Inserting kprobes on slot #1 "
+                      "is not supported\n");
+               return -EINVAL;
+       }
+
        return 0;
 }
 
+static inline void save_previous_kprobe(void)
+{
+       kprobe_prev = current_kprobe;
+       kprobe_status_prev = kprobe_status;
+}
+
+static inline void restore_previous_kprobe(void)
+{
+       current_kprobe = kprobe_prev;
+       kprobe_status = kprobe_status_prev;
+}
+
+static inline void set_current_kprobe(struct kprobe *p)
+{
+       current_kprobe = p;
+}
+
+static void kretprobe_trampoline(void)
+{
+}
+
+/*
+ * At this point the target function has been tricked into
+ * returning into our trampoline.  Lookup the associated instance
+ * and then:
+ *    - call the handler function
+ *    - cleanup by marking the instance as unused
+ *    - long jump back to the original return address
+ */
+int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct kretprobe_instance *ri = NULL;
+       struct hlist_head *head;
+       struct hlist_node *node, *tmp;
+       unsigned long orig_ret_address = 0;
+       unsigned long trampoline_address =
+               ((struct fnptr *)kretprobe_trampoline)->ip;
+
+        head = kretprobe_inst_table_head(current);
+
+       /*
+        * It is possible to have multiple instances associated with a given
+        * task either because an multiple functions in the call path
+        * have a return probe installed on them, and/or more then one return
+        * return probe was registered for a target function.
+        *
+        * We can handle this because:
+        *     - instances are always inserted at the head of the list
+        *     - when multiple return probes are registered for the same
+        *       function, the first instance's ret_addr will point to the
+        *       real return address, and all the rest will point to
+        *       kretprobe_trampoline
+        */
+       hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+                if (ri->task != current)
+                       /* another task is sharing our hash bucket */
+                        continue;
+
+               if (ri->rp && ri->rp->handler)
+                       ri->rp->handler(ri, regs);
+
+               orig_ret_address = (unsigned long)ri->ret_addr;
+               recycle_rp_inst(ri);
+
+               if (orig_ret_address != trampoline_address)
+                       /*
+                        * This is the real return address. Any other
+                        * instances associated with this task are for
+                        * other calls deeper on the call stack
+                        */
+                       break;
+       }
+
+       BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
+       regs->cr_iip = orig_ret_address;
+
+       unlock_kprobes();
+       preempt_enable_no_resched();
+
+        /*
+         * By returning a non-zero value, we are telling
+         * kprobe_handler() that we have handled unlocking
+         * and re-enabling preemption.
+         */
+        return 1;
+}
+
+void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
+{
+       struct kretprobe_instance *ri;
+
+       if ((ri = get_free_rp_inst(rp)) != NULL) {
+               ri->rp = rp;
+               ri->task = current;
+               ri->ret_addr = (kprobe_opcode_t *)regs->b0;
+
+               /* Replace the return addr with trampoline addr */
+               regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip;
+
+               add_rp_inst(ri);
+       } else {
+               rp->nmissed++;
+       }
+}
+
 int arch_prepare_kprobe(struct kprobe *p)
 {
        unsigned long addr = (unsigned long) p->addr;
@@ -215,6 +422,9 @@ int arch_prepare_kprobe(struct kprobe *p)
        /* Get kprobe_inst and major_opcode from the bundle */
        get_kprobe_inst(bundle, slot, &kprobe_inst, &major_opcode);
 
+       if (unsupported_inst(template, slot, major_opcode, kprobe_inst, p))
+                       return -EINVAL;
+
        prepare_break_inst(template, slot, major_opcode, kprobe_inst, p);
 
        return 0;
@@ -335,10 +545,11 @@ static void prepare_ss(struct kprobe *p, struct pt_regs *regs)
        ia64_psr(regs)->ss = 1;
 }
 
-static int pre_kprobes_handler(struct pt_regs *regs)
+static int pre_kprobes_handler(struct die_args *args)
 {
        struct kprobe *p;
        int ret = 0;
+       struct pt_regs *regs = args->regs;
        kprobe_opcode_t *addr = (kprobe_opcode_t *)instruction_pointer(regs);
 
        preempt_disable();
@@ -351,9 +562,19 @@ static int pre_kprobes_handler(struct pt_regs *regs)
                                unlock_kprobes();
                                goto no_kprobe;
                        }
-                       arch_disarm_kprobe(p);
-                       ret = 1;
-               } else {
+                       /* We have reentered the pre_kprobe_handler(), since
+                        * another probe was hit while within the handler.
+                        * We here save the original kprobes variables and
+                        * just single step on the instruction of the new probe
+                        * without calling any user handlers.
+                        */
+                       save_previous_kprobe();
+                       set_current_kprobe(p);
+                       p->nmissed++;
+                       prepare_ss(p, regs);
+                       kprobe_status = KPROBE_REENTER;
+                       return 1;
+               } else if (args->err == __IA64_BREAK_JPROBE) {
                        /*
                         * jprobe instrumented function just completed
                         */
@@ -361,6 +582,9 @@ static int pre_kprobes_handler(struct pt_regs *regs)
                        if (p->break_handler && p->break_handler(p, regs)) {
                                goto ss_probe;
                        }
+               } else {
+                       /* Not our break */
+                       goto no_kprobe;
                }
        }
 
@@ -372,13 +596,13 @@ static int pre_kprobes_handler(struct pt_regs *regs)
        }
 
        kprobe_status = KPROBE_HIT_ACTIVE;
-       current_kprobe = p;
+       set_current_kprobe(p);
 
        if (p->pre_handler && p->pre_handler(p, regs))
                /*
                 * Our pre-handler is specifically requesting that we just
-                * do a return.  This is handling the case where the
-                * pre-handler is really our special jprobe pre-handler.
+                * do a return.  This is used for both the jprobe pre-handler
+                * and the kretprobe trampoline
                 */
                return 1;
 
@@ -397,12 +621,22 @@ static int post_kprobes_handler(struct pt_regs *regs)
        if (!kprobe_running())
                return 0;
 
-       if (current_kprobe->post_handler)
+       if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) {
+               kprobe_status = KPROBE_HIT_SSDONE;
                current_kprobe->post_handler(current_kprobe, regs, 0);
+       }
 
        resume_execution(current_kprobe, regs);
 
+       /*Restore back the original saved kprobes variables and continue. */
+       if (kprobe_status == KPROBE_REENTER) {
+               restore_previous_kprobe();
+               goto out;
+       }
+
        unlock_kprobes();
+
+out:
        preempt_enable_no_resched();
        return 1;
 }
@@ -431,7 +665,7 @@ int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
        struct die_args *args = (struct die_args *)data;
        switch(val) {
        case DIE_BREAK:
-               if (pre_kprobes_handler(args->regs))
+               if (pre_kprobes_handler(args))
                        return NOTIFY_STOP;
                break;
        case DIE_SS:
@@ -474,3 +708,14 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
        *regs = jprobe_saved_regs;
        return 1;
 }
+
+static struct kprobe trampoline_p = {
+       .pre_handler = trampoline_probe_handler
+};
+
+int __init arch_init(void)
+{
+       trampoline_p.addr =
+               (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip;
+       return register_kprobe(&trampoline_p);
+}