Merge master.kernel.org:/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-drm-fsl-dcu.git] / arch / ia64 / kernel / kprobes.c
index 51217d63285ea35122d9373647e2dd40c5cd0346..4f5fd0960ba7c0c7e1c44e0c358edff198e947f0 100644 (file)
@@ -29,9 +29,9 @@
 #include <linux/slab.h>
 #include <linux/preempt.h>
 #include <linux/moduleloader.h>
+#include <linux/kdebug.h>
 
 #include <asm/pgtable.h>
-#include <asm/kdebug.h>
 #include <asm/sections.h>
 #include <asm/uaccess.h>
 
@@ -88,6 +88,7 @@ static void __kprobes update_kprobe_inst_flag(uint template, uint  slot,
 {
        p->ainsn.inst_flag = 0;
        p->ainsn.target_br_reg = 0;
+       p->ainsn.slot = slot;
 
        /* Check for Break instruction
         * Bits 37:40 Major opcode to be zero
@@ -127,48 +128,6 @@ static void __kprobes update_kprobe_inst_flag(uint template, uint  slot,
        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 __kprobes unsupported_inst(uint template, uint  slot,
-                                     uint major_opcode,
-                                     unsigned long kprobe_inst,
-                                     unsigned long 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
@@ -205,6 +164,119 @@ out:
        return ctype_unc;
 }
 
+/*
+ * In this function we check to see if the instruction
+ * on which we are inserting kprobe is supported.
+ * Returns qp value if supported
+ * Returns -EINVAL if unsupported
+ */
+static int __kprobes unsupported_inst(uint template, uint  slot,
+                                     uint major_opcode,
+                                     unsigned long kprobe_inst,
+                                     unsigned long addr)
+{
+       int qp;
+
+       qp = kprobe_inst & 0x3f;
+       if (is_cmp_ctype_unc_inst(template, slot, major_opcode, kprobe_inst)) {
+               if (slot == 1 && qp)  {
+                       printk(KERN_WARNING "Kprobes on cmp unc"
+                                       "instruction on slot 1 at <0x%lx>"
+                                       "is not supported\n", addr);
+                       return -EINVAL;
+
+               }
+               qp = 0;
+       }
+       else if (bundle_encoding[template][slot] == I) {
+               if (major_opcode == 0) {
+                       /*
+                        * 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;
+
+                       }
+               }
+               else if ((major_opcode == 5) && !(kprobe_inst & (0xFUl << 33)) &&
+                               (kprobe_inst & (0x1UL << 12))) {
+                       /* test bit instructions, tbit,tnat,tf
+                        * bit 33-36 to be equal to 0
+                        * bit 12 to be equal to 1
+                        */
+                       if (slot == 1 && qp) {
+                               printk(KERN_WARNING "Kprobes on test bit"
+                                               "instruction on slot at <0x%lx>"
+                                               "is not supported\n", addr);
+                               return -EINVAL;
+                       }
+                       qp = 0;
+               }
+       }
+       else if (bundle_encoding[template][slot] == B) {
+               if (major_opcode == 7) {
+                       /* IP-Relative Predict major code is 7 */
+                       printk(KERN_WARNING "Kprobes on IP-Relative"
+                                       "Predict is not supported\n");
+                       return -EINVAL;
+               }
+               else if (major_opcode == 2) {
+                       /* Indirect Predict, major code is 2
+                        * bit 27-32 to be equal to 10 or 11
+                        */
+                       int x6=(kprobe_inst >> 27) & 0x3F;
+                       if ((x6 == 0x10) || (x6 == 0x11)) {
+                               printk(KERN_WARNING "Kprobes on"
+                                       "Indirect Predict is not supported\n");
+                               return -EINVAL;
+                       }
+               }
+       }
+       /* kernel does not use float instruction, here for safety kprobe
+        * will judge whether it is fcmp/flass/float approximation instruction
+        */
+       else if (unlikely(bundle_encoding[template][slot] == F)) {
+               if ((major_opcode == 4 || major_opcode == 5) &&
+                               (kprobe_inst  & (0x1 << 12))) {
+                       /* fcmp/fclass unc instruction */
+                       if (slot == 1 && qp) {
+                               printk(KERN_WARNING "Kprobes on fcmp/fclass "
+                                       "instruction on slot at <0x%lx> "
+                                       "is not supported\n", addr);
+                               return -EINVAL;
+
+                       }
+                       qp = 0;
+               }
+               if ((major_opcode == 0 || major_opcode == 1) &&
+                       (kprobe_inst & (0x1UL << 33))) {
+                       /* float Approximation instruction */
+                       if (slot == 1 && qp) {
+                               printk(KERN_WARNING "Kprobes on float Approx "
+                                       "instr at <0x%lx> is not supported\n",
+                                               addr);
+                               return -EINVAL;
+                       }
+                       qp = 0;
+               }
+       }
+       return qp;
+}
+
 /*
  * In this function we override the bundle with
  * the break instruction at the given slot.
@@ -212,20 +284,17 @@ out:
 static void __kprobes prepare_break_inst(uint template, uint  slot,
                                         uint major_opcode,
                                         unsigned long kprobe_inst,
-                                        struct kprobe *p)
+                                        struct kprobe *p,
+                                        int qp)
 {
        unsigned long break_inst = BREAK_INST;
        bundle_t *bundle = &p->opcode.bundle;
 
        /*
         * Copy the original kprobe_inst qualifying predicate(qp)
-        * 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
+        * to the break instruction
         */
-       if (!is_cmp_ctype_unc_inst(template, slot, major_opcode, kprobe_inst))
-               break_inst |= (0x3f & kprobe_inst);
+       break_inst |= qp;
 
        switch (slot) {
          case 0:
@@ -296,12 +365,6 @@ static int __kprobes valid_kprobe_addr(int template, int slot,
                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;
 }
 
@@ -381,7 +444,8 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
                        break;
        }
 
-       BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
+       kretprobe_assert(ri, orig_ret_address, trampoline_address);
+
        regs->cr_iip = orig_ret_address;
 
        reset_current_kprobe();
@@ -401,23 +465,13 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
 }
 
 /* Called with kretprobe_lock held */
-void __kprobes arch_prepare_kretprobe(struct kretprobe *rp,
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
                                      struct pt_regs *regs)
 {
-       struct kretprobe_instance *ri;
+       ri->ret_addr = (kprobe_opcode_t *)regs->b0;
 
-       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++;
-       }
+       /* Replace the return addr with trampoline addr */
+       regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip;
 }
 
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
@@ -427,6 +481,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        unsigned long kprobe_inst=0;
        unsigned int slot = addr & 0xf, template, major_opcode = 0;
        bundle_t *bundle;
+       int qp;
 
        bundle = &((kprobe_opcode_t *)kprobe_addr)->bundle;
        template = bundle->quad0.template;
@@ -441,9 +496,9 @@ int __kprobes 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, addr))
-                       return -EINVAL;
-
+       qp = unsupported_inst(template, slot, major_opcode, kprobe_inst, addr);
+       if (qp < 0)
+               return -EINVAL;
 
        p->ainsn.insn = get_insn_slot();
        if (!p->ainsn.insn)
@@ -451,37 +506,63 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        memcpy(&p->opcode, kprobe_addr, sizeof(kprobe_opcode_t));
        memcpy(p->ainsn.insn, kprobe_addr, sizeof(kprobe_opcode_t));
 
-       prepare_break_inst(template, slot, major_opcode, kprobe_inst, p);
+       prepare_break_inst(template, slot, major_opcode, kprobe_inst, p, qp);
 
        return 0;
 }
 
 void __kprobes arch_arm_kprobe(struct kprobe *p)
 {
-       unsigned long addr = (unsigned long)p->addr;
-       unsigned long arm_addr = addr & ~0xFULL;
+       unsigned long arm_addr;
+       bundle_t *src, *dest;
+
+       arm_addr = ((unsigned long)p->addr) & ~0xFUL;
+       dest = &((kprobe_opcode_t *)arm_addr)->bundle;
+       src = &p->opcode.bundle;
 
        flush_icache_range((unsigned long)p->ainsn.insn,
                        (unsigned long)p->ainsn.insn + sizeof(kprobe_opcode_t));
-       memcpy((char *)arm_addr, &p->opcode, sizeof(kprobe_opcode_t));
+       switch (p->ainsn.slot) {
+               case 0:
+                       dest->quad0.slot0 = src->quad0.slot0;
+                       break;
+               case 1:
+                       dest->quad1.slot1_p1 = src->quad1.slot1_p1;
+                       break;
+               case 2:
+                       dest->quad1.slot2 = src->quad1.slot2;
+                       break;
+       }
        flush_icache_range(arm_addr, arm_addr + sizeof(kprobe_opcode_t));
 }
 
 void __kprobes arch_disarm_kprobe(struct kprobe *p)
 {
-       unsigned long addr = (unsigned long)p->addr;
-       unsigned long arm_addr = addr & ~0xFULL;
+       unsigned long arm_addr;
+       bundle_t *src, *dest;
 
+       arm_addr = ((unsigned long)p->addr) & ~0xFUL;
+       dest = &((kprobe_opcode_t *)arm_addr)->bundle;
        /* p->ainsn.insn contains the original unaltered kprobe_opcode_t */
-       memcpy((char *) arm_addr, (char *) p->ainsn.insn,
-                                        sizeof(kprobe_opcode_t));
+       src = &p->ainsn.insn->bundle;
+       switch (p->ainsn.slot) {
+               case 0:
+                       dest->quad0.slot0 = src->quad0.slot0;
+                       break;
+               case 1:
+                       dest->quad1.slot1_p1 = src->quad1.slot1_p1;
+                       break;
+               case 2:
+                       dest->quad1.slot2 = src->quad1.slot2;
+                       break;
+       }
        flush_icache_range(arm_addr, arm_addr + sizeof(kprobe_opcode_t));
 }
 
 void __kprobes arch_remove_kprobe(struct kprobe *p)
 {
        mutex_lock(&kprobe_mutex);
-       free_insn_slot(p->ainsn.insn);
+       free_insn_slot(p->ainsn.insn, 0);
        mutex_unlock(&kprobe_mutex);
 }
 /*
@@ -807,7 +888,9 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
        switch(val) {
        case DIE_BREAK:
                /* err is break number from ia64_bad_break() */
-               if (args->err == 0x80200 || args->err == 0x80300 || args->err == 0)
+               if ((args->err >> 12) == (__IA64_BREAK_KPROBE >> 12)
+                       || args->err == __IA64_BREAK_JPROBE
+                       || args->err == 0)
                        if (pre_kprobes_handler(args))
                                ret = NOTIFY_STOP;
                break;
@@ -851,7 +934,7 @@ static void ia64_get_bsp_cfm(struct unw_frame_info *info, void *arg)
                        return;
                }
        } while (unw_unwind(info) >= 0);
-       lp->bsp = 0;
+       lp->bsp = NULL;
        lp->cfm = 0;
        return;
 }
@@ -929,3 +1012,12 @@ int __init arch_init_kprobes(void)
                (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip;
        return register_kprobe(&trampoline_p);
 }
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+       if (p->addr ==
+               (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip)
+               return 1;
+
+       return 0;
+}