#include <dbi_kprobes.h>
#include <asm/dbi_kprobes.h>
+#include <asm/trampoline_arm.h>
#include <asm/traps.h>
+#include <swap_uprobes.h>
+#include <asm/swap_uprobes.h>
+#include <dbi_insn_slots.h>
+#include <dbi_kprobes_deps.h>
+#include "trampoline_thumb.h"
// FIXME:
#include <dbi_kdebug.h>
-extern struct hlist_head uprobe_insn_pages;
-kprobe_opcode_t *get_insn_slot(struct task_struct *task, int atomic);
-int arch_check_insn_arm(struct arch_specific_insn *ainsn);
-int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int uregs);
-void free_insn_slot(struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot);
-int isThumb2(kprobe_opcode_t insn);
-void pc_dep_insn_execbuf(void);
-void gen_insn_execbuf(void);
-void gen_insn_execbuf_thumb(void);
-void pc_dep_insn_execbuf_thumb(void);
-int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr);
+#define flush_insns(addr, size) \
+ 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)
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)
+{
+ return ((insn & 0xf800) == 0xe800 ||
+ (insn & 0xf800) == 0xf000 ||
+ (insn & 0xf800) == 0xf800);
+}
+
static int arch_copy_trampoline_arm_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
{
kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
*((unsigned short*)insns + 13) = 0xdeff;
*((unsigned short*)insns + 14) = addr & 0x0000ffff;
*((unsigned short*)insns + 15) = addr >> 16;
- if (!isThumb2(insn[0])) {
+ if (!is_thumb2(insn[0])) {
addr = ((unsigned int)p->addr) + 2;
*((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1;
*((unsigned short*)insns + 17) = addr >> 16;
} else {
memcpy(insns, gen_insn_execbuf_thumb, 18 * 2);
*((unsigned short*)insns + 13) = 0xdeff;
- if (!isThumb2(insn[0])) {
+ if (!is_thumb2(insn[0])) {
addr = ((unsigned int)p->addr) + 2;
*((unsigned short*)insns + 2) = insn[0];
*((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1;
return 0;
}
-int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
+int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list, int atomic)
{
int ret = 0;
+ struct kprobe *p = &up->kp;
+ struct task_struct *task = up->task;
kprobe_opcode_t insn[MAX_INSN_SIZE];
if ((unsigned long)p->addr & 0x01) {
}
p->opcode = insn[0];
- p->ainsn.insn_arm = get_insn_slot(task, atomic);
+ p->ainsn.insn_arm = alloc_insn_slot(up->sm);
if (!p->ainsn.insn_arm) {
printk("Error in %s at %d: kprobe slot allocation error (arm)\n", __FILE__, __LINE__);
return -ENOMEM;
ret = arch_copy_trampoline_arm_uprobe(p, task, 1);
if (ret) {
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
+ free_insn_slot(up->sm, p->ainsn.insn_arm);
return -EFAULT;
}
- p->ainsn.insn_thumb = get_insn_slot(task, atomic);
+ p->ainsn.insn_thumb = alloc_insn_slot(up->sm);
if (!p->ainsn.insn_thumb) {
printk("Error in %s at %d: kprobe slot allocation error (thumb)\n", __FILE__, __LINE__);
return -ENOMEM;
ret = arch_copy_trampoline_thumb_uprobe(p, task, 1);
if (ret) {
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_thumb);
+ free_insn_slot(up->sm, p->ainsn.insn_arm);
+ free_insn_slot(up->sm, p->ainsn.insn_thumb);
return -EFAULT;
}
panic("Failed to write memory %p!\n", p->addr);
}
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_thumb);
+ free_insn_slot(up->sm, p->ainsn.insn_arm);
+ free_insn_slot(up->sm, p->ainsn.insn_thumb);
return -EFAULT;
}
return ret;
}
+void arch_prepare_uretprobe_hl(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
+ ri->sp = (kprobe_opcode_t *)regs->ARM_sp;
+
+ /* Set flag of current mode */
+ ri->sp = (kprobe_opcode_t *)((long)ri->sp | !!thumb_mode(regs));
+
+ if (thumb_mode(regs)) {
+ regs->ARM_lr = (unsigned long)(ri->rp->up.kp.ainsn.insn) + 0x1b;
+ } else {
+ regs->ARM_lr = (unsigned long)(ri->rp->up.kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+ }
+}
+
+int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct uprobe *up = container_of(p, struct uprobe, kp);
+ struct ujprobe *jp = container_of(up, struct ujprobe, up);
+
+ kprobe_pre_entry_handler_t pre_entry = (kprobe_pre_entry_handler_t)jp->pre_entry;
+ entry_point_t entry = (entry_point_t)jp->entry;
+
+ if (pre_entry) {
+ p->ss_addr = (kprobe_opcode_t *)pre_entry(jp->priv_arg, regs);
+ }
+
+ if (entry) {
+ entry(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
+ regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
+ } else {
+ dbi_arch_uprobe_return();
+ }
+
+ return 0;
+}
+
+unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs)
+{
+ return thumb_mode(regs) ?
+ (unsigned long)(p->ainsn.insn) + 0x1b :
+ (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+}
+
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ regs->ARM_lr = orig_ret_addr;
+ regs->ARM_pc = orig_ret_addr & ~0x1;
+
+ if (regs->ARM_lr & 0x1)
+ regs->ARM_cpsr |= PSR_T_BIT;
+ else
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+}
+
+static int check_validity_insn(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe *kp;
+
+ if (unlikely(thumb_mode(regs))) {
+ if (p->safe_thumb == -1) {
+ goto disarm;
+ }
+
+ p->ainsn.insn = p->ainsn.insn_thumb;
+ list_for_each_entry_rcu(kp, &p->list, list) {
+ kp->ainsn.insn = p->ainsn.insn_thumb;
+ }
+ } else {
+ if (p->safe_arm == -1) {
+ goto disarm;
+ }
+
+ p->ainsn.insn = p->ainsn.insn_arm;
+ list_for_each_entry_rcu(kp, &p->list, list) {
+ kp->ainsn.insn = p->ainsn.insn_arm;
+ }
+ }
+
+ return 0;
+
+disarm:
+ printk("Error in %s at %d: we are in arm mode (!) and check "
+ "instruction was fail (%0lX instruction at %p address)!\n",
+ __FILE__, __LINE__, p->opcode, p->addr);
+
+ /* Test case when we do our actions on already running application */
+ disarm_uprobe(kp2up(p));
+ return -1;
+}
+
+static void restore_opcode_for_thumb(struct kprobe *p, struct pt_regs *regs)
+{
+ if (thumb_mode(regs) && !is_thumb2(p->opcode)) {
+ u16 tmp = p->opcode >> 16;
+ write_proc_vm_atomic(current,
+ (unsigned long)((u16*)p->addr + 1), &tmp, 2);
+ flush_insns(p->addr, 4);
+ }
+}
+
+static int uprobe_handler(struct pt_regs *regs)
+{
+ kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->ARM_pc);
+ struct task_struct *task = current;
+ pid_t tgid = task->tgid;
+ struct kprobe *p;
+
+ p = get_ukprobe(addr, tgid);
+
+ if (p == NULL) {
+ p = get_ukprobe_by_insn_slot(addr, tgid, regs);
+ if (p == NULL) {
+ printk("no_uprobe\n");
+ return 1;
+ }
+
+ trampoline_uprobe_handler(p, regs);
+ } else if (check_validity_insn(p, regs) != 0) {
+ printk("no_uprobe live\n");
+ } else {
+ restore_opcode_for_thumb(p, regs);
+
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ prepare_singlestep(p, regs);
+ }
+ }
+
+ return 0;
+}
+
+int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
+{
+ int ret;
+ unsigned long flags;
+ local_irq_save(flags);
+
+ preempt_disable();
+ ret = uprobe_handler(regs);
+ preempt_enable_no_resched();
+
+ local_irq_restore(flags);
+ return ret;
+}
+
/* userspace probes hook (arm) */
static struct undef_hook undef_hook_for_us_arm = {
.instr_mask = 0xffffffff,
.instr_val = BREAKPOINT_INSTRUCTION,
.cpsr_mask = MODE_MASK,
.cpsr_val = USR_MODE,
- .fn = kprobe_trap_handler
+ .fn = uprobe_trap_handler
};
/* userspace probes hook (thumb) */
.instr_val = BREAKPOINT_INSTRUCTION & 0x0000ffff,
.cpsr_mask = MODE_MASK,
.cpsr_val = USR_MODE,
- .fn = kprobe_trap_handler
+ .fn = uprobe_trap_handler
};
int swap_arch_init_uprobes(void)