xref: /linux/arch/csky/kernel/ftrace.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
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(&current->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(&param->cpu_count) == 1) {
218dd7c983eSGuo Ren 		ftrace_modify_all_code(param->command);
219dd7c983eSGuo Ren 		atomic_inc(&param->cpu_count);
220dd7c983eSGuo Ren 	} else {
221dd7c983eSGuo Ren 		while (atomic_read(&param->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, &param, 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