Merge "[REFACTOR] Buffer: move getting next queue element into separate function"
[kernel/swap-modules.git] / uprobe / arch / asm-x86 / swap_uprobes.c
index b03ef06..2ec59ac 100644 (file)
  */
 
 #include <linux/kdebug.h>
-#include <asm/dbi_kprobes.h>
-#include <swap_uprobes.h>
-#include <asm/swap_uprobes.h>
-#include <dbi_insn_slots.h>
+#include <kprobe/arch/asm/dbi_kprobes.h>
+#include <uprobe/swap_uprobes.h>
+#include <uprobe/arch/asm/swap_uprobes.h>
+#include <kprobe/dbi_insn_slots.h>
 
 struct uprobe_ctlblk {
         unsigned long flags;
         struct kprobe *p;
 };
 
+static unsigned long trampoline_addr(struct uprobe *up)
+{
+       return (unsigned long)(up->kp.ainsn.insn +
+                              UPROBES_TRAMP_RET_BREAK_IDX);
+}
+
 static DEFINE_PER_CPU(struct uprobe_ctlblk, ucb) = { 0, NULL };
 
 static struct kprobe *get_current_probe(void)
@@ -63,44 +69,30 @@ static void restore_current_flags(struct pt_regs *regs)
        regs->EREG(flags) |= __get_cpu_var(ucb).flags & IF_MASK;
 }
 
-int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list)
+int arch_prepare_uprobe(struct uprobe *up)
 {
        int ret = 0;
-       struct kprobe *p = &up->kp;
+       struct kprobe *p = up2kp(up);
        struct task_struct *task = up->task;
-       kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
-
-       if (!ret) {
-               kprobe_opcode_t insn[MAX_INSN_SIZE];
-               struct arch_specific_insn ainsn;
-
-               if (!read_proc_vm_atomic(task, (unsigned long)p->addr, &insn, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
-                       panic("failed to read memory %p!\n", p->addr);
-
-               ainsn.insn = insn;
-               ret = arch_check_insn(&ainsn);
-               if (!ret) {
-                       p->opcode = insn[0];
-                       p->ainsn.insn = alloc_insn_slot(up->sm);
-                       if (!p->ainsn.insn)
-                               return -ENOMEM;
-
-                       if (can_boost(insn))
-                               p->ainsn.boostable = 0;
-                       else
-                               p->ainsn.boostable = -1;
-
-                       memcpy(&insns[UPROBES_TRAMP_INSN_IDX], insn, MAX_INSN_SIZE*sizeof(kprobe_opcode_t));
-                       insns[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
-
-                       if (!write_proc_vm_atomic(task, (unsigned long)p->ainsn.insn, insns, sizeof(insns))) {
-                               free_insn_slot(up->sm, p->ainsn.insn);
-                               panic("failed to write memory %p!\n", p->ainsn.insn);
-                               return -EINVAL;
-                       }
-               }
+       u8 *tramp = up->atramp.tramp;
+       enum { call_relative_opcode = 0xe8 };
+
+       if (!read_proc_vm_atomic(task, (unsigned long)p->addr,
+                                tramp, MAX_INSN_SIZE))
+               panic("failed to read memory %p!\n", p->addr);
+       /* TODO: this is a workaround */
+       if (tramp[0] == call_relative_opcode) {
+               printk("cannot install probe: 1st instruction is call\n");
+               return -1;
        }
 
+       tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+
+       /* TODO: remove dual info */
+       p->opcode = tramp[0];
+
+       p->ainsn.boostable = can_boost(tramp) ? 0 : -1;
+
        return ret;
 }
 
@@ -110,7 +102,7 @@ int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
        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;
-       unsigned long addr, args[6];
+       unsigned long args[6];
 
        /* FIXME some user space apps crash if we clean interrupt bit */
        //regs->EREG(flags) &= ~IF_MASK;
@@ -123,7 +115,7 @@ int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
                panic("failed to read user space func arguments %lx!\n", regs->EREG(sp) + 4);
 
        if (pre_entry)
-               p->ss_addr = pre_entry(jp->priv_arg, regs);
+               p->ss_addr = (kprobe_opcode_t *)pre_entry(jp->priv_arg, regs);
 
        if (entry)
                entry(args[0], args[1], args[2], args[3], args[4], args[5]);
@@ -136,7 +128,8 @@ int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
 void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
 {
        /* Replace the return addr with trampoline addr */
-       unsigned long ra = (unsigned long)(ri->rp->up.kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+       unsigned long ra = trampoline_addr(&ri->rp->up);
+       ri->sp = (kprobe_opcode_t *)regs->sp;
 
        if (!read_proc_vm_atomic(current, regs->EREG(sp), &(ri->ret_addr), sizeof(ri->ret_addr)))
                panic("failed to read user space func ra %lx!\n", regs->EREG(sp));
@@ -145,9 +138,41 @@ void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
                panic("failed to write user space func ra %lx!\n", regs->EREG(sp));
 }
 
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+                        struct task_struct *task)
+{
+       int len;
+       unsigned long ret_addr;
+       unsigned long sp = (unsigned long)ri->sp;
+       unsigned long tramp_addr = trampoline_addr(&ri->rp->up);
+       len = read_proc_vm_atomic(task, sp, &ret_addr, sizeof(ret_addr));
+       if (len != sizeof(ret_addr)) {
+               printk("---> %s (%d/%d): failed to read stack from %08lx\n",
+                      task->comm, task->tgid, task->pid, sp);
+               return -EFAULT;
+       }
+
+       if (tramp_addr == ret_addr) {
+               len = write_proc_vm_atomic(task, sp, &ri->ret_addr,
+                                          sizeof(ri->ret_addr));
+               if (len != sizeof(ri->ret_addr)) {
+                       printk("---> %s (%d/%d): failed to write "
+                              "orig_ret_addr to %08lx",
+                              task->comm, task->tgid, task->pid, sp);
+                       return -EFAULT;
+               }
+       } else {
+               printk("---> %s (%d/%d): trampoline NOT found at sp = %08lx\n",
+                      task->comm, task->tgid, task->pid, sp);
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
 unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs)
 {
-       return (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+       return trampoline_addr(kp2up(p));
 }
 
 void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
@@ -256,6 +281,31 @@ no_change:
        return;
 }
 
+static int make_trampoline(struct uprobe *up)
+{
+       struct kprobe *p = up2kp(up);
+       struct task_struct *task = up->task;
+       void *tramp;
+
+       tramp = alloc_insn_slot(up->sm);
+       if (tramp == 0) {
+               printk("trampoline out of memory\n");
+               return -ENOMEM;
+       }
+
+       if (!write_proc_vm_atomic(task, (unsigned long)tramp,
+                                 up->atramp.tramp,
+                                 sizeof(up->atramp.tramp))) {
+               free_insn_slot(up->sm, tramp);
+               panic("failed to write memory %p!\n", tramp);
+               return -EINVAL;
+       }
+
+       p->ainsn.insn = tramp;
+
+       return 0;
+}
+
 static int uprobe_handler(struct pt_regs *regs)
 {
        struct kprobe *p;
@@ -269,8 +319,9 @@ static int uprobe_handler(struct pt_regs *regs)
        p = get_ukprobe(addr, tgid);
 
        if (p == NULL) {
-               p = get_ukprobe_by_insn_slot(addr, tgid, regs);
+               void *tramp_addr = (void *)addr - UPROBES_TRAMP_RET_BREAK_IDX;
 
+               p = get_ukprobe_by_insn_slot(tramp_addr, tgid, regs);
                if (p == NULL) {
                        printk("no_uprobe\n");
                        return 0;
@@ -279,6 +330,15 @@ static int uprobe_handler(struct pt_regs *regs)
                trampoline_uprobe_handler(p, regs);
                return 1;
        } else {
+               if (p->ainsn.insn == NULL) {
+                       struct uprobe *up = kp2up(p);
+
+                       make_trampoline(up);
+
+                       /* for uretprobe */
+                       add_uprobe_table(p);
+               }
+
                if (!p->pre_handler || !p->pre_handler(p, regs)) {
                        if (p->ainsn.boostable == 1 && !p->post_handler) {
                                regs->EREG(ip) = (unsigned long)p->ainsn.insn;
@@ -315,7 +375,7 @@ static int uprobe_exceptions_notify(struct notifier_block *self, unsigned long v
        struct die_args *args = (struct die_args *)data;
        int ret = NOTIFY_DONE;
 
-       if (args->regs && !user_mode_vm(args->regs))
+       if (args->regs == NULL || !user_mode_vm(args->regs))
                return ret;
 
        switch (val) {