diff options
author | Rabin Vincent <rabin@rab.in> | 2012-02-18 17:50:51 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-03-24 09:38:55 +0000 |
commit | b21d55e98ac2bbcbbeec9a8cb091f717fd95b072 (patch) | |
tree | ad3eb60af18816acef347bc22e43059c0c0d6873 /arch/arm/kernel/kprobes.c | |
parent | d82227cf8f0b42ff42c21ed47025fdf54cb1698d (diff) |
ARM: 7332/1: extract out code patch function from kprobes
Extract out the code patching code from kprobes so that it can be used
from the jump label code. Additionally, the separated code:
- Uses the IS_ENABLED() macros instead of the #ifdefs for THUMB2
support
- Unifies the two separate functions in kprobes, providing one function
that uses stop_machine() internally, and one that can be called from
stop_machine() directly
- Patches the text on all CPUs only on processors requiring software
broadcasting of cache operations
Acked-by: Jon Medhurst <tixy@yxit.co.uk>
Tested-by: Jon Medhurst <tixy@yxit.co.uk>
Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/kprobes.c')
-rw-r--r-- | arch/arm/kernel/kprobes.c | 86 |
1 files changed, 24 insertions, 62 deletions
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index 129c1163248b..ab1869dac97a 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c @@ -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_map); + 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; } |