1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/ftrace.h>
4 #include <linux/uaccess.h>
5 #include <asm/cacheflush.h>
7 #ifndef CONFIG_DYNAMIC_FTRACE
8 extern void (*ftrace_trace_function
)(unsigned long, unsigned long,
9 struct ftrace_ops
*, struct pt_regs
*);
10 extern int ftrace_graph_entry_stub(struct ftrace_graph_ent
*trace
);
11 extern void ftrace_graph_caller(void);
13 noinline
void __naked
ftrace_stub(unsigned long ip
, unsigned long parent_ip
,
14 struct ftrace_ops
*op
, struct pt_regs
*regs
)
16 __asm__ (""); /* avoid to optimize as pure function */
19 noinline
void _mcount(unsigned long parent_ip
)
21 /* save all state by the compiler prologue */
23 unsigned long ip
= (unsigned long)__builtin_return_address(0);
25 if (ftrace_trace_function
!= ftrace_stub
)
26 ftrace_trace_function(ip
- MCOUNT_INSN_SIZE
, parent_ip
,
29 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
30 if (ftrace_graph_return
!= (trace_func_graph_ret_t
)ftrace_stub
31 || ftrace_graph_entry
!= ftrace_graph_entry_stub
)
32 ftrace_graph_caller();
35 /* restore all state by the compiler epilogue */
37 EXPORT_SYMBOL(_mcount
);
39 #else /* CONFIG_DYNAMIC_FTRACE */
41 noinline
void __naked
ftrace_stub(unsigned long ip
, unsigned long parent_ip
,
42 struct ftrace_ops
*op
, struct pt_regs
*regs
)
44 __asm__ (""); /* avoid to optimize as pure function */
47 noinline
void __naked
_mcount(unsigned long parent_ip
)
49 __asm__ (""); /* avoid to optimize as pure function */
51 EXPORT_SYMBOL(_mcount
);
53 #define XSTR(s) STR(s)
55 void _ftrace_caller(unsigned long parent_ip
)
57 /* save all state needed by the compiler prologue */
60 * prepare arguments for real tracing function
61 * first arg : __builtin_return_address(0) - MCOUNT_INSN_SIZE
62 * second arg : parent_ip
64 __asm__
__volatile__ (
66 "addi $r0, %1, #-" XSTR(MCOUNT_INSN_SIZE
) "\n\t"
68 : "r" (parent_ip
), "r" (__builtin_return_address(0)));
70 /* a placeholder for the call to a real tracing function */
71 __asm__
__volatile__ (
77 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
78 /* a placeholder for the call to ftrace_graph_caller */
79 __asm__
__volatile__ (
80 "ftrace_graph_call: \n\t"
85 /* restore all state needed by the compiler epilogue */
88 int __init
ftrace_dyn_arch_init(void)
93 int ftrace_arch_code_modify_prepare(void)
95 set_all_modules_text_rw();
99 int ftrace_arch_code_modify_post_process(void)
101 set_all_modules_text_ro();
105 static unsigned long gen_sethi_insn(unsigned long addr
)
107 unsigned long opcode
= 0x46000000;
108 unsigned long imm
= addr
>> 12;
109 unsigned long rt_num
= 0xf << 20;
111 return ENDIAN_CONVERT(opcode
| rt_num
| imm
);
114 static unsigned long gen_ori_insn(unsigned long addr
)
116 unsigned long opcode
= 0x58000000;
117 unsigned long imm
= addr
& 0x0000fff;
118 unsigned long rt_num
= 0xf << 20;
119 unsigned long ra_num
= 0xf << 15;
121 return ENDIAN_CONVERT(opcode
| rt_num
| ra_num
| imm
);
124 static unsigned long gen_jral_insn(unsigned long addr
)
126 unsigned long opcode
= 0x4a000001;
127 unsigned long rt_num
= 0x1e << 20;
128 unsigned long rb_num
= 0xf << 10;
130 return ENDIAN_CONVERT(opcode
| rt_num
| rb_num
);
133 static void ftrace_gen_call_insn(unsigned long *call_insns
,
136 call_insns
[0] = gen_sethi_insn(addr
); /* sethi $r15, imm20u */
137 call_insns
[1] = gen_ori_insn(addr
); /* ori $r15, $r15, imm15u */
138 call_insns
[2] = gen_jral_insn(addr
); /* jral $lp, $r15 */
141 static int __ftrace_modify_code(unsigned long pc
, unsigned long *old_insn
,
142 unsigned long *new_insn
, bool validate
)
144 unsigned long orig_insn
[3];
147 if (probe_kernel_read(orig_insn
, (void *)pc
, MCOUNT_INSN_SIZE
))
149 if (memcmp(orig_insn
, old_insn
, MCOUNT_INSN_SIZE
))
153 if (probe_kernel_write((void *)pc
, new_insn
, MCOUNT_INSN_SIZE
))
159 static int ftrace_modify_code(unsigned long pc
, unsigned long *old_insn
,
160 unsigned long *new_insn
, bool validate
)
164 ret
= __ftrace_modify_code(pc
, old_insn
, new_insn
, validate
);
168 flush_icache_range(pc
, pc
+ MCOUNT_INSN_SIZE
);
173 int ftrace_update_ftrace_func(ftrace_func_t func
)
175 unsigned long pc
= (unsigned long)&ftrace_call
;
176 unsigned long old_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
177 unsigned long new_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
179 if (func
!= ftrace_stub
)
180 ftrace_gen_call_insn(new_insn
, (unsigned long)func
);
182 return ftrace_modify_code(pc
, old_insn
, new_insn
, false);
185 int ftrace_make_call(struct dyn_ftrace
*rec
, unsigned long addr
)
187 unsigned long pc
= rec
->ip
;
188 unsigned long nop_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
189 unsigned long call_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
191 ftrace_gen_call_insn(call_insn
, addr
);
193 return ftrace_modify_code(pc
, nop_insn
, call_insn
, true);
196 int ftrace_make_nop(struct module
*mod
, struct dyn_ftrace
*rec
,
199 unsigned long pc
= rec
->ip
;
200 unsigned long nop_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
201 unsigned long call_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
203 ftrace_gen_call_insn(call_insn
, addr
);
205 return ftrace_modify_code(pc
, call_insn
, nop_insn
, true);
207 #endif /* CONFIG_DYNAMIC_FTRACE */
209 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
210 void prepare_ftrace_return(unsigned long *parent
, unsigned long self_addr
,
211 unsigned long frame_pointer
)
213 unsigned long return_hooker
= (unsigned long)&return_to_handler
;
216 if (unlikely(atomic_read(¤t
->tracing_graph_pause
)))
221 if (!function_graph_enter(old
, self_addr
, frame_pointer
, NULL
))
222 *parent
= return_hooker
;
225 noinline
void ftrace_graph_caller(void)
227 unsigned long *parent_ip
=
228 (unsigned long *)(__builtin_frame_address(2) - 4);
230 unsigned long selfpc
=
231 (unsigned long)(__builtin_return_address(1) - MCOUNT_INSN_SIZE
);
233 unsigned long frame_pointer
=
234 (unsigned long)__builtin_frame_address(3);
236 prepare_ftrace_return(parent_ip
, selfpc
, frame_pointer
);
239 extern unsigned long ftrace_return_to_handler(unsigned long frame_pointer
);
240 void __naked
return_to_handler(void)
242 __asm__
__volatile__ (
243 /* save state needed by the ABI */
244 "smw.adm $r0,[$sp],$r1,#0x0 \n\t"
246 /* get original return address */
248 "bal ftrace_return_to_handler\n\t"
251 /* restore state nedded by the ABI */
252 "lmw.bim $r0,[$sp],$r1,#0x0 \n\t");
255 #ifdef CONFIG_DYNAMIC_FTRACE
256 extern unsigned long ftrace_graph_call
;
258 static int ftrace_modify_graph_caller(bool enable
)
260 unsigned long pc
= (unsigned long)&ftrace_graph_call
;
261 unsigned long nop_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
262 unsigned long call_insn
[3] = {INSN_NOP
, INSN_NOP
, INSN_NOP
};
264 ftrace_gen_call_insn(call_insn
, (unsigned long)ftrace_graph_caller
);
267 return ftrace_modify_code(pc
, nop_insn
, call_insn
, true);
269 return ftrace_modify_code(pc
, call_insn
, nop_insn
, true);
272 int ftrace_enable_ftrace_graph_caller(void)
274 return ftrace_modify_graph_caller(true);
277 int ftrace_disable_ftrace_graph_caller(void)
279 return ftrace_modify_graph_caller(false);
281 #endif /* CONFIG_DYNAMIC_FTRACE */
283 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
286 #ifdef CONFIG_TRACE_IRQFLAGS
287 noinline
void __trace_hardirqs_off(void)
289 trace_hardirqs_off();
291 noinline
void __trace_hardirqs_on(void)
295 #endif /* CONFIG_TRACE_IRQFLAGS */