ARM: move signal handlers into a vdso-like page
[linux-3.10.git] / arch / arm / kernel / kprobes.c
index a79e5c7..170e9f3 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/cacheflush.h>
 
 #include "kprobes.h"
+#include "patch.h"
 
 #define MIN_STACK_SIZE(addr)                           \
        min((unsigned long)MAX_STACK_SIZE,              \
@@ -103,57 +104,33 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        return 0;
 }
 
-#ifdef CONFIG_THUMB2_KERNEL
-
-/*
- * For a 32-bit Thumb breakpoint spanning two memory words we need to take
- * special precautions to insert the breakpoint atomically, especially on SMP
- * systems. This is achieved by calling this arming function using stop_machine.
- */
-static int __kprobes set_t32_breakpoint(void *addr)
-{
-       ((u16 *)addr)[0] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION >> 16;
-       ((u16 *)addr)[1] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION & 0xffff;
-       flush_insns(addr, 2*sizeof(u16));
-       return 0;
-}
-
 void __kprobes arch_arm_kprobe(struct kprobe *p)
 {
-       uintptr_t addr = (uintptr_t)p->addr & ~1; /* Remove any Thumb flag */
-
-       if (!is_wide_instruction(p->opcode)) {
-               *(u16 *)addr = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
-               flush_insns(addr, sizeof(u16));
-       } else if (addr & 2) {
-               /* A 32-bit instruction spanning two words needs special care */
-               stop_machine(set_t32_breakpoint, (void *)addr, cpu_online_mask);
+       unsigned int brkp;
+       void *addr;
+
+       if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+               /* Remove any Thumb flag */
+               addr = (void *)((uintptr_t)p->addr & ~1);
+
+               if (is_wide_instruction(p->opcode))
+                       brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
+               else
+                       brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
        } else {
-               /* Word aligned 32-bit instruction can be written atomically */
-               u32 bkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
-#ifndef __ARMEB__ /* Swap halfwords for little-endian */
-               bkp = (bkp >> 16) | (bkp << 16);
-#endif
-               *(u32 *)addr = bkp;
-               flush_insns(addr, sizeof(u32));
-       }
-}
+               kprobe_opcode_t insn = p->opcode;
 
-#else /* !CONFIG_THUMB2_KERNEL */
+               addr = p->addr;
+               brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
 
-void __kprobes arch_arm_kprobe(struct kprobe *p)
-{
-       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]));
-}
+               if (insn >= 0xe0000000)
+                       brkp |= 0xe0000000;  /* Unconditional instruction */
+               else
+                       brkp |= insn & 0xf0000000;  /* Copy condition from insn */
+       }
 
-#endif /* !CONFIG_THUMB2_KERNEL */
+       patch_text(addr, brkp);
+}
 
 /*
  * The actual disarming is done here on each CPU and synchronized using
@@ -166,25 +143,10 @@ void __kprobes arch_arm_kprobe(struct kprobe *p)
 int __kprobes __arch_disarm_kprobe(void *p)
 {
        struct kprobe *kp = p;
-#ifdef CONFIG_THUMB2_KERNEL
-       u16 *addr = (u16 *)((uintptr_t)kp->addr & ~1);
-       kprobe_opcode_t insn = kp->opcode;
-       unsigned int len;
+       void *addr = (void *)((uintptr_t)kp->addr & ~1);
 
-       if (is_wide_instruction(insn)) {
-               ((u16 *)addr)[0] = insn>>16;
-               ((u16 *)addr)[1] = insn;
-               len = 2*sizeof(u16);
-       } else {
-               ((u16 *)addr)[0] = insn;
-               len = sizeof(u16);
-       }
-       flush_insns(addr, len);
+       __patch_text(addr, kp->opcode);
 
-#else /* !CONFIG_THUMB2_KERNEL */
-       *kp->addr = kp->opcode;
-       flush_insns(kp->addr, sizeof(kp->addr[0]));
-#endif
        return 0;
 }
 
@@ -433,7 +395,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
 {
        struct kretprobe_instance *ri = NULL;
        struct hlist_head *head, empty_rp;
-       struct hlist_node *node, *tmp;
+       struct hlist_node *tmp;
        unsigned long flags, orig_ret_address = 0;
        unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
 
@@ -453,7 +415,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
         *       real return address, and all the rest will point to
         *       kretprobe_trampoline
         */
-       hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+       hlist_for_each_entry_safe(ri, tmp, head, hlist) {
                if (ri->task != current)
                        /* another task is sharing our hash bucket */
                        continue;
@@ -480,7 +442,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
        kretprobe_assert(ri, orig_ret_address, trampoline_address);
        kretprobe_hash_unlock(current, &flags);
 
-       hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+       hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
                hlist_del(&ri->hlist);
                kfree(ri);
        }