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(¤t->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