1230c77a5SGuo Ren // SPDX-License-Identifier: GPL-2.0 2230c77a5SGuo Ren // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 3230c77a5SGuo Ren 4230c77a5SGuo Ren #include <linux/ftrace.h> 5230c77a5SGuo Ren #include <linux/uaccess.h> 6dd7c983eSGuo Ren #include <linux/stop_machine.h> 728bb030fSGuo Ren #include <asm/cacheflush.h> 828bb030fSGuo Ren 928bb030fSGuo Ren #ifdef CONFIG_DYNAMIC_FTRACE 1028bb030fSGuo Ren 1128bb030fSGuo Ren #define NOP 0x4000 1228bb030fSGuo Ren #define NOP32_HI 0xc400 1328bb030fSGuo Ren #define NOP32_LO 0x4820 1428bb030fSGuo Ren #define PUSH_LR 0x14d0 1528bb030fSGuo Ren #define MOVIH_LINK 0xea3a 1628bb030fSGuo Ren #define ORI_LINK 0xef5a 1728bb030fSGuo Ren #define JSR_LINK 0xe8fa 1828bb030fSGuo Ren #define BSR_LINK 0xe000 1928bb030fSGuo Ren 2028bb030fSGuo Ren /* 2128bb030fSGuo Ren * Gcc-csky with -pg will insert stub in function prologue: 2228bb030fSGuo Ren * push lr 2328bb030fSGuo Ren * jbsr _mcount 2428bb030fSGuo Ren * nop32 2528bb030fSGuo Ren * nop32 2628bb030fSGuo Ren * 2728bb030fSGuo Ren * If the (callee - current_pc) is less then 64MB, we'll use bsr: 2828bb030fSGuo Ren * push lr 2928bb030fSGuo Ren * bsr _mcount 3028bb030fSGuo Ren * nop32 3128bb030fSGuo Ren * nop32 3228bb030fSGuo Ren * else we'll use (movih + ori + jsr): 3328bb030fSGuo Ren * push lr 3428bb030fSGuo Ren * movih r26, ... 3528bb030fSGuo Ren * ori r26, ... 3628bb030fSGuo Ren * jsr r26 3728bb030fSGuo Ren * 3828bb030fSGuo Ren * (r26 is our reserved link-reg) 3928bb030fSGuo Ren * 4028bb030fSGuo Ren */ 4128bb030fSGuo Ren static inline void make_jbsr(unsigned long callee, unsigned long pc, 4228bb030fSGuo Ren uint16_t *call, bool nolr) 4328bb030fSGuo Ren { 4428bb030fSGuo Ren long offset; 4528bb030fSGuo Ren 4628bb030fSGuo Ren call[0] = nolr ? NOP : PUSH_LR; 4728bb030fSGuo Ren 4828bb030fSGuo Ren offset = (long) callee - (long) pc; 4928bb030fSGuo Ren 5028bb030fSGuo Ren if (unlikely(offset < -67108864 || offset > 67108864)) { 5128bb030fSGuo Ren call[1] = MOVIH_LINK; 5228bb030fSGuo Ren call[2] = callee >> 16; 5328bb030fSGuo Ren call[3] = ORI_LINK; 5428bb030fSGuo Ren call[4] = callee & 0xffff; 5528bb030fSGuo Ren call[5] = JSR_LINK; 5628bb030fSGuo Ren call[6] = 0; 5728bb030fSGuo Ren } else { 5828bb030fSGuo Ren offset = offset >> 1; 5928bb030fSGuo Ren 6028bb030fSGuo Ren call[1] = BSR_LINK | 6128bb030fSGuo Ren ((uint16_t)((unsigned long) offset >> 16) & 0x3ff); 6228bb030fSGuo Ren call[2] = (uint16_t)((unsigned long) offset & 0xffff); 6328bb030fSGuo Ren call[3] = call[5] = NOP32_HI; 6428bb030fSGuo Ren call[4] = call[6] = NOP32_LO; 6528bb030fSGuo Ren } 6628bb030fSGuo Ren } 6728bb030fSGuo Ren 6828bb030fSGuo Ren static uint16_t nops[7] = {NOP, NOP32_HI, NOP32_LO, NOP32_HI, NOP32_LO, 6928bb030fSGuo Ren NOP32_HI, NOP32_LO}; 7028bb030fSGuo Ren static int ftrace_check_current_nop(unsigned long hook) 7128bb030fSGuo Ren { 7228bb030fSGuo Ren uint16_t olds[7]; 7328bb030fSGuo Ren unsigned long hook_pos = hook - 2; 7428bb030fSGuo Ren 75*fe557319SChristoph Hellwig if (copy_from_kernel_nofault((void *)olds, (void *)hook_pos, 76*fe557319SChristoph Hellwig sizeof(nops))) 7728bb030fSGuo Ren return -EFAULT; 7828bb030fSGuo Ren 7928bb030fSGuo Ren if (memcmp((void *)nops, (void *)olds, sizeof(nops))) { 8028bb030fSGuo Ren pr_err("%p: nop but get (%04x %04x %04x %04x %04x %04x %04x)\n", 8128bb030fSGuo Ren (void *)hook_pos, 8228bb030fSGuo Ren olds[0], olds[1], olds[2], olds[3], olds[4], olds[5], 8328bb030fSGuo Ren olds[6]); 8428bb030fSGuo Ren 8528bb030fSGuo Ren return -EINVAL; 8628bb030fSGuo Ren } 8728bb030fSGuo Ren 8828bb030fSGuo Ren return 0; 8928bb030fSGuo Ren } 9028bb030fSGuo Ren 9128bb030fSGuo Ren static int ftrace_modify_code(unsigned long hook, unsigned long target, 9228bb030fSGuo Ren bool enable, bool nolr) 9328bb030fSGuo Ren { 9428bb030fSGuo Ren uint16_t call[7]; 9528bb030fSGuo Ren 9628bb030fSGuo Ren unsigned long hook_pos = hook - 2; 9728bb030fSGuo Ren int ret = 0; 9828bb030fSGuo Ren 9928bb030fSGuo Ren make_jbsr(target, hook, call, nolr); 10028bb030fSGuo Ren 101*fe557319SChristoph Hellwig ret = copy_to_kernel_nofault((void *)hook_pos, enable ? call : nops, 10228bb030fSGuo Ren sizeof(nops)); 10328bb030fSGuo Ren if (ret) 10428bb030fSGuo Ren return -EPERM; 10528bb030fSGuo Ren 10628bb030fSGuo Ren flush_icache_range(hook_pos, hook_pos + MCOUNT_INSN_SIZE); 10728bb030fSGuo Ren 10828bb030fSGuo Ren return 0; 10928bb030fSGuo Ren } 11028bb030fSGuo Ren 11128bb030fSGuo Ren int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 11228bb030fSGuo Ren { 11328bb030fSGuo Ren int ret = ftrace_check_current_nop(rec->ip); 11428bb030fSGuo Ren 11528bb030fSGuo Ren if (ret) 11628bb030fSGuo Ren return ret; 11728bb030fSGuo Ren 11828bb030fSGuo Ren return ftrace_modify_code(rec->ip, addr, true, false); 11928bb030fSGuo Ren } 12028bb030fSGuo Ren 12128bb030fSGuo Ren int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, 12228bb030fSGuo Ren unsigned long addr) 12328bb030fSGuo Ren { 12428bb030fSGuo Ren return ftrace_modify_code(rec->ip, addr, false, false); 12528bb030fSGuo Ren } 12628bb030fSGuo Ren 12728bb030fSGuo Ren int ftrace_update_ftrace_func(ftrace_func_t func) 12828bb030fSGuo Ren { 12928bb030fSGuo Ren int ret = ftrace_modify_code((unsigned long)&ftrace_call, 13028bb030fSGuo Ren (unsigned long)func, true, true); 13189a3927aSGuo Ren if (!ret) 13289a3927aSGuo Ren ret = ftrace_modify_code((unsigned long)&ftrace_regs_call, 13389a3927aSGuo Ren (unsigned long)func, true, true); 13428bb030fSGuo Ren return ret; 13528bb030fSGuo Ren } 13628bb030fSGuo Ren 13728bb030fSGuo Ren int __init ftrace_dyn_arch_init(void) 13828bb030fSGuo Ren { 13928bb030fSGuo Ren return 0; 14028bb030fSGuo Ren } 14128bb030fSGuo Ren #endif /* CONFIG_DYNAMIC_FTRACE */ 142230c77a5SGuo Ren 14389a3927aSGuo Ren #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 14489a3927aSGuo Ren int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, 14589a3927aSGuo Ren unsigned long addr) 14689a3927aSGuo Ren { 14789a3927aSGuo Ren return ftrace_modify_code(rec->ip, addr, true, true); 14889a3927aSGuo Ren } 14989a3927aSGuo Ren #endif 15089a3927aSGuo Ren 151d7950be1SGuo Ren #ifdef CONFIG_FUNCTION_GRAPH_TRACER 152d7950be1SGuo Ren void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, 153d7950be1SGuo Ren unsigned long frame_pointer) 154230c77a5SGuo Ren { 155d7950be1SGuo Ren unsigned long return_hooker = (unsigned long)&return_to_handler; 156d7950be1SGuo Ren unsigned long old; 157230c77a5SGuo Ren 158d7950be1SGuo Ren if (unlikely(atomic_read(¤t->tracing_graph_pause))) 159d7950be1SGuo Ren return; 160d7950be1SGuo Ren 161d7950be1SGuo Ren old = *parent; 162d7950be1SGuo Ren 163d7950be1SGuo Ren if (!function_graph_enter(old, self_addr, 164d7950be1SGuo Ren *(unsigned long *)frame_pointer, parent)) { 165d7950be1SGuo Ren /* 166d7950be1SGuo Ren * For csky-gcc function has sub-call: 167d7950be1SGuo Ren * subi sp, sp, 8 168d7950be1SGuo Ren * stw r8, (sp, 0) 169d7950be1SGuo Ren * mov r8, sp 170d7950be1SGuo Ren * st.w r15, (sp, 0x4) 171d7950be1SGuo Ren * push r15 172d7950be1SGuo Ren * jl _mcount 173d7950be1SGuo Ren * We only need set *parent for resume 174d7950be1SGuo Ren * 175d7950be1SGuo Ren * For csky-gcc function has no sub-call: 176d7950be1SGuo Ren * subi sp, sp, 4 177d7950be1SGuo Ren * stw r8, (sp, 0) 178d7950be1SGuo Ren * mov r8, sp 179d7950be1SGuo Ren * push r15 180d7950be1SGuo Ren * jl _mcount 181d7950be1SGuo Ren * We need set *parent and *(frame_pointer + 4) for resume, 182d7950be1SGuo Ren * because lr is resumed twice. 183d7950be1SGuo Ren */ 184d7950be1SGuo Ren *parent = return_hooker; 185d7950be1SGuo Ren frame_pointer += 4; 186d7950be1SGuo Ren if (*(unsigned long *)frame_pointer == old) 187d7950be1SGuo Ren *(unsigned long *)frame_pointer = return_hooker; 188230c77a5SGuo Ren } 189d7950be1SGuo Ren } 19028bb030fSGuo Ren 19128bb030fSGuo Ren #ifdef CONFIG_DYNAMIC_FTRACE 19228bb030fSGuo Ren int ftrace_enable_ftrace_graph_caller(void) 19328bb030fSGuo Ren { 19428bb030fSGuo Ren return ftrace_modify_code((unsigned long)&ftrace_graph_call, 19528bb030fSGuo Ren (unsigned long)&ftrace_graph_caller, true, true); 19628bb030fSGuo Ren } 19728bb030fSGuo Ren 19828bb030fSGuo Ren int ftrace_disable_ftrace_graph_caller(void) 19928bb030fSGuo Ren { 20028bb030fSGuo Ren return ftrace_modify_code((unsigned long)&ftrace_graph_call, 20128bb030fSGuo Ren (unsigned long)&ftrace_graph_caller, false, true); 20228bb030fSGuo Ren } 20328bb030fSGuo Ren #endif /* CONFIG_DYNAMIC_FTRACE */ 20428bb030fSGuo Ren #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 205230c77a5SGuo Ren 206a13d5887SGuo Ren #ifdef CONFIG_DYNAMIC_FTRACE 207dd7c983eSGuo Ren #ifndef CONFIG_CPU_HAS_ICACHE_INS 208dd7c983eSGuo Ren struct ftrace_modify_param { 209dd7c983eSGuo Ren int command; 210dd7c983eSGuo Ren atomic_t cpu_count; 211dd7c983eSGuo Ren }; 212dd7c983eSGuo Ren 213dd7c983eSGuo Ren static int __ftrace_modify_code(void *data) 214dd7c983eSGuo Ren { 215dd7c983eSGuo Ren struct ftrace_modify_param *param = data; 216dd7c983eSGuo Ren 217dd7c983eSGuo Ren if (atomic_inc_return(¶m->cpu_count) == 1) { 218dd7c983eSGuo Ren ftrace_modify_all_code(param->command); 219dd7c983eSGuo Ren atomic_inc(¶m->cpu_count); 220dd7c983eSGuo Ren } else { 221dd7c983eSGuo Ren while (atomic_read(¶m->cpu_count) <= num_online_cpus()) 222dd7c983eSGuo Ren cpu_relax(); 223dd7c983eSGuo Ren local_icache_inv_all(NULL); 224dd7c983eSGuo Ren } 225dd7c983eSGuo Ren 226dd7c983eSGuo Ren return 0; 227dd7c983eSGuo Ren } 228dd7c983eSGuo Ren 229dd7c983eSGuo Ren void arch_ftrace_update_code(int command) 230dd7c983eSGuo Ren { 231dd7c983eSGuo Ren struct ftrace_modify_param param = { command, ATOMIC_INIT(0) }; 232dd7c983eSGuo Ren 233dd7c983eSGuo Ren stop_machine(__ftrace_modify_code, ¶m, cpu_online_mask); 234dd7c983eSGuo Ren } 235dd7c983eSGuo Ren #endif 236a13d5887SGuo Ren #endif /* CONFIG_DYNAMIC_FTRACE */ 237dd7c983eSGuo Ren 238230c77a5SGuo Ren /* _mcount is defined in abi's mcount.S */ 239230c77a5SGuo Ren EXPORT_SYMBOL(_mcount); 240