[IMPROVE] create slot_manager
[kernel/swap-modules.git] / uprobe / arch / asm-arm / swap_uprobes.c
index 8c4f0fa..8ae763b 100644 (file)
@@ -1,21 +1,21 @@
 #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, struct hlist_head *page_list, 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);
-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)
 
@@ -536,9 +536,11 @@ static int arch_copy_trampoline_thumb_uprobe(struct kprobe *p, struct task_struc
        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) {
@@ -551,7 +553,7 @@ int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
        }
 
        p->opcode = insn[0];
-       p->ainsn.insn_arm = get_insn_slot(task, &uprobe_insn_pages, 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;
@@ -559,11 +561,11 @@ int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
 
        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, &uprobe_insn_pages, 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;
@@ -571,8 +573,8 @@ int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
 
        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;
        }
 
@@ -583,8 +585,8 @@ int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
                        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;
        }
@@ -592,127 +594,136 @@ int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
        return ret;
 }
 
-static int check_validity_insn(struct kprobe *p, struct pt_regs *regs, struct task_struct *task)
+void arch_prepare_uretprobe_hl(struct uretprobe_instance *ri,
+                              struct pt_regs *regs)
 {
-       struct kprobe *kp;
+       ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
+       ri->sp = (kprobe_opcode_t *)regs->ARM_sp;
 
-       if (unlikely(thumb_mode(regs))) {
-               if (p->safe_thumb != -1) {
-                       p->ainsn.insn = p->ainsn.insn_thumb;
-                       list_for_each_entry_rcu(kp, &p->list, list) {
-                               kp->ainsn.insn = p->ainsn.insn_thumb;
-                       }
-               } else {
-                       printk("Error in %s at %d: we are in thumb 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
-                       arch_disarm_uprobe(p, task);
-                       return -1;
-               }
+       /* 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 {
-               if (p->safe_arm != -1) {
-                       p->ainsn.insn = p->ainsn.insn_arm;
-                       list_for_each_entry_rcu(kp, &p->list, list) {
-                               kp->ainsn.insn = p->ainsn.insn_arm;
-                       }
-               } else {
-                       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
-                       arch_disarm_uprobe(p, task);
-                       return -1;
-               }
+               regs->ARM_lr = (unsigned long)(ri->rp->up.kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
        }
-
-       return 0;
 }
 
-static int uprobe_handler(struct pt_regs *regs)
+int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
 {
-       int err_out = 0;
-       char *msg_out = NULL;
-       struct task_struct *task = current;
-       pid_t tgid = task->tgid;
-       kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->ARM_pc);
-       struct kprobe *p = NULL;
-       int ret = 0, retprobe = 0;
-       struct kprobe_ctlblk *kcb;
+       struct uprobe *up = container_of(p, struct uprobe, kp);
+       struct ujprobe *jp = container_of(up, struct ujprobe, up);
 
-#ifdef SUPRESS_BUG_MESSAGES
-       int swap_oops_in_progress;
-       // oops_in_progress used to avoid BUG() messages that slow down kprobe_handler() execution
-       swap_oops_in_progress = oops_in_progress;
-       oops_in_progress = 1;
-#endif
+       kprobe_pre_entry_handler_t pre_entry = (kprobe_pre_entry_handler_t)jp->pre_entry;
+       entry_point_t entry = (entry_point_t)jp->entry;
 
-       p = get_kprobe(addr, tgid);
+       if (pre_entry) {
+               p->ss_addr = (kprobe_opcode_t *)pre_entry(jp->priv_arg, regs);
+       }
 
-       if (p && (check_validity_insn(p, regs, task) != 0)) {
-               goto no_uprobe_live;
+       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();
        }
 
-       /* We're in an interrupt, but this is clear and BUG()-safe. */
-       kcb = get_kprobe_ctlblk();
+       return 0;
+}
 
-       if (p == NULL) {
-               p = get_kprobe_by_insn_slot(addr, tgid, regs);
-               if (p == NULL) {
-                       /* Not one of ours: let kernel handle it */
-                       goto no_uprobe;
-               }
+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);
+}
 
-               retprobe = 1;
-       }
+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;
 
-       /* restore opcode for thumb app */
-       if (thumb_mode(regs)) {
-               if (!is_thumb2(p->opcode)) {
-                       unsigned long tmp = p->opcode >> 16;
-                       write_proc_vm_atomic(task, (unsigned long)((unsigned short*)p->addr + 1), &tmp, 2);
+       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;
 
-                       // "2*sizeof(kprobe_opcode_t)" - strange. Should be "sizeof(kprobe_opcode_t)", need to test
-                       flush_icache_range((unsigned int)p->addr, ((unsigned int)p->addr) + (2 * sizeof(kprobe_opcode_t)));
+       if (unlikely(thumb_mode(regs))) {
+               if (p->safe_thumb == -1) {
+                       goto disarm;
                }
-       }
 
-       set_current_kprobe(p, NULL, NULL);
-       kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+               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;
+               }
 
-       if (retprobe) {
-               ret = trampoline_probe_handler(p, regs);
-       } else if (p->pre_handler) {
-               ret = p->pre_handler(p, regs);
-               if(p->pre_handler != trampoline_probe_handler) {
-                       reset_current_kprobe();
+               p->ainsn.insn = p->ainsn.insn_arm;
+               list_for_each_entry_rcu(kp, &p->list, list) {
+                       kp->ainsn.insn = p->ainsn.insn_arm;
                }
        }
 
-       if (ret) {
-               /* handler has already set things up, so skip ss setup */
-               err_out = 0;
-               goto out;
+       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;
 
-no_uprobe:
-       msg_out = "no_uprobe\n";
-       err_out = 1;            // return with death
-       goto out;
+       p = get_ukprobe(addr, tgid);
 
-no_uprobe_live:
-       msg_out = "no_uprobe live\n";
-       err_out = 0;            // ok - life is life
-       goto out;
+       if (p == NULL) {
+               p = get_ukprobe_by_insn_slot(addr, tgid, regs);
+               if (p == NULL) {
+                       printk("no_uprobe\n");
+                       return 1;
+               }
 
-out:
-#ifdef SUPRESS_BUG_MESSAGES
-       oops_in_progress = swap_oops_in_progress;
-#endif
+               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(msg_out) {
-               printk(msg_out);
+               if (!p->pre_handler || !p->pre_handler(p, regs)) {
+                       prepare_singlestep(p, regs);
+               }
        }
 
-       return err_out;
+       return 0;
 }
 
 int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)