uprobes/x86: Introduce uprobe_xol_ops and arch_uprobe->ops
[linux-2.6-microblaze.git] / arch / x86 / kernel / uprobes.c
index 2ed8459..13ad8a3 100644 (file)
@@ -53,7 +53,7 @@
 #define OPCODE1(insn)          ((insn)->opcode.bytes[0])
 #define OPCODE2(insn)          ((insn)->opcode.bytes[1])
 #define OPCODE3(insn)          ((insn)->opcode.bytes[2])
-#define MODRM_REG(insn)                X86_MODRM_REG(insn->modrm.value)
+#define MODRM_REG(insn)                X86_MODRM_REG((insn)->modrm.value)
 
 #define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\
        (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) |   \
@@ -229,63 +229,6 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn)
        return -ENOTSUPP;
 }
 
-/*
- * Figure out which fixups arch_uprobe_post_xol() will need to perform, and
- * annotate arch_uprobe->fixups accordingly.  To start with,
- * arch_uprobe->fixups is either zero or it reflects rip-related fixups.
- */
-static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn)
-{
-       bool fix_ip = true, fix_call = false;   /* defaults */
-       int reg;
-
-       insn_get_opcode(insn);  /* should be a nop */
-
-       switch (OPCODE1(insn)) {
-       case 0x9d:
-               /* popf */
-               auprobe->fixups |= UPROBE_FIX_SETF;
-               break;
-       case 0xc3:              /* ret/lret */
-       case 0xcb:
-       case 0xc2:
-       case 0xca:
-               /* ip is correct */
-               fix_ip = false;
-               break;
-       case 0xe8:              /* call relative - Fix return addr */
-               fix_call = true;
-               break;
-       case 0x9a:              /* call absolute - Fix return addr, not ip */
-               fix_call = true;
-               fix_ip = false;
-               break;
-       case 0xff:
-               insn_get_modrm(insn);
-               reg = MODRM_REG(insn);
-               if (reg == 2 || reg == 3) {
-                       /* call or lcall, indirect */
-                       /* Fix return addr; ip is correct. */
-                       fix_call = true;
-                       fix_ip = false;
-               } else if (reg == 4 || reg == 5) {
-                       /* jmp or ljmp, indirect */
-                       /* ip is correct. */
-                       fix_ip = false;
-               }
-               break;
-       case 0xea:              /* jmp absolute -- ip is correct */
-               fix_ip = false;
-               break;
-       default:
-               break;
-       }
-       if (fix_ip)
-               auprobe->fixups |= UPROBE_FIX_IP;
-       if (fix_call)
-               auprobe->fixups |= UPROBE_FIX_CALL;
-}
-
 #ifdef CONFIG_X86_64
 /*
  * If arch_uprobe->insn doesn't use rip-relative addressing, return
@@ -310,15 +253,11 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn)
  *  - The displacement is always 4 bytes.
  */
 static void
-handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn)
+handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn)
 {
        u8 *cursor;
        u8 reg;
 
-       if (mm->context.ia32_compat)
-               return;
-
-       auprobe->rip_rela_target_address = 0x0;
        if (!insn_rip_relative(insn))
                return;
 
@@ -372,7 +311,48 @@ handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct ins
                cursor++;
                memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes);
        }
-       return;
+}
+
+/*
+ * If we're emulating a rip-relative instruction, save the contents
+ * of the scratch register and store the target address in that register.
+ */
+static void
+pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
+                               struct arch_uprobe_task *autask)
+{
+       if (auprobe->fixups & UPROBE_FIX_RIP_AX) {
+               autask->saved_scratch_register = regs->ax;
+               regs->ax = current->utask->vaddr;
+               regs->ax += auprobe->rip_rela_target_address;
+       } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) {
+               autask->saved_scratch_register = regs->cx;
+               regs->cx = current->utask->vaddr;
+               regs->cx += auprobe->rip_rela_target_address;
+       }
+}
+
+static void
+handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction)
+{
+       if (auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) {
+               struct arch_uprobe_task *autask;
+
+               autask = &current->utask->autask;
+               if (auprobe->fixups & UPROBE_FIX_RIP_AX)
+                       regs->ax = autask->saved_scratch_register;
+               else
+                       regs->cx = autask->saved_scratch_register;
+
+               /*
+                * The original instruction includes a displacement, and so
+                * is 4 bytes longer than what we've just single-stepped.
+                * Caller may need to apply other fixups to handle stuff
+                * like "jmpq *...(%rip)" and "callq *...(%rip)".
+                */
+               if (correction)
+                       *correction += 4;
+       }
 }
 
 static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn)
@@ -401,94 +381,41 @@ static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm,
        return validate_insn_64bits(auprobe, insn);
 }
 #else /* 32-bit: */
-static void handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn)
-{
-       /* No RIP-relative addressing on 32-bit */
-}
-
-static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm,  struct insn *insn)
-{
-       return validate_insn_32bits(auprobe, insn);
-}
-#endif /* CONFIG_X86_64 */
-
-/**
- * arch_uprobe_analyze_insn - instruction analysis including validity and fixups.
- * @mm: the probed address space.
- * @arch_uprobe: the probepoint information.
- * @addr: virtual address at which to install the probepoint
- * Return 0 on success or a -ve number on error.
+/*
+ * No RIP-relative addressing on 32-bit
  */
-int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr)
+static void handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn)
 {
-       int ret;
-       struct insn insn;
-
-       auprobe->fixups = 0;
-       ret = validate_insn_bits(auprobe, mm, &insn);
-       if (ret != 0)
-               return ret;
-
-       handle_riprel_insn(auprobe, mm, &insn);
-       prepare_fixups(auprobe, &insn);
-
-       return 0;
 }
-
-#ifdef CONFIG_X86_64
-/*
- * If we're emulating a rip-relative instruction, save the contents
- * of the scratch register and store the target address in that register.
- */
-static void
-pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
+static void pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
                                struct arch_uprobe_task *autask)
 {
-       if (auprobe->fixups & UPROBE_FIX_RIP_AX) {
-               autask->saved_scratch_register = regs->ax;
-               regs->ax = current->utask->vaddr;
-               regs->ax += auprobe->rip_rela_target_address;
-       } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) {
-               autask->saved_scratch_register = regs->cx;
-               regs->cx = current->utask->vaddr;
-               regs->cx += auprobe->rip_rela_target_address;
-       }
 }
-#else
-static void
-pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
-                               struct arch_uprobe_task *autask)
+static void handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs,
+                                       long *correction)
 {
-       /* No RIP-relative addressing on 32-bit */
 }
-#endif
 
-/*
- * arch_uprobe_pre_xol - prepare to execute out of line.
- * @auprobe: the probepoint information.
- * @regs: reflects the saved user state of current task.
- */
-int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm,  struct insn *insn)
 {
-       struct arch_uprobe_task *autask;
-
-       autask = &current->utask->autask;
-       autask->saved_trap_nr = current->thread.trap_nr;
-       current->thread.trap_nr = UPROBE_TRAP_NR;
-       regs->ip = current->utask->xol_vaddr;
-       pre_xol_rip_insn(auprobe, regs, autask);
+       return validate_insn_32bits(auprobe, insn);
+}
+#endif /* CONFIG_X86_64 */
 
-       autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF);
-       regs->flags |= X86_EFLAGS_TF;
-       if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
-               set_task_blockstep(current, false);
+struct uprobe_xol_ops {
+       bool    (*emulate)(struct arch_uprobe *, struct pt_regs *);
+       int     (*pre_xol)(struct arch_uprobe *, struct pt_regs *);
+       int     (*post_xol)(struct arch_uprobe *, struct pt_regs *);
+};
 
+static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       pre_xol_rip_insn(auprobe, regs, &current->utask->autask);
        return 0;
 }
 
 /*
- * This function is called by arch_uprobe_post_xol() to adjust the return
- * address pushed by a call instruction executed out of line.
+ * Adjust the return address pushed by a call insn executed out of line.
  */
 static int adjust_ret_addr(unsigned long sp, long correction)
 {
@@ -512,41 +439,115 @@ static int adjust_ret_addr(unsigned long sp, long correction)
        return 0;
 }
 
-#ifdef CONFIG_X86_64
-static bool is_riprel_insn(struct arch_uprobe *auprobe)
+static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       return ((auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) != 0);
+       struct uprobe_task *utask = current->utask;
+       long correction = (long)(utask->vaddr - utask->xol_vaddr);
+       int ret = 0;
+
+       handle_riprel_post_xol(auprobe, regs, &correction);
+       if (auprobe->fixups & UPROBE_FIX_IP)
+               regs->ip += correction;
+
+       if (auprobe->fixups & UPROBE_FIX_CALL)
+               ret = adjust_ret_addr(regs->sp, correction);
+
+       return ret;
 }
 
-static void
-handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction)
+static struct uprobe_xol_ops default_xol_ops = {
+       .pre_xol  = default_pre_xol_op,
+       .post_xol = default_post_xol_op,
+};
+
+/**
+ * arch_uprobe_analyze_insn - instruction analysis including validity and fixups.
+ * @mm: the probed address space.
+ * @arch_uprobe: the probepoint information.
+ * @addr: virtual address at which to install the probepoint
+ * Return 0 on success or a -ve number on error.
+ */
+int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr)
 {
-       if (is_riprel_insn(auprobe)) {
-               struct arch_uprobe_task *autask;
+       struct insn insn;
+       bool fix_ip = true, fix_call = false;
+       int ret;
 
-               autask = &current->utask->autask;
-               if (auprobe->fixups & UPROBE_FIX_RIP_AX)
-                       regs->ax = autask->saved_scratch_register;
-               else
-                       regs->cx = autask->saved_scratch_register;
+       ret = validate_insn_bits(auprobe, mm, &insn);
+       if (ret)
+               return ret;
 
-               /*
-                * The original instruction includes a displacement, and so
-                * is 4 bytes longer than what we've just single-stepped.
-                * Fall through to handle stuff like "jmpq *...(%rip)" and
-                * "callq *...(%rip)".
-                */
-               if (correction)
-                       *correction += 4;
+       /*
+        * Figure out which fixups arch_uprobe_post_xol() will need to perform,
+        * and annotate arch_uprobe->fixups accordingly. To start with, ->fixups
+        * is either zero or it reflects rip-related fixups.
+        */
+       handle_riprel_insn(auprobe, &insn);
+
+       switch (OPCODE1(&insn)) {
+       case 0x9d:              /* popf */
+               auprobe->fixups |= UPROBE_FIX_SETF;
+               break;
+       case 0xc3:              /* ret or lret -- ip is correct */
+       case 0xcb:
+       case 0xc2:
+       case 0xca:
+               fix_ip = false;
+               break;
+       case 0xe8:              /* call relative - Fix return addr */
+               fix_call = true;
+               break;
+       case 0x9a:              /* call absolute - Fix return addr, not ip */
+               fix_call = true;
+               fix_ip = false;
+               break;
+       case 0xea:              /* jmp absolute -- ip is correct */
+               fix_ip = false;
+               break;
+       case 0xff:
+               insn_get_modrm(&insn);
+               switch (MODRM_REG(&insn)) {
+               case 2: case 3:                 /* call or lcall, indirect */
+                       fix_call = true;
+               case 4: case 5:                 /* jmp or ljmp, indirect */
+                       fix_ip = false;
+               }
+               break;
+       default:
+               break;
        }
+
+       if (fix_ip)
+               auprobe->fixups |= UPROBE_FIX_IP;
+       if (fix_call)
+               auprobe->fixups |= UPROBE_FIX_CALL;
+
+       auprobe->ops = &default_xol_ops;
+       return 0;
 }
-#else
-static void
-handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction)
+
+/*
+ * arch_uprobe_pre_xol - prepare to execute out of line.
+ * @auprobe: the probepoint information.
+ * @regs: reflects the saved user state of current task.
+ */
+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       /* No RIP-relative addressing on 32-bit */
+       struct uprobe_task *utask = current->utask;
+
+       regs->ip = utask->xol_vaddr;
+       utask->autask.saved_trap_nr = current->thread.trap_nr;
+       current->thread.trap_nr = UPROBE_TRAP_NR;
+
+       utask->autask.saved_tf = !!(regs->flags & X86_EFLAGS_TF);
+       regs->flags |= X86_EFLAGS_TF;
+       if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
+               set_task_blockstep(current, false);
+
+       if (auprobe->ops->pre_xol)
+               return auprobe->ops->pre_xol(auprobe, regs);
+       return 0;
 }
-#endif
 
 /*
  * If xol insn itself traps and generates a signal(Say,
@@ -592,22 +593,10 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t)
  */
 int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       struct uprobe_task *utask;
-       long correction;
-       int result = 0;
+       struct uprobe_task *utask = current->utask;
 
        WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
-
-       utask = current->utask;
        current->thread.trap_nr = utask->autask.saved_trap_nr;
-       correction = (long)(utask->vaddr - utask->xol_vaddr);
-       handle_riprel_post_xol(auprobe, regs, &correction);
-       if (auprobe->fixups & UPROBE_FIX_IP)
-               regs->ip += correction;
-
-       if (auprobe->fixups & UPROBE_FIX_CALL)
-               result = adjust_ret_addr(regs->sp, correction);
-
        /*
         * arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP
         * so we can get an extra SIGTRAP if we do not clear TF. We need
@@ -618,7 +607,9 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        else if (!(auprobe->fixups & UPROBE_FIX_SETF))
                regs->flags &= ~X86_EFLAGS_TF;
 
-       return result;
+       if (auprobe->ops->post_xol)
+               return auprobe->ops->post_xol(auprobe, regs);
+       return 0;
 }
 
 /* callback routine for handling exceptions. */
@@ -676,6 +667,10 @@ static bool __skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        int i;
 
+       if (auprobe->ops->emulate)
+               return auprobe->ops->emulate(auprobe, regs);
+
+       /* TODO: move this code into ->emulate() hook */
        for (i = 0; i < MAX_UINSN_BYTES; i++) {
                if (auprobe->insn[i] == 0x66)
                        continue;