ARM: 7306/1: vfp: flush thread hwstate before restoring context from sigframe
[pandora-kernel.git] / arch / arm / kernel / kprobes.c
index a9050ba..129c116 100644 (file)
@@ -51,6 +51,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        kprobe_opcode_t insn;
        kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
        unsigned long addr = (unsigned long)p->addr;
+       bool thumb;
        kprobe_decode_insn_t *decode_insn;
        int is;
 
@@ -58,6 +59,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
                return -EINVAL;
 
 #ifdef CONFIG_THUMB2_KERNEL
+       thumb = true;
        addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
        insn = ((u16 *)addr)[0];
        if (is_wide_instruction(insn)) {
@@ -67,6 +69,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        } else
                decode_insn = thumb16_kprobe_decode_insn;
 #else /* !CONFIG_THUMB2_KERNEL */
+       thumb = false;
        if (addr & 0x3)
                return -EINVAL;
        insn = *p->addr;
@@ -88,6 +91,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
                        p->ainsn.insn[is] = tmp_insn[is];
                flush_insns(p->ainsn.insn,
                                sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE);
+               p->ainsn.insn_fn = (kprobe_insn_fn_t *)
+                                       ((uintptr_t)p->ainsn.insn | thumb);
                break;
 
        case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */
@@ -138,7 +143,13 @@ void __kprobes arch_arm_kprobe(struct kprobe *p)
 
 void __kprobes arch_arm_kprobe(struct kprobe *p)
 {
-       *p->addr = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
+       kprobe_opcode_t insn = p->opcode;
+       kprobe_opcode_t brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
+       if (insn >= 0xe0000000)
+               brkp |= 0xe0000000;  /* Unconditional instruction */
+       else
+               brkp |= insn & 0xf0000000;  /* Copy condition from insn */
+       *p->addr = brkp;
        flush_insns(p->addr, sizeof(p->addr[0]));
 }
 
@@ -207,12 +218,24 @@ static void __kprobes set_current_kprobe(struct kprobe *p)
        __get_cpu_var(current_kprobe) = p;
 }
 
-static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs,
-                                struct kprobe_ctlblk *kcb)
+static void __kprobes
+singlestep_skip(struct kprobe *p, struct pt_regs *regs)
 {
+#ifdef CONFIG_THUMB2_KERNEL
+       regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
+       if (is_wide_instruction(p->opcode))
+               regs->ARM_pc += 4;
+       else
+               regs->ARM_pc += 2;
+#else
        regs->ARM_pc += 4;
-       if (p->ainsn.insn_check_cc(regs->ARM_cpsr))
-               p->ainsn.insn_handler(p, regs);
+#endif
+}
+
+static inline void __kprobes
+singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
+{
+       p->ainsn.insn_singlestep(p, regs);
 }
 
 /*
@@ -262,7 +285,8 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
                                /* impossible cases */
                                BUG();
                        }
-               } else {
+               } else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) {
+                       /* Probe hit and conditional execution check ok. */
                        set_current_kprobe(p);
                        kcb->kprobe_status = KPROBE_HIT_ACTIVE;
 
@@ -282,6 +306,13 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
                                }
                                reset_current_kprobe();
                        }
+               } else {
+                       /*
+                        * Probe hit but conditional execution check failed,
+                        * so just skip the instruction and continue as if
+                        * nothing had happened.
+                        */
+                       singlestep_skip(p, regs);
                }
        } else if (cur) {
                /* We probably hit a jprobe.  Call its break handler. */
@@ -603,7 +634,7 @@ static struct undef_hook kprobes_thumb32_break_hook = {
 #else  /* !CONFIG_THUMB2_KERNEL */
 
 static struct undef_hook kprobes_arm_break_hook = {
-       .instr_mask     = 0xffffffff,
+       .instr_mask     = 0x0fffffff,
        .instr_val      = KPROBE_ARM_BREAKPOINT_INSTRUCTION,
        .cpsr_mask      = MODE_MASK,
        .cpsr_val       = SVC_MODE,