#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 {
}
+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;
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))
.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)
{
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);
#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;
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)
{