Branch data Line data Source code
1 : : /* 2 : : * Dynamic function tracing support. 3 : : * 4 : : * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com> 5 : : * Copyright (C) 2010 Rabin Vincent <rabin@rab.in> 6 : : * 7 : : * For licencing details, see COPYING. 8 : : * 9 : : * Defines low-level handling of mcount calls when the kernel 10 : : * is compiled with the -pg flag. When using dynamic ftrace, the 11 : : * mcount call-sites get patched with NOP till they are enabled. 12 : : * All code mutation routines here are called under stop_machine(). 13 : : */ 14 : : 15 : : #include <linux/ftrace.h> 16 : : #include <linux/uaccess.h> 17 : : #include <linux/module.h> 18 : : #include <linux/stop_machine.h> 19 : : 20 : : #include <asm/cacheflush.h> 21 : : #include <asm/opcodes.h> 22 : : #include <asm/ftrace.h> 23 : : #include <asm/insn.h> 24 : : #include <asm/set_memory.h> 25 : : 26 : : #ifdef CONFIG_THUMB2_KERNEL 27 : : #define NOP 0xf85deb04 /* pop.w {lr} */ 28 : : #else 29 : : #define NOP 0xe8bd4000 /* pop {lr} */ 30 : : #endif 31 : : 32 : : #ifdef CONFIG_DYNAMIC_FTRACE 33 : : 34 : 0 : static int __ftrace_modify_code(void *data) 35 : : { 36 : : int *command = data; 37 : : 38 : 0 : set_kernel_text_rw(); 39 : 0 : ftrace_modify_all_code(*command); 40 : 0 : set_kernel_text_ro(); 41 : : 42 : 0 : return 0; 43 : : } 44 : : 45 : 0 : void arch_ftrace_update_code(int command) 46 : : { 47 : 0 : stop_machine(__ftrace_modify_code, &command, NULL); 48 : 0 : } 49 : : 50 : : static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) 51 : : { 52 : : return NOP; 53 : : } 54 : : 55 : : static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) 56 : : { 57 : : return addr; 58 : : } 59 : : 60 : 0 : int ftrace_arch_code_modify_prepare(void) 61 : : { 62 : 0 : set_all_modules_text_rw(); 63 : 0 : return 0; 64 : : } 65 : : 66 : 0 : int ftrace_arch_code_modify_post_process(void) 67 : : { 68 : 0 : set_all_modules_text_ro(); 69 : : /* Make sure any TLB misses during machine stop are cleared. */ 70 : 0 : flush_tlb_all(); 71 : 0 : return 0; 72 : : } 73 : : 74 : : static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) 75 : : { 76 : : return arm_gen_branch_link(pc, addr); 77 : : } 78 : : 79 : 3 : static int ftrace_modify_code(unsigned long pc, unsigned long old, 80 : : unsigned long new, bool validate) 81 : : { 82 : : unsigned long replaced; 83 : : 84 : : if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) { 85 : : old = __opcode_to_mem_thumb32(old); 86 : : new = __opcode_to_mem_thumb32(new); 87 : : } else { 88 : : old = __opcode_to_mem_arm(old); 89 : : new = __opcode_to_mem_arm(new); 90 : : } 91 : : 92 : 3 : if (validate) { 93 : 3 : if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE)) 94 : : return -EFAULT; 95 : : 96 : 3 : if (replaced != old) 97 : : return -EINVAL; 98 : : } 99 : : 100 : 3 : if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE)) 101 : : return -EPERM; 102 : : 103 : 3 : flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); 104 : : 105 : 3 : return 0; 106 : : } 107 : : 108 : 0 : int ftrace_update_ftrace_func(ftrace_func_t func) 109 : : { 110 : : unsigned long pc; 111 : : unsigned long new; 112 : : int ret; 113 : : 114 : 0 : pc = (unsigned long)&ftrace_call; 115 : 0 : new = ftrace_call_replace(pc, (unsigned long)func); 116 : : 117 : 0 : ret = ftrace_modify_code(pc, 0, new, false); 118 : : 119 : : #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 120 : 0 : if (!ret) { 121 : 0 : pc = (unsigned long)&ftrace_regs_call; 122 : : new = ftrace_call_replace(pc, (unsigned long)func); 123 : : 124 : 0 : ret = ftrace_modify_code(pc, 0, new, false); 125 : : } 126 : : #endif 127 : : 128 : 0 : return ret; 129 : : } 130 : : 131 : 0 : int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 132 : : { 133 : : unsigned long new, old; 134 : 0 : unsigned long ip = rec->ip; 135 : : 136 : : old = ftrace_nop_replace(rec); 137 : : 138 : : new = ftrace_call_replace(ip, adjust_address(rec, addr)); 139 : : 140 : 0 : return ftrace_modify_code(rec->ip, old, new, true); 141 : : } 142 : : 143 : : #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 144 : : 145 : 0 : int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, 146 : : unsigned long addr) 147 : : { 148 : : unsigned long new, old; 149 : 0 : unsigned long ip = rec->ip; 150 : : 151 : : old = ftrace_call_replace(ip, adjust_address(rec, old_addr)); 152 : : 153 : : new = ftrace_call_replace(ip, adjust_address(rec, addr)); 154 : : 155 : 0 : return ftrace_modify_code(rec->ip, old, new, true); 156 : : } 157 : : 158 : : #endif 159 : : 160 : 3 : int ftrace_make_nop(struct module *mod, 161 : : struct dyn_ftrace *rec, unsigned long addr) 162 : : { 163 : 3 : unsigned long ip = rec->ip; 164 : : unsigned long old; 165 : : unsigned long new; 166 : : int ret; 167 : : 168 : : old = ftrace_call_replace(ip, adjust_address(rec, addr)); 169 : : new = ftrace_nop_replace(rec); 170 : 3 : ret = ftrace_modify_code(ip, old, new, true); 171 : : 172 : 3 : return ret; 173 : : } 174 : : 175 : 3 : int __init ftrace_dyn_arch_init(void) 176 : : { 177 : 3 : return 0; 178 : : } 179 : : #endif /* CONFIG_DYNAMIC_FTRACE */ 180 : : 181 : : #ifdef CONFIG_FUNCTION_GRAPH_TRACER 182 : 0 : void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, 183 : : unsigned long frame_pointer) 184 : : { 185 : 0 : unsigned long return_hooker = (unsigned long) &return_to_handler; 186 : : unsigned long old; 187 : : 188 : 0 : if (unlikely(atomic_read(¤t->tracing_graph_pause))) 189 : 0 : return; 190 : : 191 : 0 : old = *parent; 192 : 0 : *parent = return_hooker; 193 : : 194 : 0 : if (function_graph_enter(old, self_addr, frame_pointer, NULL)) 195 : 0 : *parent = old; 196 : : } 197 : : 198 : : #ifdef CONFIG_DYNAMIC_FTRACE 199 : : extern unsigned long ftrace_graph_call; 200 : : extern unsigned long ftrace_graph_call_old; 201 : : extern void ftrace_graph_caller_old(void); 202 : : extern unsigned long ftrace_graph_regs_call; 203 : : extern void ftrace_graph_regs_caller(void); 204 : : 205 : 0 : static int __ftrace_modify_caller(unsigned long *callsite, 206 : : void (*func) (void), bool enable) 207 : : { 208 : 0 : unsigned long caller_fn = (unsigned long) func; 209 : 0 : unsigned long pc = (unsigned long) callsite; 210 : : unsigned long branch = arm_gen_branch(pc, caller_fn); 211 : : unsigned long nop = 0xe1a00000; /* mov r0, r0 */ 212 : 0 : unsigned long old = enable ? nop : branch; 213 : 0 : unsigned long new = enable ? branch : nop; 214 : : 215 : 0 : return ftrace_modify_code(pc, old, new, true); 216 : : } 217 : : 218 : 0 : static int ftrace_modify_graph_caller(bool enable) 219 : : { 220 : : int ret; 221 : : 222 : 0 : ret = __ftrace_modify_caller(&ftrace_graph_call, 223 : : ftrace_graph_caller, 224 : : enable); 225 : : 226 : : #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 227 : 0 : if (!ret) 228 : 0 : ret = __ftrace_modify_caller(&ftrace_graph_regs_call, 229 : : ftrace_graph_regs_caller, 230 : : enable); 231 : : #endif 232 : : 233 : : 234 : 0 : return ret; 235 : : } 236 : : 237 : 0 : int ftrace_enable_ftrace_graph_caller(void) 238 : : { 239 : 0 : return ftrace_modify_graph_caller(true); 240 : : } 241 : : 242 : 0 : int ftrace_disable_ftrace_graph_caller(void) 243 : : { 244 : 0 : return ftrace_modify_graph_caller(false); 245 : : } 246 : : #endif /* CONFIG_DYNAMIC_FTRACE */ 247 : : #endif /* CONFIG_FUNCTION_GRAPH_TRACER */