Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: optprobes: execute instruction during restoring if possible.

This patch removes software emulation or simulation for most of probed
instructions. If the instruction doesn't use PC relative addressing,
it will be translated into following instructions in the restore code
in code template:

ldmia {r0 - r14} // restore all instruction except PC
<instruction> // direct execute the probed instruction
b next_insn // branch to next instruction.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Jon Medhurst <tixy@linaro.org>

authored by

Wang Nan and committed by
Jon Medhurst
bfc9657d 28a1899d

+54 -2
+3
arch/arm/include/asm/kprobes.h
··· 57 57 extern __visible kprobe_opcode_t optprobe_template_end; 58 58 extern __visible kprobe_opcode_t optprobe_template_sub_sp; 59 59 extern __visible kprobe_opcode_t optprobe_template_add_sp; 60 + extern __visible kprobe_opcode_t optprobe_template_restore_begin; 61 + extern __visible kprobe_opcode_t optprobe_template_restore_orig_insn; 62 + extern __visible kprobe_opcode_t optprobe_template_restore_end; 60 63 61 64 #define MAX_OPTIMIZED_LENGTH 4 62 65 #define MAX_OPTINSN_SIZE \
+1
arch/arm/include/asm/probes.h
··· 42 42 probes_insn_fn_t *insn_fn; 43 43 int stack_space; 44 44 unsigned long register_usage_flags; 45 + bool kprobe_direct_exec; 45 46 }; 46 47 47 48 #endif /* __ASSEMBLY__ */
+50 -2
arch/arm/probes/kprobes/opt-arm.c
··· 32 32 #include "core.h" 33 33 34 34 /* 35 + * See register_usage_flags. If the probed instruction doesn't use PC, 36 + * we can copy it into template and have it executed directly without 37 + * simulation or emulation. 38 + */ 39 + #define ARM_REG_PC 15 40 + #define can_kprobe_direct_exec(m) (!test_bit(ARM_REG_PC, &(m))) 41 + 42 + /* 35 43 * NOTE: the first sub and add instruction will be modified according 36 44 * to the stack cost of the instruction. 37 45 */ ··· 79 71 " orrne r2, #1\n" 80 72 " strne r2, [sp, #60] @ set bit0 of PC for thumb\n" 81 73 " msr cpsr_cxsf, r1\n" 74 + ".global optprobe_template_restore_begin\n" 75 + "optprobe_template_restore_begin:\n" 82 76 " ldmia sp, {r0 - r15}\n" 77 + ".global optprobe_template_restore_orig_insn\n" 78 + "optprobe_template_restore_orig_insn:\n" 79 + " nop\n" 80 + ".global optprobe_template_restore_end\n" 81 + "optprobe_template_restore_end:\n" 82 + " nop\n" 83 83 ".global optprobe_template_val\n" 84 84 "optprobe_template_val:\n" 85 85 "1: .long 0\n" ··· 107 91 ((unsigned long *)&optprobe_template_add_sp - (unsigned long *)&optprobe_template_entry) 108 92 #define TMPL_SUB_SP \ 109 93 ((unsigned long *)&optprobe_template_sub_sp - (unsigned long *)&optprobe_template_entry) 94 + #define TMPL_RESTORE_BEGIN \ 95 + ((unsigned long *)&optprobe_template_restore_begin - (unsigned long *)&optprobe_template_entry) 96 + #define TMPL_RESTORE_ORIGN_INSN \ 97 + ((unsigned long *)&optprobe_template_restore_orig_insn - (unsigned long *)&optprobe_template_entry) 98 + #define TMPL_RESTORE_END \ 99 + ((unsigned long *)&optprobe_template_restore_end - (unsigned long *)&optprobe_template_entry) 110 100 111 101 /* 112 102 * ARM can always optimize an instruction when using ARM ISA, except ··· 182 160 __this_cpu_write(current_kprobe, NULL); 183 161 } 184 162 185 - /* In each case, we must singlestep the replaced instruction. */ 186 - op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); 163 + /* 164 + * We singlestep the replaced instruction only when it can't be 165 + * executed directly during restore. 166 + */ 167 + if (!p->ainsn.kprobe_direct_exec) 168 + op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); 187 169 188 170 local_irq_restore(flags); 189 171 } ··· 268 242 /* Set probe function call */ 269 243 val = (unsigned long)optimized_callback; 270 244 code[TMPL_CALL_IDX] = val; 245 + 246 + /* If possible, copy insn and have it executed during restore */ 247 + orig->ainsn.kprobe_direct_exec = false; 248 + if (can_kprobe_direct_exec(orig->ainsn.register_usage_flags)) { 249 + kprobe_opcode_t final_branch = arm_gen_branch( 250 + (unsigned long)(&code[TMPL_RESTORE_END]), 251 + (unsigned long)(op->kp.addr) + 4); 252 + if (final_branch != 0) { 253 + /* 254 + * Replace original 'ldmia sp, {r0 - r15}' with 255 + * 'ldmia {r0 - r14}', restore all registers except pc. 256 + */ 257 + code[TMPL_RESTORE_BEGIN] = __opcode_to_mem_arm(0xe89d7fff); 258 + 259 + /* The original probed instruction */ 260 + code[TMPL_RESTORE_ORIGN_INSN] = __opcode_to_mem_arm(orig->opcode); 261 + 262 + /* Jump back to next instruction */ 263 + code[TMPL_RESTORE_END] = __opcode_to_mem_arm(final_branch); 264 + orig->ainsn.kprobe_direct_exec = true; 265 + } 266 + } 271 267 272 268 flush_icache_range((unsigned long)code, 273 269 (unsigned long)(&code[TMPL_END_IDX]));