ARM64: implement uretprobe 06/111106/3
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Fri, 1 Jul 2016 18:48:20 +0000 (21:48 +0300)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Mon, 30 Jan 2017 16:43:21 +0000 (19:43 +0300)
Change-Id: I9775576f8901eac71e62356ee0ae56942f1e5406
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
uprobe/arch/arm64/swap-asm/swap_uprobes.c
uprobe/arch/arm64/swap-asm/swap_uprobes.h

index b75cb28..f2e7a9a 100644 (file)
 
 #define BRK_BP                 0x45
 #define BRK_PSEUDO_SS          0x54
+#define BRK_URP                        0x67
 #define BRK64_OPCODE_BP                MAKE_BRK(BRK_BP)
 #define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS)
+#define BRK64_OPCODE_URP       MAKE_BRK(BRK_URP)
 
 
 struct uprobe_ctlblk {
@@ -62,6 +64,72 @@ static void reset_current_uprobe(void)
 }
 
 
+int arch_prepare_uretprobe(struct uretprobe_instance *ri,
+                          struct pt_regs *regs)
+{
+       unsigned long bp_addr = (unsigned long)(ri->rp->up.ainsn.insn +
+                                               URP_RET_BREAK_IDX);
+
+       ri->sp = (kprobe_opcode_t *)regs->sp;
+
+       /* replace the return address (regs[30] - lr) */
+       ri->ret_addr = (kprobe_opcode_t *)regs->regs[30];
+       regs->regs[30] = bp_addr;
+
+       return 0;
+}
+
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+       regs->pc = orig_ret_addr;
+}
+
+static int make_urp_arm64(struct uprobe *p)
+{
+       u32 *utramp, urp_brk;
+
+       if (p->ainsn.insn == NULL) {
+               utramp = swap_slot_alloc(p->sm);
+               if (utramp == NULL)
+                       return -ENOMEM;
+
+               p->ainsn.insn = utramp;
+       }
+
+       utramp = p->ainsn.insn;
+       urp_brk = BRK64_OPCODE_URP;
+
+       if (!write_proc_vm_atomic(p->task,
+                                 (long)&utramp[URP_RET_BREAK_IDX],
+                                 &urp_brk, sizeof(urp_brk))) {
+               pr_err("%s failed to write memory %p!\n", __func__, utramp);
+       }
+
+       add_uprobe_table(p);
+
+       return 0;
+}
+
+static enum dbg_code urp_handler(struct pt_regs *regs, unsigned int esr)
+{
+       struct uprobe *p;
+       unsigned long pc = regs->pc;
+       unsigned long insn_addr = pc - sizeof(u32) * URP_RET_BREAK_IDX;
+
+       p = get_uprobe_by_insn_slot((void *)insn_addr, current->tgid, regs);
+       if (p == NULL) {
+               pr_err("no_uretprobe: Not one of ours: let "
+                      "kernel handle it %lx\n", pc);
+               return DBG_ERROR;
+       }
+
+       local_irq_enable();
+       trampoline_uprobe_handler(p, regs);
+
+       return DBG_HANDLED;
+}
+
+
 int arch_arm_uprobe(struct uprobe *p)
 {
        uprobe_opcode_t insn = BRK64_OPCODE_BP;
@@ -247,6 +315,12 @@ static enum dbg_code uprobe_handler_arm64(struct pt_regs *regs)
                                pr_err("no_uprobe live\n");
                                goto out_ok;
                        }
+
+                       if (make_urp_arm64(p)) {
+                               disarm_uprobe(p, p->task);
+                               pr_err("no_uretprobe\n");
+                               goto out_ok;
+                       }
                }
 
                if (!p->pre_handler || !p->pre_handler(p, regs))
@@ -298,6 +372,14 @@ static struct brk_hook dbg_up_ss = {
        .fn = uprobe_ss_handler,
 };
 
+static struct brk_hook dbg_urp_bp = {
+       .spsr_mask = PSR_MODE_MASK,
+       .spsr_val = PSR_MODE_EL0t,
+       .esr_mask = DBG_BRK_ESR_MASK,
+       .esr_val = DBG_BRK_ESR(BRK_URP),
+       .fn = urp_handler,
+};
+
 
 int swap_arch_init_uprobes(void)
 {
@@ -309,12 +391,14 @@ int swap_arch_init_uprobes(void)
 
        dbg_brk_hook_reg(&dbg_up_ss);
        dbg_brk_hook_reg(&dbg_up_bp);
+       dbg_brk_hook_reg(&dbg_urp_bp);
 
        return 0;
 }
 
 void swap_arch_exit_uprobes(void)
 {
+       dbg_brk_hook_unreg(&dbg_urp_bp);
        dbg_brk_hook_unreg(&dbg_up_bp);
        dbg_brk_hook_unreg(&dbg_up_ss);
        swap_td_raw_unreg(&td_raw);
index bfa205b..bca4f84 100644 (file)
@@ -26,8 +26,9 @@
 #include <linux/types.h>
 
 
-#define UP_TRAMP_INSN_CNT  2 /* | opcode | ss_bp | */
+#define UP_TRAMP_INSN_CNT  3 /* | opcode | ss_bp | urp_bp | */
 #define UPROBES_TRAMP_LEN  (UP_TRAMP_INSN_CNT * 4) /* 4 - instruction size */
+#define URP_RET_BREAK_IDX  2
 
 
 struct uprobe;
@@ -127,18 +128,9 @@ static inline unsigned long arch_get_trampoline_addr(struct uprobe *p,
        return 0;
 }
 
-static inline void arch_set_orig_ret_addr(unsigned long orig_ret_addr,
-                                         struct pt_regs *regs)
-{
-       WARN(1, "not implemented"); /* FIXME: to implement */
-}
-
-static inline int arch_prepare_uretprobe(struct uretprobe_instance *ri,
-                                        struct pt_regs *regs)
-{
-       WARN(1, "not implemented"); /* FIXME: to implement */
-       return 0;
-}
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
+int arch_prepare_uretprobe(struct uretprobe_instance *ri,
+                          struct pt_regs *regs);
 
 static inline int arch_opcode_analysis_uretprobe(struct uretprobe *rp)
 {