[FIX] branch instrumentation for KS 55/14155/3
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 14 Jan 2014 10:20:30 +0000 (14:20 +0400)
committerDmitry Kovalenko <d.kovalenko@samsung.com>
Thu, 27 Mar 2014 06:01:03 +0000 (23:01 -0700)
Change-Id: I1f7f3ea5abe5ff318b2bda83827a6d5967936ba1
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
kprobe/arch/asm-arm/dbi_kprobes.c
kprobe/arch/asm-arm/dbi_kprobes.h
kprobe/dbi_insn_slots.h
uprobe/arch/asm-arm/swap_uprobes.c

index 92ede61..0ae7308 100644 (file)
 
 #define SUPRESS_BUG_MESSAGES
 
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
 extern struct kprobe * per_cpu__current_kprobe;
 extern struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
 
 static void (*__swap_register_undef_hook)(struct undef_hook *hook);
 static void (*__swap_unregister_undef_hook)(struct undef_hook *hook);
 
-int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int uregs)
+static unsigned long get_addr_b(unsigned long insn, unsigned long addr)
+{
+       /* real position less then PC by 8 */
+       return (kprobe_opcode_t)((long)addr + 8 + branch_displacement(insn));
+}
+
+static int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns,
+                                   kprobe_opcode_t insn, int uregs)
 {
        int i;
 
@@ -111,9 +121,8 @@ int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int u
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(prep_pc_dep_insn_execbuf);
 
-int arch_check_insn_arm(unsigned long insn)
+static int arch_check_insn_arm(unsigned long insn)
 {
        /* check instructions that can change PC by nature */
        if (
@@ -148,97 +157,161 @@ bad_insn:
        printk("Bad insn arch_check_insn_arm: %lx\n", insn);
        return -EFAULT;
 }
-EXPORT_SYMBOL_GPL(arch_check_insn_arm);
 
-int arch_prepare_kprobe(struct kprobe *p, struct slot_manager *sm)
+static int make_branch_tarmpoline(unsigned long addr, unsigned long insn,
+                                 unsigned long *tramp)
 {
-       kprobe_opcode_t insns[KPROBES_TRAMP_LEN];
-       int uregs, pc_dep, ret = 0;
-       kprobe_opcode_t insn[MAX_INSN_SIZE];
-       struct arch_specific_insn ainsn;
-
-       /* insn: must be on special executable page on i386. */
-       p->ainsn.insn = alloc_insn_slot(sm);
-       if (!p->ainsn.insn)
-               return -ENOMEM;
+       int ok = 0;
+
+       /* B */
+       if (ARM_INSN_MATCH(B, insn) &&
+           !ARM_INSN_MATCH(BLX1, insn)) {
+               /* B check can be false positive on BLX1 instruction */
+               memcpy(tramp, b_cond_insn_execbuf, KPROBES_TRAMP_LEN);
+               tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+               tramp[0] |= insn & 0xf0000000;
+               tramp[6] = get_addr_b(insn, addr);
+               tramp[7] = addr + 4;
+               ok = 1;
+       /* BX, BLX (Rm) */
+       } else if (ARM_INSN_MATCH(BX, insn) ||
+                  ARM_INSN_MATCH(BLX2, insn)) {
+               memcpy(tramp, b_r_insn_execbuf, KPROBES_TRAMP_LEN);
+               tramp[0] = insn;
+               tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+               tramp[7] = addr + 4;
+               ok = 1;
+       /* BL, BLX (Off) */
+       } else if (ARM_INSN_MATCH(BLX1, insn)) {
+               memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN);
+               tramp[0] |= 0xe0000000;
+               tramp[1] |= 0xe0000000;
+               tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+               tramp[6] = get_addr_b(insn, addr) +
+                          2 * (insn & 01000000) + 1; /* jump to thumb */
+               tramp[7] = addr + 4;
+               ok = 1;
+       /* BL */
+       } else if (ARM_INSN_MATCH(BL, insn)) {
+               memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN);
+               tramp[0] |= insn & 0xf0000000;
+               tramp[1] |= insn & 0xf0000000;
+               tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+               tramp[6] = get_addr_b(insn, addr);
+               tramp[7] = addr + 4;
+               ok = 1;
+       }
 
-       memcpy(insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
-       ainsn.insn = insn;
-       ret = arch_check_insn_arm(insn[0]);
-       if (!ret) {
-               p->opcode = *p->addr;
-               uregs = pc_dep = 0;
-
-               // Rn, Rm ,Rd
-               if (ARM_INSN_MATCH(DPIS, insn[0]) || ARM_INSN_MATCH(LRO, insn[0]) ||
-                   ARM_INSN_MATCH(SRO, insn[0])) {
-                       uregs = 0xb;
-                       if ((ARM_INSN_REG_RN(insn[0]) == 15) || (ARM_INSN_REG_RM(insn[0]) == 15) ||
-                           (ARM_INSN_MATCH(SRO, insn[0]) && (ARM_INSN_REG_RD(insn[0]) == 15))) {
-                               DBPRINTF ("Unboostable insn %lx, DPIS/LRO/SRO\n", insn[0]);
-                               pc_dep = 1;
-                       }
+       return ok;
+}
 
-               // Rn ,Rd
-               } else if( ARM_INSN_MATCH(DPI, insn[0]) || ARM_INSN_MATCH(LIO, insn[0]) ||
-                          ARM_INSN_MATCH(SIO, insn[0])) {
-                       uregs = 0x3;
-                       if ((ARM_INSN_REG_RN(insn[0]) == 15) || (ARM_INSN_MATCH(SIO, insn[0]) &&
-                           (ARM_INSN_REG_RD (insn[0]) == 15))) {
-                               pc_dep = 1;
-                               DBPRINTF ("Unboostable insn %lx/%p, DPI/LIO/SIO\n", insn[0], p);
-                       }
-               // Rn, Rm, Rs
-               } else if (ARM_INSN_MATCH(DPRS, insn[0])) {
-                       uregs = 0xd;
-                       if ((ARM_INSN_REG_RN(insn[0]) == 15) || (ARM_INSN_REG_RM(insn[0]) == 15) ||
-                           (ARM_INSN_REG_RS (insn[0]) == 15)) {
-                               pc_dep = 1;
-                               DBPRINTF ("Unboostable insn %lx, DPRS\n", insn[0]);
-                       }
-               // register list
-               } else if(ARM_INSN_MATCH(SM, insn[0])) {
-                       uregs = 0x10;
-                       if (ARM_INSN_REG_MR(insn[0], 15)) {
-                               DBPRINTF ("Unboostable insn %lx, SM\n", insn[0]);
-                               pc_dep = 1;
-                       }
+int arch_make_trampoline_arm(unsigned long addr, unsigned long insn,
+                            unsigned long *tramp)
+{
+       int ret, uregs, pc_dep;
+
+       if (addr & 0x03) {
+               printk("Error in %s at %d: attempt to register uprobe "
+                      "at an unaligned address\n", __FILE__, __LINE__);
+               return -EINVAL;
+       }
+
+       ret = arch_check_insn_arm(insn);
+       if (ret)
+               return ret;
+
+       if (make_branch_tarmpoline(addr, insn, tramp))
+               return 0;
+
+       uregs = pc_dep = 0;
+       /* Rn, Rm ,Rd */
+       if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) ||
+           ARM_INSN_MATCH(SRO, insn)) {
+               uregs = 0xb;
+               if ((ARM_INSN_REG_RN(insn) == 15) ||
+                   (ARM_INSN_REG_RM(insn) == 15) ||
+                   (ARM_INSN_MATCH(SRO, insn) &&
+                    (ARM_INSN_REG_RD(insn) == 15))) {
+                       pc_dep = 1;
                }
+       /* Rn ,Rd */
+       } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) ||
+                  ARM_INSN_MATCH(SIO, insn)) {
+               uregs = 0x3;
+               if ((ARM_INSN_REG_RN(insn) == 15) ||
+                   (ARM_INSN_MATCH(SIO, insn) &&
+                   (ARM_INSN_REG_RD(insn) == 15))) {
+                       pc_dep = 1;
+               }
+       /* Rn, Rm, Rs */
+       } else if (ARM_INSN_MATCH(DPRS, insn)) {
+               uregs = 0xd;
+               if ((ARM_INSN_REG_RN(insn) == 15) ||
+                   (ARM_INSN_REG_RM(insn) == 15) ||
+                   (ARM_INSN_REG_RS(insn) == 15)) {
+                       pc_dep = 1;
+               }
+       /* register list */
+       } else if (ARM_INSN_MATCH(SM, insn)) {
+               uregs = 0x10;
+               if (ARM_INSN_REG_MR(insn, 15)) {
+                       pc_dep = 1;
+               }
+       }
 
-               // check instructions that can write result to SP andu uses PC
-               if (pc_dep  && (ARM_INSN_REG_RD(ainsn.insn[0]) == 13)) {
-                       free_insn_slot(sm, p->ainsn.insn);
-                       ret = -EFAULT;
-               } else {
-                       if (uregs && pc_dep) {
-                               memcpy(insns, pc_dep_insn_execbuf, sizeof(insns));
-                               if (prep_pc_dep_insn_execbuf(insns, insn[0], uregs) != 0) {
-                                       DBPRINTF ("failed to prepare exec buffer for insn %lx!", insn[0]);
-                                       free_insn_slot(sm, p->ainsn.insn);
-                                       return -EINVAL;
-                               }
-                               insns[6] = (kprobe_opcode_t)(p->addr + 2);
-                       } else {
-                               memcpy(insns, gen_insn_execbuf, sizeof(insns));
-                               insns[KPROBES_TRAMP_INSN_IDX] = insn[0];
-                       }
-                       insns[7] = (kprobe_opcode_t)(p->addr + 1);
-                       DBPRINTF ("arch_prepare_kprobe: insn %lx", insn[0]);
-                       DBPRINTF ("arch_prepare_kprobe: to %p - %lx %lx %lx %lx %lx %lx %lx %lx %lx",
-                                 p->ainsn.insn, insns[0], insns[1], insns[2], insns[3], insns[4],
-                                 insns[5], insns[6], insns[7], insns[8]);
-                       memcpy(p->ainsn.insn, insns, sizeof(insns));
-                       flush_icache_range((long unsigned)p->ainsn.insn, (long unsigned)(p->ainsn.insn) + sizeof(insns));
-#ifdef BOARD_tegra
-                       flush_cache_all();
-#endif
+       /* check instructions that can write result to SP and uses PC */
+       if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) {
+               printk("Error in %s at %d: instruction check failed (arm)\n",
+                      __FILE__, __LINE__);
+               return -EFAULT;
+       }
+
+       if (unlikely(uregs && pc_dep)) {
+               memcpy(tramp, pc_dep_insn_execbuf, KPROBES_TRAMP_LEN);
+               if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) {
+                       printk("Error in %s at %d: failed "
+                              "to prepare exec buffer for insn %lx!",
+                              __FILE__, __LINE__, insn);
+                       return -EINVAL;
                }
+
+               tramp[6] = addr + 8;
        } else {
-               free_insn_slot(sm, p->ainsn.insn);
-               printk("arch_prepare_kprobe: instruction 0x%lx not instrumentation, addr=0x%p\n", insn[0], p->addr);
+               memcpy(tramp, gen_insn_execbuf, KPROBES_TRAMP_LEN);
+               tramp[KPROBES_TRAMP_INSN_IDX] = insn;
        }
 
-       return ret;
+       /* TODO: remove for kprobe */
+       tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+       tramp[7] = addr + 4;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(arch_make_trampoline_arm);
+
+int arch_prepare_kprobe(struct kprobe *p, struct slot_manager *sm)
+{
+       unsigned long addr = (unsigned long)p->addr;
+       unsigned long insn = p->opcode = *p->addr;
+       unsigned long *tramp;
+       int ret;
+
+       tramp = alloc_insn_slot(sm);
+       if (tramp == NULL)
+               return -ENOMEM;
+
+       ret = arch_make_trampoline_arm(addr, insn, tramp);
+       if (ret) {
+               free_insn_slot(sm, tramp);
+               return ret;
+       }
+
+       flush_icache_range((unsigned long)tramp,
+                          (unsigned long)tramp + KPROBES_TRAMP_LEN);
+
+       p->ainsn.insn = tramp;
+
+       return 0;
 }
 
 void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
index 2e2c85d..3ce451c 100644 (file)
@@ -52,13 +52,15 @@ typedef unsigned long kprobe_opcode_t;
 
 #define MAX_INSN_SIZE                   1
 
-# define UPROBES_TRAMP_LEN              9
+#define UPROBES_TRAMP_LEN              9 * 4
 # define UPROBES_TRAMP_INSN_IDX         2
 # define UPROBES_TRAMP_SS_BREAK_IDX     4
 # define UPROBES_TRAMP_RET_BREAK_IDX    5
-# define KPROBES_TRAMP_LEN              9
+#define KPROBES_TRAMP_LEN              9 * 4
 # define KPROBES_TRAMP_INSN_IDX         UPROBES_TRAMP_INSN_IDX
 # define KPROBES_TRAMP_SS_BREAK_IDX     UPROBES_TRAMP_SS_BREAK_IDX
+
+/* TODO: remove (not needed for kprobe) */
 # define KPROBES_TRAMP_RET_BREAK_IDX   UPROBES_TRAMP_RET_BREAK_IDX
 
 #define UREGS_OFFSET 8
@@ -482,8 +484,8 @@ static inline int arch_init_module_deps(void)
        return 0;
 }
 
-int arch_check_insn_arm(unsigned long insn);
-int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int uregs);
+int arch_make_trampoline_arm(unsigned long addr, unsigned long insn,
+                            unsigned long *tramp);
 
 struct slot_manager;
 struct kretprobe;
index 39e63a3..db5ad62 100644 (file)
@@ -51,7 +51,7 @@
 #include <linux/types.h>
 
 struct slot_manager {
-       unsigned long slot_size;
+       unsigned long slot_size;        /* FIXME: allocated in long (4 byte) */
        void *(*alloc)(struct slot_manager *sm);
        void (*free)(struct slot_manager *sm, void *ptr);
        struct hlist_head page_list;
index 526b6a7..48ca993 100644 (file)
@@ -42,9 +42,6 @@
        flush_icache_range((unsigned long)(addr),               \
                           (unsigned long)(addr) + (size))
 
-#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
-#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
-
 static inline long branch_t16_dest(kprobe_opcode_t insn, unsigned int insn_addr)
 {
        long offset = insn & 0x3ff;
@@ -79,12 +76,6 @@ static inline long cbz_t16_dest(kprobe_opcode_t insn, unsigned int insn_addr)
        return insn_addr + 4 + i + offset;
 }
 
-static unsigned long get_addr_b(unsigned long insn, unsigned long addr)
-{
-       /* real position less then PC by 8 */
-       return (kprobe_opcode_t)((long)addr + 8 + branch_displacement(insn));
-}
-
 /* is instruction Thumb2 and NOT a branch, etc... */
 static int is_thumb2(kprobe_opcode_t insn)
 {
@@ -95,126 +86,16 @@ static int is_thumb2(kprobe_opcode_t insn)
 
 static int arch_copy_trampoline_arm_uprobe(struct uprobe *up)
 {
+       int ret;
        struct kprobe *p = up2kp(up);
-       int uregs, pc_dep;
        unsigned long insn = p->opcode;
        unsigned long vaddr = (unsigned long)p->addr;
        unsigned long *tramp = up->atramp.tramp_arm;
-       enum { tramp_len = sizeof(up->atramp.tramp_arm) };
-
-       p->safe_arm = 1;
-       if (vaddr & 0x03) {
-               printk("Error in %s at %d: attempt to register uprobe "
-                      "at an unaligned address\n", __FILE__, __LINE__);
-               return -EINVAL;
-       }
 
-       if (!arch_check_insn_arm(insn)) {
-               p->safe_arm = 0;
-       }
+       ret = arch_make_trampoline_arm(vaddr, insn, tramp);
+       p->safe_arm = !!ret;
 
-       uregs = pc_dep = 0;
-       /* Rn, Rm ,Rd */
-       if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) ||
-           ARM_INSN_MATCH(SRO, insn)) {
-               uregs = 0xb;
-               if ((ARM_INSN_REG_RN(insn) == 15) ||
-                   (ARM_INSN_REG_RM(insn) == 15) ||
-                   (ARM_INSN_MATCH(SRO, insn) &&
-                    (ARM_INSN_REG_RD(insn) == 15))) {
-                       DBPRINTF("Unboostable insn %lx, DPIS/LRO/SRO\n", insn);
-                       pc_dep = 1;
-               }
-       /* Rn ,Rd */
-       } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) ||
-                  ARM_INSN_MATCH(SIO, insn)) {
-               uregs = 0x3;
-               if ((ARM_INSN_REG_RN(insn) == 15) ||
-                   (ARM_INSN_MATCH(SIO, insn) &&
-                   (ARM_INSN_REG_RD(insn) == 15))) {
-                       pc_dep = 1;
-                       DBPRINTF("Unboostable insn %lx/%p, DPI/LIO/SIO\n",
-                                insn, p);
-               }
-       /* Rn, Rm, Rs */
-       } else if (ARM_INSN_MATCH(DPRS, insn)) {
-               uregs = 0xd;
-               if ((ARM_INSN_REG_RN(insn) == 15) ||
-                   (ARM_INSN_REG_RM(insn) == 15) ||
-                   (ARM_INSN_REG_RS(insn) == 15)) {
-                       pc_dep = 1;
-                       DBPRINTF("Unboostable insn %lx, DPRS\n", insn);
-               }
-       /* register list */
-       } else if (ARM_INSN_MATCH(SM, insn)) {
-               uregs = 0x10;
-               if (ARM_INSN_REG_MR (insn, 15)) {
-                       DBPRINTF ("Unboostable insn %lx, SM\n", insn);
-                       pc_dep = 1;
-               }
-       }
-
-       /* check instructions that can write result to SP andu uses PC */
-       if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) {
-               printk("Error in %s at %d: instruction check failed (arm)\n",
-                      __FILE__, __LINE__);
-               p->safe_arm = 1;
-       }
-
-       if (unlikely(uregs && pc_dep)) {
-               memcpy(tramp, pc_dep_insn_execbuf, tramp_len);
-               if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) {
-                       printk("Error in %s at %d: failed "
-                              "to prepare exec buffer for insn %lx!",
-                              __FILE__, __LINE__, insn);
-                       p->safe_arm = 1;
-               }
-
-               tramp[6] = vaddr + 8;
-       } else {
-               memcpy(tramp, gen_insn_execbuf, tramp_len);
-               tramp[UPROBES_TRAMP_INSN_IDX] = insn;
-       }
-
-       tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
-       tramp[7] = vaddr + 4;
-
-       /* B */
-       if (ARM_INSN_MATCH(B, insn) &&
-           !ARM_INSN_MATCH(BLX1, insn)) {
-               /* B check can be false positive on BLX1 instruction */
-               memcpy(tramp, b_cond_insn_execbuf, tramp_len);
-               tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
-               tramp[0] |= insn & 0xf0000000;
-               tramp[6] = get_addr_b(insn, vaddr);
-               tramp[7] = vaddr + 4;
-       /* BX, BLX (Rm) */
-       } else if (ARM_INSN_MATCH(BX, insn) ||
-                  ARM_INSN_MATCH(BLX2, insn)) {
-               memcpy(tramp, b_r_insn_execbuf, tramp_len);
-               tramp[0] = insn;
-               tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
-               tramp[7] = vaddr + 4;
-       /* BL, BLX (Off) */
-       } else if (ARM_INSN_MATCH(BLX1, insn)) {
-               memcpy(tramp, blx_off_insn_execbuf, tramp_len);
-               tramp[0] |= 0xe0000000;
-               tramp[1] |= 0xe0000000;
-               tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
-               tramp[6] = get_addr_b(insn, vaddr) +
-                          2 * (insn & 01000000) + 1; /* jump to thumb */
-               tramp[7] = vaddr + 4;
-       /* BL */
-       } else if (ARM_INSN_MATCH(BL, insn)) {
-               memcpy(tramp, blx_off_insn_execbuf, tramp_len);
-               tramp[0] |= insn & 0xf0000000;
-               tramp[1] |= insn & 0xf0000000;
-               tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
-               tramp[6] = get_addr_b(insn, vaddr);
-               tramp[7] = vaddr + 4;
-       }
-
-       return 0;
+       return ret;
 }
 
 static int arch_check_insn_thumb(unsigned long insn)