xref: /openbmc/linux/arch/arm64/kernel/entry-ftrace.S (revision 819e50e2)
1819e50e2SAKASHI Takahiro/*
2819e50e2SAKASHI Takahiro * arch/arm64/kernel/entry-ftrace.S
3819e50e2SAKASHI Takahiro *
4819e50e2SAKASHI Takahiro * Copyright (C) 2013 Linaro Limited
5819e50e2SAKASHI Takahiro * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
6819e50e2SAKASHI Takahiro *
7819e50e2SAKASHI Takahiro * This program is free software; you can redistribute it and/or modify
8819e50e2SAKASHI Takahiro * it under the terms of the GNU General Public License version 2 as
9819e50e2SAKASHI Takahiro * published by the Free Software Foundation.
10819e50e2SAKASHI Takahiro */
11819e50e2SAKASHI Takahiro
12819e50e2SAKASHI Takahiro#include <linux/linkage.h>
13819e50e2SAKASHI Takahiro#include <asm/ftrace.h>
14819e50e2SAKASHI Takahiro#include <asm/insn.h>
15819e50e2SAKASHI Takahiro
16819e50e2SAKASHI Takahiro/*
17819e50e2SAKASHI Takahiro * Gcc with -pg will put the following code in the beginning of each function:
18819e50e2SAKASHI Takahiro *      mov x0, x30
19819e50e2SAKASHI Takahiro *      bl _mcount
20819e50e2SAKASHI Takahiro *	[function's body ...]
21819e50e2SAKASHI Takahiro * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
22819e50e2SAKASHI Takahiro * ftrace is enabled.
23819e50e2SAKASHI Takahiro *
24819e50e2SAKASHI Takahiro * Please note that x0 as an argument will not be used here because we can
25819e50e2SAKASHI Takahiro * get lr(x30) of instrumented function at any time by winding up call stack
26819e50e2SAKASHI Takahiro * as long as the kernel is compiled without -fomit-frame-pointer.
27819e50e2SAKASHI Takahiro * (or CONFIG_FRAME_POINTER, this is forced on arm64)
28819e50e2SAKASHI Takahiro *
29819e50e2SAKASHI Takahiro * stack layout after mcount_enter in _mcount():
30819e50e2SAKASHI Takahiro *
31819e50e2SAKASHI Takahiro * current sp/fp =>  0:+-----+
32819e50e2SAKASHI Takahiro * in _mcount()        | x29 | -> instrumented function's fp
33819e50e2SAKASHI Takahiro *                     +-----+
34819e50e2SAKASHI Takahiro *                     | x30 | -> _mcount()'s lr (= instrumented function's pc)
35819e50e2SAKASHI Takahiro * old sp       => +16:+-----+
36819e50e2SAKASHI Takahiro * when instrumented   |     |
37819e50e2SAKASHI Takahiro * function calls      | ... |
38819e50e2SAKASHI Takahiro * _mcount()           |     |
39819e50e2SAKASHI Takahiro *                     |     |
40819e50e2SAKASHI Takahiro * instrumented => +xx:+-----+
41819e50e2SAKASHI Takahiro * function's fp       | x29 | -> parent's fp
42819e50e2SAKASHI Takahiro *                     +-----+
43819e50e2SAKASHI Takahiro *                     | x30 | -> instrumented function's lr (= parent's pc)
44819e50e2SAKASHI Takahiro *                     +-----+
45819e50e2SAKASHI Takahiro *                     | ... |
46819e50e2SAKASHI Takahiro */
47819e50e2SAKASHI Takahiro
48819e50e2SAKASHI Takahiro	.macro mcount_enter
49819e50e2SAKASHI Takahiro	stp	x29, x30, [sp, #-16]!
50819e50e2SAKASHI Takahiro	mov	x29, sp
51819e50e2SAKASHI Takahiro	.endm
52819e50e2SAKASHI Takahiro
53819e50e2SAKASHI Takahiro	.macro mcount_exit
54819e50e2SAKASHI Takahiro	ldp	x29, x30, [sp], #16
55819e50e2SAKASHI Takahiro	ret
56819e50e2SAKASHI Takahiro	.endm
57819e50e2SAKASHI Takahiro
58819e50e2SAKASHI Takahiro	.macro mcount_adjust_addr rd, rn
59819e50e2SAKASHI Takahiro	sub	\rd, \rn, #AARCH64_INSN_SIZE
60819e50e2SAKASHI Takahiro	.endm
61819e50e2SAKASHI Takahiro
62819e50e2SAKASHI Takahiro	/* for instrumented function's parent */
63819e50e2SAKASHI Takahiro	.macro mcount_get_parent_fp reg
64819e50e2SAKASHI Takahiro	ldr	\reg, [x29]
65819e50e2SAKASHI Takahiro	ldr	\reg, [\reg]
66819e50e2SAKASHI Takahiro	.endm
67819e50e2SAKASHI Takahiro
68819e50e2SAKASHI Takahiro	/* for instrumented function */
69819e50e2SAKASHI Takahiro	.macro mcount_get_pc0 reg
70819e50e2SAKASHI Takahiro	mcount_adjust_addr	\reg, x30
71819e50e2SAKASHI Takahiro	.endm
72819e50e2SAKASHI Takahiro
73819e50e2SAKASHI Takahiro	.macro mcount_get_pc reg
74819e50e2SAKASHI Takahiro	ldr	\reg, [x29, #8]
75819e50e2SAKASHI Takahiro	mcount_adjust_addr	\reg, \reg
76819e50e2SAKASHI Takahiro	.endm
77819e50e2SAKASHI Takahiro
78819e50e2SAKASHI Takahiro	.macro mcount_get_lr reg
79819e50e2SAKASHI Takahiro	ldr	\reg, [x29]
80819e50e2SAKASHI Takahiro	ldr	\reg, [\reg, #8]
81819e50e2SAKASHI Takahiro	mcount_adjust_addr	\reg, \reg
82819e50e2SAKASHI Takahiro	.endm
83819e50e2SAKASHI Takahiro
84819e50e2SAKASHI Takahiro	.macro mcount_get_lr_addr reg
85819e50e2SAKASHI Takahiro	ldr	\reg, [x29]
86819e50e2SAKASHI Takahiro	add	\reg, \reg, #8
87819e50e2SAKASHI Takahiro	.endm
88819e50e2SAKASHI Takahiro
89819e50e2SAKASHI Takahiro/*
90819e50e2SAKASHI Takahiro * void _mcount(unsigned long return_address)
91819e50e2SAKASHI Takahiro * @return_address: return address to instrumented function
92819e50e2SAKASHI Takahiro *
93819e50e2SAKASHI Takahiro * This function makes calls, if enabled, to:
94819e50e2SAKASHI Takahiro *     - tracer function to probe instrumented function's entry,
95819e50e2SAKASHI Takahiro *     - ftrace_graph_caller to set up an exit hook
96819e50e2SAKASHI Takahiro */
97819e50e2SAKASHI TakahiroENTRY(_mcount)
98819e50e2SAKASHI Takahiro#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
99819e50e2SAKASHI Takahiro	ldr	x0, =ftrace_trace_stop
100819e50e2SAKASHI Takahiro	ldr	x0, [x0]		// if ftrace_trace_stop
101819e50e2SAKASHI Takahiro	ret				//   return;
102819e50e2SAKASHI Takahiro#endif
103819e50e2SAKASHI Takahiro	mcount_enter
104819e50e2SAKASHI Takahiro
105819e50e2SAKASHI Takahiro	ldr	x0, =ftrace_trace_function
106819e50e2SAKASHI Takahiro	ldr	x2, [x0]
107819e50e2SAKASHI Takahiro	adr	x0, ftrace_stub
108819e50e2SAKASHI Takahiro	cmp	x0, x2			// if (ftrace_trace_function
109819e50e2SAKASHI Takahiro	b.eq	skip_ftrace_call	//     != ftrace_stub) {
110819e50e2SAKASHI Takahiro
111819e50e2SAKASHI Takahiro	mcount_get_pc	x0		//       function's pc
112819e50e2SAKASHI Takahiro	mcount_get_lr	x1		//       function's lr (= parent's pc)
113819e50e2SAKASHI Takahiro	blr	x2			//   (*ftrace_trace_function)(pc, lr);
114819e50e2SAKASHI Takahiro
115819e50e2SAKASHI Takahiro#ifndef CONFIG_FUNCTION_GRAPH_TRACER
116819e50e2SAKASHI Takahiroskip_ftrace_call:			//   return;
117819e50e2SAKASHI Takahiro	mcount_exit			// }
118819e50e2SAKASHI Takahiro#else
119819e50e2SAKASHI Takahiro	mcount_exit			//   return;
120819e50e2SAKASHI Takahiro					// }
121819e50e2SAKASHI Takahiroskip_ftrace_call:
122819e50e2SAKASHI Takahiro	ldr	x1, =ftrace_graph_return
123819e50e2SAKASHI Takahiro	ldr	x2, [x1]		//   if ((ftrace_graph_return
124819e50e2SAKASHI Takahiro	cmp	x0, x2			//        != ftrace_stub)
125819e50e2SAKASHI Takahiro	b.ne	ftrace_graph_caller
126819e50e2SAKASHI Takahiro
127819e50e2SAKASHI Takahiro	ldr	x1, =ftrace_graph_entry	//     || (ftrace_graph_entry
128819e50e2SAKASHI Takahiro	ldr	x2, [x1]		//        != ftrace_graph_entry_stub))
129819e50e2SAKASHI Takahiro	ldr	x0, =ftrace_graph_entry_stub
130819e50e2SAKASHI Takahiro	cmp	x0, x2
131819e50e2SAKASHI Takahiro	b.ne	ftrace_graph_caller	//     ftrace_graph_caller();
132819e50e2SAKASHI Takahiro
133819e50e2SAKASHI Takahiro	mcount_exit
134819e50e2SAKASHI Takahiro#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
135819e50e2SAKASHI TakahiroENDPROC(_mcount)
136819e50e2SAKASHI Takahiro
137819e50e2SAKASHI TakahiroENTRY(ftrace_stub)
138819e50e2SAKASHI Takahiro	ret
139819e50e2SAKASHI TakahiroENDPROC(ftrace_stub)
140819e50e2SAKASHI Takahiro
141819e50e2SAKASHI Takahiro#ifdef CONFIG_FUNCTION_GRAPH_TRACER
142819e50e2SAKASHI Takahiro/*
143819e50e2SAKASHI Takahiro * void ftrace_graph_caller(void)
144819e50e2SAKASHI Takahiro *
145819e50e2SAKASHI Takahiro * Called from _mcount() or ftrace_caller() when function_graph tracer is
146819e50e2SAKASHI Takahiro * selected.
147819e50e2SAKASHI Takahiro * This function w/ prepare_ftrace_return() fakes link register's value on
148819e50e2SAKASHI Takahiro * the call stack in order to intercept instrumented function's return path
149819e50e2SAKASHI Takahiro * and run return_to_handler() later on its exit.
150819e50e2SAKASHI Takahiro */
151819e50e2SAKASHI TakahiroENTRY(ftrace_graph_caller)
152819e50e2SAKASHI Takahiro	mcount_get_lr_addr	  x0	//     pointer to function's saved lr
153819e50e2SAKASHI Takahiro	mcount_get_pc		  x1	//     function's pc
154819e50e2SAKASHI Takahiro	mcount_get_parent_fp	  x2	//     parent's fp
155819e50e2SAKASHI Takahiro	bl	prepare_ftrace_return	// prepare_ftrace_return(&lr, pc, fp)
156819e50e2SAKASHI Takahiro
157819e50e2SAKASHI Takahiro	mcount_exit
158819e50e2SAKASHI TakahiroENDPROC(ftrace_graph_caller)
159819e50e2SAKASHI Takahiro
160819e50e2SAKASHI Takahiro/*
161819e50e2SAKASHI Takahiro * void return_to_handler(void)
162819e50e2SAKASHI Takahiro *
163819e50e2SAKASHI Takahiro * Run ftrace_return_to_handler() before going back to parent.
164819e50e2SAKASHI Takahiro * @fp is checked against the value passed by ftrace_graph_caller()
165819e50e2SAKASHI Takahiro * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled.
166819e50e2SAKASHI Takahiro */
167819e50e2SAKASHI TakahiroENTRY(return_to_handler)
168819e50e2SAKASHI Takahiro	str	x0, [sp, #-16]!
169819e50e2SAKASHI Takahiro	mov	x0, x29			//     parent's fp
170819e50e2SAKASHI Takahiro	bl	ftrace_return_to_handler// addr = ftrace_return_to_hander(fp);
171819e50e2SAKASHI Takahiro	mov	x30, x0			// restore the original return address
172819e50e2SAKASHI Takahiro	ldr	x0, [sp], #16
173819e50e2SAKASHI Takahiro	ret
174819e50e2SAKASHI TakahiroEND(return_to_handler)
175819e50e2SAKASHI Takahiro#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
176