diff options
| -rw-r--r-- | arch/arm/Kconfig | 1 | ||||
| -rw-r--r-- | arch/arm/include/asm/insn.h (renamed from arch/arm/kernel/insn.h) | 0 | ||||
| -rw-r--r-- | arch/arm/include/asm/kprobes.h | 29 | ||||
| -rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/kernel/ftrace.c | 3 | ||||
| -rw-r--r-- | arch/arm/kernel/jump_label.c | 3 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/Makefile | 1 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/core.c | 26 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/core.h | 2 | ||||
| -rw-r--r-- | arch/arm/probes/kprobes/opt-arm.c | 322 |
10 files changed, 377 insertions, 12 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 97d07ed60a0b..3d5dc2df835b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -60,6 +60,7 @@ config ARM | |||
| 60 | select HAVE_MEMBLOCK | 60 | select HAVE_MEMBLOCK |
| 61 | select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND | 61 | select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND |
| 62 | select HAVE_OPROFILE if (HAVE_PERF_EVENTS) | 62 | select HAVE_OPROFILE if (HAVE_PERF_EVENTS) |
| 63 | select HAVE_OPTPROBES if !THUMB2_KERNEL | ||
| 63 | select HAVE_PERF_EVENTS | 64 | select HAVE_PERF_EVENTS |
| 64 | select HAVE_PERF_REGS | 65 | select HAVE_PERF_REGS |
| 65 | select HAVE_PERF_USER_STACK_DUMP | 66 | select HAVE_PERF_USER_STACK_DUMP |
diff --git a/arch/arm/kernel/insn.h b/arch/arm/include/asm/insn.h index e96065da4dae..e96065da4dae 100644 --- a/arch/arm/kernel/insn.h +++ b/arch/arm/include/asm/insn.h | |||
diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h index 56f9ac68fbd1..50ff3bc7928e 100644 --- a/arch/arm/include/asm/kprobes.h +++ b/arch/arm/include/asm/kprobes.h | |||
| @@ -50,5 +50,34 @@ int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr); | |||
| 50 | int kprobe_exceptions_notify(struct notifier_block *self, | 50 | int kprobe_exceptions_notify(struct notifier_block *self, |
| 51 | unsigned long val, void *data); | 51 | unsigned long val, void *data); |
| 52 | 52 | ||
| 53 | /* optinsn template addresses */ | ||
| 54 | extern __visible kprobe_opcode_t optprobe_template_entry; | ||
| 55 | extern __visible kprobe_opcode_t optprobe_template_val; | ||
| 56 | extern __visible kprobe_opcode_t optprobe_template_call; | ||
| 57 | extern __visible kprobe_opcode_t optprobe_template_end; | ||
| 58 | extern __visible kprobe_opcode_t optprobe_template_sub_sp; | ||
| 59 | extern __visible kprobe_opcode_t optprobe_template_add_sp; | ||
| 60 | |||
| 61 | #define MAX_OPTIMIZED_LENGTH 4 | ||
| 62 | #define MAX_OPTINSN_SIZE \ | ||
| 63 | ((unsigned long)&optprobe_template_end - \ | ||
| 64 | (unsigned long)&optprobe_template_entry) | ||
| 65 | #define RELATIVEJUMP_SIZE 4 | ||
| 66 | |||
| 67 | struct arch_optimized_insn { | ||
| 68 | /* | ||
| 69 | * copy of the original instructions. | ||
| 70 | * Different from x86, ARM kprobe_opcode_t is u32. | ||
| 71 | */ | ||
| 72 | #define MAX_COPIED_INSN DIV_ROUND_UP(RELATIVEJUMP_SIZE, sizeof(kprobe_opcode_t)) | ||
| 73 | kprobe_opcode_t copied_insn[MAX_COPIED_INSN]; | ||
| 74 | /* detour code buffer */ | ||
| 75 | kprobe_opcode_t *insn; | ||
| 76 | /* | ||
| 77 | * We always copy one instruction on ARM, | ||
| 78 | * so size will always be 4, and unlike x86, there is no | ||
| 79 | * need for a size field. | ||
| 80 | */ | ||
| 81 | }; | ||
| 53 | 82 | ||
| 54 | #endif /* _ARM_KPROBES_H */ | 83 | #endif /* _ARM_KPROBES_H */ |
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 9c51a433e025..902397dd1000 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
| @@ -52,7 +52,7 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o | |||
| 52 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o | 52 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o |
| 53 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | 53 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o |
| 54 | # Main staffs in KPROBES are in arch/arm/probes/ . | 54 | # Main staffs in KPROBES are in arch/arm/probes/ . |
| 55 | obj-$(CONFIG_KPROBES) += patch.o | 55 | obj-$(CONFIG_KPROBES) += patch.o insn.o |
| 56 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o | 56 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o |
| 57 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o | 57 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o |
| 58 | obj-$(CONFIG_KGDB) += kgdb.o patch.o | 58 | obj-$(CONFIG_KGDB) += kgdb.o patch.o |
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index b8c75e45a950..709ee1d6d4df 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
| @@ -20,8 +20,7 @@ | |||
| 20 | #include <asm/cacheflush.h> | 20 | #include <asm/cacheflush.h> |
| 21 | #include <asm/opcodes.h> | 21 | #include <asm/opcodes.h> |
| 22 | #include <asm/ftrace.h> | 22 | #include <asm/ftrace.h> |
| 23 | 23 | #include <asm/insn.h> | |
| 24 | #include "insn.h" | ||
| 25 | 24 | ||
| 26 | #ifdef CONFIG_THUMB2_KERNEL | 25 | #ifdef CONFIG_THUMB2_KERNEL |
| 27 | #define NOP 0xf85deb04 /* pop.w {lr} */ | 26 | #define NOP 0xf85deb04 /* pop.w {lr} */ |
diff --git a/arch/arm/kernel/jump_label.c b/arch/arm/kernel/jump_label.c index d8da075959bf..e39cbf488cfe 100644 --- a/arch/arm/kernel/jump_label.c +++ b/arch/arm/kernel/jump_label.c | |||
| @@ -1,8 +1,7 @@ | |||
| 1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
| 2 | #include <linux/jump_label.h> | 2 | #include <linux/jump_label.h> |
| 3 | #include <asm/patch.h> | 3 | #include <asm/patch.h> |
| 4 | 4 | #include <asm/insn.h> | |
| 5 | #include "insn.h" | ||
| 6 | 5 | ||
| 7 | #ifdef HAVE_JUMP_LABEL | 6 | #ifdef HAVE_JUMP_LABEL |
| 8 | 7 | ||
diff --git a/arch/arm/probes/kprobes/Makefile b/arch/arm/probes/kprobes/Makefile index bc8d504c3d78..76a36bf102b7 100644 --- a/arch/arm/probes/kprobes/Makefile +++ b/arch/arm/probes/kprobes/Makefile | |||
| @@ -7,5 +7,6 @@ obj-$(CONFIG_KPROBES) += actions-thumb.o checkers-thumb.o | |||
| 7 | test-kprobes-objs += test-thumb.o | 7 | test-kprobes-objs += test-thumb.o |
| 8 | else | 8 | else |
| 9 | obj-$(CONFIG_KPROBES) += actions-arm.o checkers-arm.o | 9 | obj-$(CONFIG_KPROBES) += actions-arm.o checkers-arm.o |
| 10 | obj-$(CONFIG_OPTPROBES) += opt-arm.o | ||
| 10 | test-kprobes-objs += test-arm.o | 11 | test-kprobes-objs += test-arm.o |
| 11 | endif | 12 | endif |
diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c index 3a58db4cc1c6..a4ec240ee7ba 100644 --- a/arch/arm/probes/kprobes/core.c +++ b/arch/arm/probes/kprobes/core.c | |||
| @@ -163,19 +163,31 @@ void __kprobes arch_arm_kprobe(struct kprobe *p) | |||
| 163 | * memory. It is also needed to atomically set the two half-words of a 32-bit | 163 | * memory. It is also needed to atomically set the two half-words of a 32-bit |
| 164 | * Thumb breakpoint. | 164 | * Thumb breakpoint. |
| 165 | */ | 165 | */ |
| 166 | int __kprobes __arch_disarm_kprobe(void *p) | 166 | struct patch { |
| 167 | { | 167 | void *addr; |
| 168 | struct kprobe *kp = p; | 168 | unsigned int insn; |
| 169 | void *addr = (void *)((uintptr_t)kp->addr & ~1); | 169 | }; |
| 170 | |||
| 171 | __patch_text(addr, kp->opcode); | ||
| 172 | 170 | ||
| 171 | static int __kprobes_remove_breakpoint(void *data) | ||
| 172 | { | ||
| 173 | struct patch *p = data; | ||
| 174 | __patch_text(p->addr, p->insn); | ||
| 173 | return 0; | 175 | return 0; |
| 174 | } | 176 | } |
| 175 | 177 | ||
| 178 | void __kprobes kprobes_remove_breakpoint(void *addr, unsigned int insn) | ||
| 179 | { | ||
| 180 | struct patch p = { | ||
| 181 | .addr = addr, | ||
| 182 | .insn = insn, | ||
| 183 | }; | ||
| 184 | stop_machine(__kprobes_remove_breakpoint, &p, cpu_online_mask); | ||
| 185 | } | ||
| 186 | |||
| 176 | void __kprobes arch_disarm_kprobe(struct kprobe *p) | 187 | void __kprobes arch_disarm_kprobe(struct kprobe *p) |
| 177 | { | 188 | { |
| 178 | stop_machine(__arch_disarm_kprobe, p, cpu_online_mask); | 189 | kprobes_remove_breakpoint((void *)((uintptr_t)p->addr & ~1), |
| 190 | p->opcode); | ||
| 179 | } | 191 | } |
| 180 | 192 | ||
| 181 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 193 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h index f88c79fe632a..b3036c587a76 100644 --- a/arch/arm/probes/kprobes/core.h +++ b/arch/arm/probes/kprobes/core.h | |||
| @@ -30,6 +30,8 @@ | |||
| 30 | #define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18 | 30 | #define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18 |
| 31 | #define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018 | 31 | #define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018 |
| 32 | 32 | ||
| 33 | extern void kprobes_remove_breakpoint(void *addr, unsigned int insn); | ||
| 34 | |||
| 33 | enum probes_insn __kprobes | 35 | enum probes_insn __kprobes |
| 34 | kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, | 36 | kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, |
| 35 | const struct decode_header *h); | 37 | const struct decode_header *h); |
diff --git a/arch/arm/probes/kprobes/opt-arm.c b/arch/arm/probes/kprobes/opt-arm.c new file mode 100644 index 000000000000..13d5232118df --- /dev/null +++ b/arch/arm/probes/kprobes/opt-arm.c | |||
| @@ -0,0 +1,322 @@ | |||
| 1 | /* | ||
| 2 | * Kernel Probes Jump Optimization (Optprobes) | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation; either version 2 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program; if not, write to the Free Software | ||
| 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 17 | * | ||
| 18 | * Copyright (C) IBM Corporation, 2002, 2004 | ||
| 19 | * Copyright (C) Hitachi Ltd., 2012 | ||
| 20 | * Copyright (C) Huawei Inc., 2014 | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/kprobes.h> | ||
| 24 | #include <linux/jump_label.h> | ||
| 25 | #include <asm/kprobes.h> | ||
| 26 | #include <asm/cacheflush.h> | ||
| 27 | /* for arm_gen_branch */ | ||
| 28 | #include <asm/insn.h> | ||
| 29 | /* for patch_text */ | ||
| 30 | #include <asm/patch.h> | ||
| 31 | |||
| 32 | #include "core.h" | ||
| 33 | |||
| 34 | /* | ||
| 35 | * NOTE: the first sub and add instruction will be modified according | ||
| 36 | * to the stack cost of the instruction. | ||
| 37 | */ | ||
| 38 | asm ( | ||
| 39 | ".global optprobe_template_entry\n" | ||
| 40 | "optprobe_template_entry:\n" | ||
| 41 | ".global optprobe_template_sub_sp\n" | ||
| 42 | "optprobe_template_sub_sp:" | ||
| 43 | " sub sp, sp, #0xff\n" | ||
| 44 | " stmia sp, {r0 - r14} \n" | ||
| 45 | ".global optprobe_template_add_sp\n" | ||
| 46 | "optprobe_template_add_sp:" | ||
| 47 | " add r3, sp, #0xff\n" | ||
| 48 | " str r3, [sp, #52]\n" | ||
| 49 | " mrs r4, cpsr\n" | ||
| 50 | " str r4, [sp, #64]\n" | ||
| 51 | " mov r1, sp\n" | ||
| 52 | " ldr r0, 1f\n" | ||
| 53 | " ldr r2, 2f\n" | ||
| 54 | /* | ||
| 55 | * AEABI requires an 8-bytes alignment stack. If | ||
| 56 | * SP % 8 != 0 (SP % 4 == 0 should be ensured), | ||
| 57 | * alloc more bytes here. | ||
| 58 | */ | ||
| 59 | " and r4, sp, #4\n" | ||
| 60 | " sub sp, sp, r4\n" | ||
| 61 | #if __LINUX_ARM_ARCH__ >= 5 | ||
| 62 | " blx r2\n" | ||
| 63 | #else | ||
| 64 | " mov lr, pc\n" | ||
| 65 | " mov pc, r2\n" | ||
| 66 | #endif | ||
| 67 | " add sp, sp, r4\n" | ||
| 68 | " ldr r1, [sp, #64]\n" | ||
| 69 | " tst r1, #"__stringify(PSR_T_BIT)"\n" | ||
| 70 | " ldrne r2, [sp, #60]\n" | ||
| 71 | " orrne r2, #1\n" | ||
| 72 | " strne r2, [sp, #60] @ set bit0 of PC for thumb\n" | ||
| 73 | " msr cpsr_cxsf, r1\n" | ||
| 74 | " ldmia sp, {r0 - r15}\n" | ||
| 75 | ".global optprobe_template_val\n" | ||
| 76 | "optprobe_template_val:\n" | ||
| 77 | "1: .long 0\n" | ||
| 78 | ".global optprobe_template_call\n" | ||
| 79 | "optprobe_template_call:\n" | ||
| 80 | "2: .long 0\n" | ||
| 81 | ".global optprobe_template_end\n" | ||
| 82 | "optprobe_template_end:\n"); | ||
| 83 | |||
| 84 | #define TMPL_VAL_IDX \ | ||
| 85 | ((unsigned long *)&optprobe_template_val - (unsigned long *)&optprobe_template_entry) | ||
| 86 | #define TMPL_CALL_IDX \ | ||
| 87 | ((unsigned long *)&optprobe_template_call - (unsigned long *)&optprobe_template_entry) | ||
| 88 | #define TMPL_END_IDX \ | ||
| 89 | ((unsigned long *)&optprobe_template_end - (unsigned long *)&optprobe_template_entry) | ||
| 90 | #define TMPL_ADD_SP \ | ||
| 91 | ((unsigned long *)&optprobe_template_add_sp - (unsigned long *)&optprobe_template_entry) | ||
| 92 | #define TMPL_SUB_SP \ | ||
| 93 | ((unsigned long *)&optprobe_template_sub_sp - (unsigned long *)&optprobe_template_entry) | ||
| 94 | |||
| 95 | /* | ||
| 96 | * ARM can always optimize an instruction when using ARM ISA, except | ||
| 97 | * instructions like 'str r0, [sp, r1]' which store to stack and unable | ||
| 98 | * to determine stack space consumption statically. | ||
| 99 | */ | ||
| 100 | int arch_prepared_optinsn(struct arch_optimized_insn *optinsn) | ||
| 101 | { | ||
| 102 | return optinsn->insn != NULL; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* | ||
| 106 | * In ARM ISA, kprobe opt always replace one instruction (4 bytes | ||
| 107 | * aligned and 4 bytes long). It is impossible to encounter another | ||
| 108 | * kprobe in the address range. So always return 0. | ||
| 109 | */ | ||
| 110 | int arch_check_optimized_kprobe(struct optimized_kprobe *op) | ||
| 111 | { | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Caller must ensure addr & 3 == 0 */ | ||
| 116 | static int can_optimize(struct kprobe *kp) | ||
| 117 | { | ||
| 118 | if (kp->ainsn.stack_space < 0) | ||
| 119 | return 0; | ||
| 120 | /* | ||
| 121 | * 255 is the biggest imm can be used in 'sub r0, r0, #<imm>'. | ||
| 122 | * Number larger than 255 needs special encoding. | ||
| 123 | */ | ||
| 124 | if (kp->ainsn.stack_space > 255 - sizeof(struct pt_regs)) | ||
| 125 | return 0; | ||
| 126 | return 1; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Free optimized instruction slot */ | ||
| 130 | static void | ||
| 131 | __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty) | ||
| 132 | { | ||
| 133 | if (op->optinsn.insn) { | ||
| 134 | free_optinsn_slot(op->optinsn.insn, dirty); | ||
| 135 | op->optinsn.insn = NULL; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | extern void kprobe_handler(struct pt_regs *regs); | ||
| 140 | |||
| 141 | static void | ||
| 142 | optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) | ||
| 143 | { | ||
| 144 | unsigned long flags; | ||
| 145 | struct kprobe *p = &op->kp; | ||
| 146 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | ||
| 147 | |||
| 148 | /* Save skipped registers */ | ||
| 149 | regs->ARM_pc = (unsigned long)op->kp.addr; | ||
| 150 | regs->ARM_ORIG_r0 = ~0UL; | ||
| 151 | |||
| 152 | local_irq_save(flags); | ||
| 153 | |||
| 154 | if (kprobe_running()) { | ||
| 155 | kprobes_inc_nmissed_count(&op->kp); | ||
| 156 | } else { | ||
| 157 | __this_cpu_write(current_kprobe, &op->kp); | ||
| 158 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; | ||
| 159 | opt_pre_handler(&op->kp, regs); | ||
| 160 | __this_cpu_write(current_kprobe, NULL); | ||
| 161 | } | ||
| 162 | |||
| 163 | /* In each case, we must singlestep the replaced instruction. */ | ||
| 164 | op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); | ||
| 165 | |||
| 166 | local_irq_restore(flags); | ||
| 167 | } | ||
| 168 | |||
| 169 | int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig) | ||
| 170 | { | ||
| 171 | kprobe_opcode_t *code; | ||
| 172 | unsigned long rel_chk; | ||
| 173 | unsigned long val; | ||
| 174 | unsigned long stack_protect = sizeof(struct pt_regs); | ||
| 175 | |||
| 176 | if (!can_optimize(orig)) | ||
| 177 | return -EILSEQ; | ||
| 178 | |||
| 179 | code = get_optinsn_slot(); | ||
| 180 | if (!code) | ||
| 181 | return -ENOMEM; | ||
| 182 | |||
| 183 | /* | ||
| 184 | * Verify if the address gap is in 32MiB range, because this uses | ||
| 185 | * a relative jump. | ||
| 186 | * | ||
| 187 | * kprobe opt use a 'b' instruction to branch to optinsn.insn. | ||
| 188 | * According to ARM manual, branch instruction is: | ||
| 189 | * | ||
| 190 | * 31 28 27 24 23 0 | ||
| 191 | * +------+---+---+---+---+----------------+ | ||
| 192 | * | cond | 1 | 0 | 1 | 0 | imm24 | | ||
| 193 | * +------+---+---+---+---+----------------+ | ||
| 194 | * | ||
| 195 | * imm24 is a signed 24 bits integer. The real branch offset is computed | ||
| 196 | * by: imm32 = SignExtend(imm24:'00', 32); | ||
| 197 | * | ||
| 198 | * So the maximum forward branch should be: | ||
| 199 | * (0x007fffff << 2) = 0x01fffffc = 0x1fffffc | ||
| 200 | * The maximum backword branch should be: | ||
| 201 | * (0xff800000 << 2) = 0xfe000000 = -0x2000000 | ||
| 202 | * | ||
| 203 | * We can simply check (rel & 0xfe000003): | ||
| 204 | * if rel is positive, (rel & 0xfe000000) shoule be 0 | ||
| 205 | * if rel is negitive, (rel & 0xfe000000) should be 0xfe000000 | ||
| 206 | * the last '3' is used for alignment checking. | ||
| 207 | */ | ||
| 208 | rel_chk = (unsigned long)((long)code - | ||
| 209 | (long)orig->addr + 8) & 0xfe000003; | ||
| 210 | |||
| 211 | if ((rel_chk != 0) && (rel_chk != 0xfe000000)) { | ||
| 212 | /* | ||
| 213 | * Different from x86, we free code buf directly instead of | ||
| 214 | * calling __arch_remove_optimized_kprobe() because | ||
| 215 | * we have not fill any field in op. | ||
| 216 | */ | ||
| 217 | free_optinsn_slot(code, 0); | ||
| 218 | return -ERANGE; | ||
| 219 | } | ||
| 220 | |||
| 221 | /* Copy arch-dep-instance from template. */ | ||
| 222 | memcpy(code, &optprobe_template_entry, | ||
| 223 | TMPL_END_IDX * sizeof(kprobe_opcode_t)); | ||
| 224 | |||
| 225 | /* Adjust buffer according to instruction. */ | ||
| 226 | BUG_ON(orig->ainsn.stack_space < 0); | ||
| 227 | |||
| 228 | stack_protect += orig->ainsn.stack_space; | ||
| 229 | |||
| 230 | /* Should have been filtered by can_optimize(). */ | ||
| 231 | BUG_ON(stack_protect > 255); | ||
| 232 | |||
| 233 | /* Create a 'sub sp, sp, #<stack_protect>' */ | ||
| 234 | code[TMPL_SUB_SP] = __opcode_to_mem_arm(0xe24dd000 | stack_protect); | ||
| 235 | /* Create a 'add r3, sp, #<stack_protect>' */ | ||
| 236 | code[TMPL_ADD_SP] = __opcode_to_mem_arm(0xe28d3000 | stack_protect); | ||
| 237 | |||
| 238 | /* Set probe information */ | ||
| 239 | val = (unsigned long)op; | ||
| 240 | code[TMPL_VAL_IDX] = val; | ||
| 241 | |||
| 242 | /* Set probe function call */ | ||
| 243 | val = (unsigned long)optimized_callback; | ||
| 244 | code[TMPL_CALL_IDX] = val; | ||
| 245 | |||
| 246 | flush_icache_range((unsigned long)code, | ||
| 247 | (unsigned long)(&code[TMPL_END_IDX])); | ||
| 248 | |||
| 249 | /* Set op->optinsn.insn means prepared. */ | ||
| 250 | op->optinsn.insn = code; | ||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | |||
| 254 | void __kprobes arch_optimize_kprobes(struct list_head *oplist) | ||
| 255 | { | ||
| 256 | struct optimized_kprobe *op, *tmp; | ||
| 257 | |||
| 258 | list_for_each_entry_safe(op, tmp, oplist, list) { | ||
| 259 | unsigned long insn; | ||
| 260 | WARN_ON(kprobe_disabled(&op->kp)); | ||
| 261 | |||
| 262 | /* | ||
| 263 | * Backup instructions which will be replaced | ||
| 264 | * by jump address | ||
| 265 | */ | ||
| 266 | memcpy(op->optinsn.copied_insn, op->kp.addr, | ||
| 267 | RELATIVEJUMP_SIZE); | ||
| 268 | |||
| 269 | insn = arm_gen_branch((unsigned long)op->kp.addr, | ||
| 270 | (unsigned long)op->optinsn.insn); | ||
| 271 | BUG_ON(insn == 0); | ||
| 272 | |||
| 273 | /* | ||
| 274 | * Make it a conditional branch if replaced insn | ||
| 275 | * is consitional | ||
| 276 | */ | ||
| 277 | insn = (__mem_to_opcode_arm( | ||
| 278 | op->optinsn.copied_insn[0]) & 0xf0000000) | | ||
| 279 | (insn & 0x0fffffff); | ||
| 280 | |||
| 281 | /* | ||
| 282 | * Similar to __arch_disarm_kprobe, operations which | ||
| 283 | * removing breakpoints must be wrapped by stop_machine | ||
| 284 | * to avoid racing. | ||
| 285 | */ | ||
| 286 | kprobes_remove_breakpoint(op->kp.addr, insn); | ||
| 287 | |||
| 288 | list_del_init(&op->list); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | void arch_unoptimize_kprobe(struct optimized_kprobe *op) | ||
| 293 | { | ||
| 294 | arch_arm_kprobe(&op->kp); | ||
| 295 | } | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Recover original instructions and breakpoints from relative jumps. | ||
| 299 | * Caller must call with locking kprobe_mutex. | ||
| 300 | */ | ||
| 301 | void arch_unoptimize_kprobes(struct list_head *oplist, | ||
| 302 | struct list_head *done_list) | ||
| 303 | { | ||
| 304 | struct optimized_kprobe *op, *tmp; | ||
| 305 | |||
| 306 | list_for_each_entry_safe(op, tmp, oplist, list) { | ||
| 307 | arch_unoptimize_kprobe(op); | ||
| 308 | list_move(&op->list, done_list); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | int arch_within_optimized_kprobe(struct optimized_kprobe *op, | ||
| 313 | unsigned long addr) | ||
| 314 | { | ||
| 315 | return ((unsigned long)op->kp.addr <= addr && | ||
| 316 | (unsigned long)op->kp.addr + RELATIVEJUMP_SIZE > addr); | ||
| 317 | } | ||
| 318 | |||
| 319 | void arch_remove_optimized_kprobe(struct optimized_kprobe *op) | ||
| 320 | { | ||
| 321 | __arch_remove_optimized_kprobe(op, 1); | ||
| 322 | } | ||
