xref: /openbmc/linux/arch/loongarch/kernel/ftrace.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1*dbe3ba30SQing Zhang // SPDX-License-Identifier: GPL-2.0
2*dbe3ba30SQing Zhang /*
3*dbe3ba30SQing Zhang  * Copyright (C) 2022 Loongson Technology Corporation Limited
4*dbe3ba30SQing Zhang  */
5*dbe3ba30SQing Zhang 
6*dbe3ba30SQing Zhang #include <linux/init.h>
7*dbe3ba30SQing Zhang #include <linux/ftrace.h>
8*dbe3ba30SQing Zhang #include <linux/syscalls.h>
9*dbe3ba30SQing Zhang #include <linux/uaccess.h>
10*dbe3ba30SQing Zhang 
11*dbe3ba30SQing Zhang #include <asm/asm.h>
12*dbe3ba30SQing Zhang #include <asm/asm-offsets.h>
13*dbe3ba30SQing Zhang #include <asm/cacheflush.h>
14*dbe3ba30SQing Zhang #include <asm/inst.h>
15*dbe3ba30SQing Zhang #include <asm/loongarch.h>
16*dbe3ba30SQing Zhang #include <asm/syscall.h>
17*dbe3ba30SQing Zhang 
18*dbe3ba30SQing Zhang #include <asm-generic/sections.h>
19*dbe3ba30SQing Zhang 
20*dbe3ba30SQing Zhang #ifdef CONFIG_FUNCTION_GRAPH_TRACER
21*dbe3ba30SQing Zhang 
22*dbe3ba30SQing Zhang /*
23*dbe3ba30SQing Zhang  * As `call _mcount` follows LoongArch psABI, ra-saved operation and
24*dbe3ba30SQing Zhang  * stack operation can be found before this insn.
25*dbe3ba30SQing Zhang  */
26*dbe3ba30SQing Zhang 
ftrace_get_parent_ra_addr(unsigned long insn_addr,int * ra_off)27*dbe3ba30SQing Zhang static int ftrace_get_parent_ra_addr(unsigned long insn_addr, int *ra_off)
28*dbe3ba30SQing Zhang {
29*dbe3ba30SQing Zhang 	int limit = 32;
30*dbe3ba30SQing Zhang 	union loongarch_instruction *insn;
31*dbe3ba30SQing Zhang 
32*dbe3ba30SQing Zhang 	insn = (union loongarch_instruction *)insn_addr;
33*dbe3ba30SQing Zhang 
34*dbe3ba30SQing Zhang 	do {
35*dbe3ba30SQing Zhang 		insn--;
36*dbe3ba30SQing Zhang 		limit--;
37*dbe3ba30SQing Zhang 
38*dbe3ba30SQing Zhang 		if (is_ra_save_ins(insn))
39*dbe3ba30SQing Zhang 			*ra_off = -((1 << 12) - insn->reg2i12_format.immediate);
40*dbe3ba30SQing Zhang 
41*dbe3ba30SQing Zhang 	} while (!is_stack_alloc_ins(insn) && limit);
42*dbe3ba30SQing Zhang 
43*dbe3ba30SQing Zhang 	if (!limit)
44*dbe3ba30SQing Zhang 		return -EINVAL;
45*dbe3ba30SQing Zhang 
46*dbe3ba30SQing Zhang 	return 0;
47*dbe3ba30SQing Zhang }
48*dbe3ba30SQing Zhang 
prepare_ftrace_return(unsigned long self_addr,unsigned long callsite_sp,unsigned long old)49*dbe3ba30SQing Zhang void prepare_ftrace_return(unsigned long self_addr,
50*dbe3ba30SQing Zhang 		unsigned long callsite_sp, unsigned long old)
51*dbe3ba30SQing Zhang {
52*dbe3ba30SQing Zhang 	int ra_off;
53*dbe3ba30SQing Zhang 	unsigned long return_hooker = (unsigned long)&return_to_handler;
54*dbe3ba30SQing Zhang 
55*dbe3ba30SQing Zhang 	if (unlikely(ftrace_graph_is_dead()))
56*dbe3ba30SQing Zhang 		return;
57*dbe3ba30SQing Zhang 
58*dbe3ba30SQing Zhang 	if (unlikely(atomic_read(&current->tracing_graph_pause)))
59*dbe3ba30SQing Zhang 		return;
60*dbe3ba30SQing Zhang 
61*dbe3ba30SQing Zhang 	if (ftrace_get_parent_ra_addr(self_addr, &ra_off))
62*dbe3ba30SQing Zhang 		goto out;
63*dbe3ba30SQing Zhang 
64*dbe3ba30SQing Zhang 	if (!function_graph_enter(old, self_addr, 0, NULL))
65*dbe3ba30SQing Zhang 		*(unsigned long *)(callsite_sp + ra_off) = return_hooker;
66*dbe3ba30SQing Zhang 
67*dbe3ba30SQing Zhang 	return;
68*dbe3ba30SQing Zhang 
69*dbe3ba30SQing Zhang out:
70*dbe3ba30SQing Zhang 	ftrace_graph_stop();
71*dbe3ba30SQing Zhang 	WARN_ON(1);
72*dbe3ba30SQing Zhang }
73*dbe3ba30SQing Zhang #endif	/* CONFIG_FUNCTION_GRAPH_TRACER */
74