File Index
Files and directories
Sign in
FILE

ftrace.h

File preview with syntax highlighting.

linux-6.19.14/arch/riscv/include/asm/ftrace.h C
  1/* SPDX-License-Identifier: GPL-2.0 */
  2/* Copyright (C) 2017 Andes Technology Corporation */
  3
  4#ifndef _ASM_RISCV_FTRACE_H
  5#define _ASM_RISCV_FTRACE_H
  6
  7/*
  8 * The graph frame test is not possible if CONFIG_FRAME_POINTER is not enabled.
  9 * Check arch/riscv/kernel/mcount.S for detail.
 10 */
 11#if defined(CONFIG_FUNCTION_GRAPH_TRACER) && defined(CONFIG_FRAME_POINTER)
 12#define HAVE_FUNCTION_GRAPH_FP_TEST
 13#endif
 14
 15#define ARCH_SUPPORTS_FTRACE_OPS 1
 16#ifndef __ASSEMBLER__
 17
 18extern void *return_address(unsigned int level);
 19
 20#define ftrace_return_address(n) return_address(n)
 21
 22void _mcount(void);
 23unsigned long ftrace_call_adjust(unsigned long addr);
 24unsigned long arch_ftrace_get_symaddr(unsigned long fentry_ip);
 25#define ftrace_get_symaddr(fentry_ip) arch_ftrace_get_symaddr(fentry_ip)
 26
 27/*
 28 * Let's do like x86/arm64 and ignore the compat syscalls.
 29 */
 30#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS
 31static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
 32{
 33	return is_compat_task();
 34}
 35
 36#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
 37static inline bool arch_syscall_match_sym_name(const char *sym,
 38					       const char *name)
 39{
 40	/*
 41	 * Since all syscall functions have __riscv_ prefix, we must skip it.
 42	 * However, as we described above, we decided to ignore compat
 43	 * syscalls, so we don't care about __riscv_compat_ prefix here.
 44	 */
 45	return !strcmp(sym + 8, name);
 46}
 47
 48struct dyn_arch_ftrace {
 49};
 50#endif
 51
 52#ifdef CONFIG_DYNAMIC_FTRACE
 53/*
 54 * A general call in RISC-V is a pair of insts:
 55 * 1) auipc: setting high-20 pc-related bits to ra register
 56 * 2) jalr: setting low-12 offset to ra, jump to ra, and set ra to
 57 *          return address (original pc + 4)
 58 *
 59 * The first 2 instructions for each tracable function is compiled to 2 nop
 60 * instructions. Then, the kernel initializes the first instruction to auipc at
 61 * boot time (<ftrace disable>). The second instruction is patched to jalr to
 62 * start the trace.
 63 *
 64 *<Image>:
 65 * 0: nop
 66 * 4: nop
 67 *
 68 *<ftrace enable>:
 69 * 0: auipc  t0, 0x?
 70 * 4: jalr   t0, ?(t0)
 71 *
 72 *<ftrace disable>:
 73 * 0: auipc  t0, 0x?
 74 * 4: nop
 75 *
 76 * Dynamic ftrace generates probes to call sites, so we must deal with
 77 * both auipc and jalr at the same time.
 78 */
 79
 80#define MCOUNT_ADDR		((unsigned long)_mcount)
 81#define JALR_SIGN_MASK		(0x00000800)
 82#define JALR_OFFSET_MASK	(0x00000fff)
 83#define AUIPC_OFFSET_MASK	(0xfffff000)
 84#define AUIPC_PAD		(0x00001000)
 85#define JALR_SHIFT		20
 86#define JALR_T0			(0x000282e7)
 87#define AUIPC_T0		(0x00000297)
 88#define JALR_RANGE		(JALR_SIGN_MASK - 1)
 89
 90#define to_jalr_t0(offset)						\
 91	(((offset & JALR_OFFSET_MASK) << JALR_SHIFT) | JALR_T0)
 92
 93#define to_auipc_t0(offset)						\
 94	((offset & JALR_SIGN_MASK) ?					\
 95	(((offset & AUIPC_OFFSET_MASK) + AUIPC_PAD) | AUIPC_T0) :	\
 96	((offset & AUIPC_OFFSET_MASK) | AUIPC_T0))
 97
 98#define make_call_t0(caller, callee, call)				\
 99do {									\
100	unsigned int offset =						\
101		(unsigned long) (callee) - (unsigned long) (caller);	\
102	call[0] = to_auipc_t0(offset);					\
103	call[1] = to_jalr_t0(offset);					\
104} while (0)
105
106/*
107 * Only the jalr insn in the auipc+jalr is patched, so we make it 4
108 * bytes here.
109 */
110#define MCOUNT_INSN_SIZE	4
111#define MCOUNT_AUIPC_SIZE	4
112#define MCOUNT_JALR_SIZE	4
113#define MCOUNT_NOP4_SIZE	4
114
115#ifndef __ASSEMBLER__
116struct dyn_ftrace;
117int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
118#define ftrace_init_nop ftrace_init_nop
119
120#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
121#define arch_ftrace_get_regs(regs) NULL
122#define HAVE_ARCH_FTRACE_REGS
123struct ftrace_ops;
124struct ftrace_regs;
125#define arch_ftrace_regs(fregs) ((struct __arch_ftrace_regs *)(fregs))
126
127struct __arch_ftrace_regs {
128	unsigned long epc;
129	unsigned long ra;
130	unsigned long sp;
131	unsigned long s0;
132	unsigned long t1;
133#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
134	unsigned long direct_tramp;
135#endif
136	union {
137		unsigned long args[8];
138		struct {
139			unsigned long a0;
140			unsigned long a1;
141			unsigned long a2;
142			unsigned long a3;
143			unsigned long a4;
144			unsigned long a5;
145			unsigned long a6;
146			unsigned long a7;
147#ifdef CONFIG_CC_IS_CLANG
148			unsigned long t2;
149			unsigned long t3;
150			unsigned long t4;
151			unsigned long t5;
152			unsigned long t6;
153#endif
154		};
155	};
156};
157
158static __always_inline unsigned long ftrace_regs_get_instruction_pointer(const struct ftrace_regs
159									 *fregs)
160{
161	return arch_ftrace_regs(fregs)->epc;
162}
163
164static __always_inline void ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
165								unsigned long pc)
166{
167	arch_ftrace_regs(fregs)->epc = pc;
168}
169
170static __always_inline unsigned long ftrace_regs_get_stack_pointer(const struct ftrace_regs *fregs)
171{
172	return arch_ftrace_regs(fregs)->sp;
173}
174
175static __always_inline unsigned long ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
176{
177	return arch_ftrace_regs(fregs)->s0;
178}
179
180static __always_inline unsigned long ftrace_regs_get_argument(struct ftrace_regs *fregs,
181							      unsigned int n)
182{
183	if (n < 8)
184		return arch_ftrace_regs(fregs)->args[n];
185	return 0;
186}
187
188static __always_inline unsigned long ftrace_regs_get_return_value(const struct ftrace_regs *fregs)
189{
190	return arch_ftrace_regs(fregs)->a0;
191}
192
193static __always_inline unsigned long ftrace_regs_get_return_address(const struct ftrace_regs *fregs)
194{
195	return arch_ftrace_regs(fregs)->ra;
196}
197
198static __always_inline void ftrace_regs_set_return_value(struct ftrace_regs *fregs,
199							 unsigned long ret)
200{
201	arch_ftrace_regs(fregs)->a0 = ret;
202}
203
204static __always_inline void ftrace_override_function_with_return(struct ftrace_regs *fregs)
205{
206	arch_ftrace_regs(fregs)->epc = arch_ftrace_regs(fregs)->ra;
207}
208
209static __always_inline struct pt_regs *
210ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
211{
212	struct __arch_ftrace_regs *afregs = arch_ftrace_regs(fregs);
213
214	memcpy(&regs->a_regs, afregs->args, sizeof(afregs->args));
215	regs->epc = afregs->epc;
216	regs->ra = afregs->ra;
217	regs->sp = afregs->sp;
218	regs->s0 = afregs->s0;
219	regs->t1 = afregs->t1;
220	return regs;
221}
222
223int ftrace_regs_query_register_offset(const char *name);
224
225void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
226		       struct ftrace_ops *op, struct ftrace_regs *fregs);
227#define ftrace_graph_func ftrace_graph_func
228
229#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
230static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsigned long addr)
231{
232	arch_ftrace_regs(fregs)->t1 = addr;
233}
234#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
235
236#endif /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
237
238#endif /* __ASSEMBLER__ */
239
240#endif /* CONFIG_DYNAMIC_FTRACE */
241
242#endif /* _ASM_RISCV_FTRACE_H */
~/ Read-only file preview.