xref: /openbmc/linux/arch/arm/kernel/stacktrace.c (revision 5854e4d8530e6ed4c2532a71a6b0474e199d44dd)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ecea4ab6SPaul Gortmaker #include <linux/export.h>
3fed240d9SMasami Hiramatsu #include <linux/kprobes.h>
4f16fb1ecSRussell King #include <linux/sched.h>
5b17b0153SIngo Molnar #include <linux/sched/debug.h>
6f16fb1ecSRussell King #include <linux/stacktrace.h>
7f16fb1ecSRussell King 
8c6089061SRussell King #include <asm/sections.h>
92d7c11bfSCatalin Marinas #include <asm/stacktrace.h>
1007b40341SRussell King #include <asm/traps.h>
11f16fb1ecSRussell King 
12*5854e4d8SLi Huafei #include "reboot.h"
13*5854e4d8SLi Huafei 
142d7c11bfSCatalin Marinas #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
152d7c11bfSCatalin Marinas /*
162d7c11bfSCatalin Marinas  * Unwind the current stack frame and store the new register values in the
172d7c11bfSCatalin Marinas  * structure passed as argument. Unwinding is equivalent to a function return,
182d7c11bfSCatalin Marinas  * hence the new PC value rather than LR should be used for backtrace.
192d7c11bfSCatalin Marinas  *
202d7c11bfSCatalin Marinas  * With framepointer enabled, a simple function prologue looks like this:
212d7c11bfSCatalin Marinas  *	mov	ip, sp
222d7c11bfSCatalin Marinas  *	stmdb	sp!, {fp, ip, lr, pc}
232d7c11bfSCatalin Marinas  *	sub	fp, ip, #4
242d7c11bfSCatalin Marinas  *
252d7c11bfSCatalin Marinas  * A simple function epilogue looks like this:
262d7c11bfSCatalin Marinas  *	ldm	sp, {fp, sp, pc}
272d7c11bfSCatalin Marinas  *
28b4d5ec9bSNathan Huckleberry  * When compiled with clang, pc and sp are not pushed. A simple function
29b4d5ec9bSNathan Huckleberry  * prologue looks like this when built with clang:
30b4d5ec9bSNathan Huckleberry  *
31b4d5ec9bSNathan Huckleberry  *	stmdb	{..., fp, lr}
32b4d5ec9bSNathan Huckleberry  *	add	fp, sp, #x
33b4d5ec9bSNathan Huckleberry  *	sub	sp, sp, #y
34b4d5ec9bSNathan Huckleberry  *
35b4d5ec9bSNathan Huckleberry  * A simple function epilogue looks like this when built with clang:
36b4d5ec9bSNathan Huckleberry  *
37b4d5ec9bSNathan Huckleberry  *	sub	sp, fp, #x
38b4d5ec9bSNathan Huckleberry  *	ldm	{..., fp, pc}
39b4d5ec9bSNathan Huckleberry  *
40b4d5ec9bSNathan Huckleberry  *
412d7c11bfSCatalin Marinas  * Note that with framepointer enabled, even the leaf functions have the same
422d7c11bfSCatalin Marinas  * prologue and epilogue, therefore we can ignore the LR value in this case.
432d7c11bfSCatalin Marinas  */
44*5854e4d8SLi Huafei 
45*5854e4d8SLi Huafei extern unsigned long call_with_stack_end;
46*5854e4d8SLi Huafei 
47*5854e4d8SLi Huafei static int frame_pointer_check(struct stackframe *frame)
482d7c11bfSCatalin Marinas {
492d7c11bfSCatalin Marinas 	unsigned long high, low;
502d7c11bfSCatalin Marinas 	unsigned long fp = frame->fp;
51*5854e4d8SLi Huafei 	unsigned long pc = frame->pc;
52*5854e4d8SLi Huafei 
53*5854e4d8SLi Huafei 	/*
54*5854e4d8SLi Huafei 	 * call_with_stack() is the only place we allow SP to jump from one
55*5854e4d8SLi Huafei 	 * stack to another, with FP and SP pointing to different stacks,
56*5854e4d8SLi Huafei 	 * skipping the FP boundary check at this point.
57*5854e4d8SLi Huafei 	 */
58*5854e4d8SLi Huafei 	if (pc >= (unsigned long)&call_with_stack &&
59*5854e4d8SLi Huafei 			pc < (unsigned long)&call_with_stack_end)
60*5854e4d8SLi Huafei 		return 0;
612d7c11bfSCatalin Marinas 
622d7c11bfSCatalin Marinas 	/* only go to a higher address on the stack */
632d7c11bfSCatalin Marinas 	low = frame->sp;
64d33aadbfSWill Deacon 	high = ALIGN(low, THREAD_SIZE);
652d7c11bfSCatalin Marinas 
66b4d5ec9bSNathan Huckleberry 	/* check current frame pointer is within bounds */
67*5854e4d8SLi Huafei #ifdef CONFIG_CC_IS_CLANG
68b4d5ec9bSNathan Huckleberry 	if (fp < low + 4 || fp > high - 4)
69b4d5ec9bSNathan Huckleberry 		return -EINVAL;
70*5854e4d8SLi Huafei #else
71*5854e4d8SLi Huafei 	if (fp < low + 12 || fp > high - 4)
72*5854e4d8SLi Huafei 		return -EINVAL;
73*5854e4d8SLi Huafei #endif
74b4d5ec9bSNathan Huckleberry 
75*5854e4d8SLi Huafei 	return 0;
76*5854e4d8SLi Huafei }
77*5854e4d8SLi Huafei 
78*5854e4d8SLi Huafei int notrace unwind_frame(struct stackframe *frame)
79*5854e4d8SLi Huafei {
80*5854e4d8SLi Huafei 	unsigned long fp = frame->fp;
81*5854e4d8SLi Huafei 
82*5854e4d8SLi Huafei 	if (frame_pointer_check(frame))
83*5854e4d8SLi Huafei 		return -EINVAL;
84*5854e4d8SLi Huafei 
85*5854e4d8SLi Huafei 	/* restore the registers from the stack frame */
86*5854e4d8SLi Huafei #ifdef CONFIG_CC_IS_CLANG
87b4d5ec9bSNathan Huckleberry 	frame->sp = frame->fp;
889be4c88bSlinyujun 	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
899be4c88bSlinyujun 	frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 4));
90b4d5ec9bSNathan Huckleberry #else
919be4c88bSlinyujun 	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 12));
929be4c88bSlinyujun 	frame->sp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 8));
939be4c88bSlinyujun 	frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 4));
94b4d5ec9bSNathan Huckleberry #endif
95fed240d9SMasami Hiramatsu #ifdef CONFIG_KRETPROBES
96fed240d9SMasami Hiramatsu 	if (is_kretprobe_trampoline(frame->pc))
97fed240d9SMasami Hiramatsu 		frame->pc = kretprobe_find_ret_addr(frame->tsk,
98fed240d9SMasami Hiramatsu 					(void *)frame->fp, &frame->kr_cur);
99fed240d9SMasami Hiramatsu #endif
1002d7c11bfSCatalin Marinas 
1012d7c11bfSCatalin Marinas 	return 0;
1022d7c11bfSCatalin Marinas }
1032d7c11bfSCatalin Marinas #endif
1042d7c11bfSCatalin Marinas 
1054bf1fa5aSUwe Kleine-König void notrace walk_stackframe(struct stackframe *frame,
106f16fb1ecSRussell King 		     int (*fn)(struct stackframe *, void *), void *data)
107f16fb1ecSRussell King {
1082d7c11bfSCatalin Marinas 	while (1) {
1092d7c11bfSCatalin Marinas 		int ret;
110f16fb1ecSRussell King 
111f16fb1ecSRussell King 		if (fn(frame, data))
112f16fb1ecSRussell King 			break;
1132d7c11bfSCatalin Marinas 		ret = unwind_frame(frame);
1142d7c11bfSCatalin Marinas 		if (ret < 0)
1152d7c11bfSCatalin Marinas 			break;
1162d7c11bfSCatalin Marinas 	}
117f16fb1ecSRussell King }
1187b104bcbSAl Viro EXPORT_SYMBOL(walk_stackframe);
119f16fb1ecSRussell King 
120f16fb1ecSRussell King #ifdef CONFIG_STACKTRACE
121f16fb1ecSRussell King struct stack_trace_data {
122f16fb1ecSRussell King 	struct stack_trace *trace;
123f76e9154SNicolas Pitre 	unsigned int no_sched_functions;
124f16fb1ecSRussell King 	unsigned int skip;
125f16fb1ecSRussell King };
126f16fb1ecSRussell King 
127f16fb1ecSRussell King static int save_trace(struct stackframe *frame, void *d)
128f16fb1ecSRussell King {
129f16fb1ecSRussell King 	struct stack_trace_data *data = d;
130f16fb1ecSRussell King 	struct stack_trace *trace = data->trace;
13107b40341SRussell King 	struct pt_regs *regs;
1322d7c11bfSCatalin Marinas 	unsigned long addr = frame->pc;
133f16fb1ecSRussell King 
134f76e9154SNicolas Pitre 	if (data->no_sched_functions && in_sched_functions(addr))
135f76e9154SNicolas Pitre 		return 0;
136f16fb1ecSRussell King 	if (data->skip) {
137f16fb1ecSRussell King 		data->skip--;
138f16fb1ecSRussell King 		return 0;
139f16fb1ecSRussell King 	}
140f16fb1ecSRussell King 
141f76e9154SNicolas Pitre 	trace->entries[trace->nr_entries++] = addr;
142f16fb1ecSRussell King 
14307b40341SRussell King 	if (trace->nr_entries >= trace->max_entries)
14407b40341SRussell King 		return 1;
14507b40341SRussell King 
146c6089061SRussell King 	if (!in_entry_text(frame->pc))
14707b40341SRussell King 		return 0;
14807b40341SRussell King 
14907b40341SRussell King 	regs = (struct pt_regs *)frame->sp;
15040ff1ddbSVincent Whitchurch 	if ((unsigned long)&regs[1] > ALIGN(frame->sp, THREAD_SIZE))
15140ff1ddbSVincent Whitchurch 		return 0;
15207b40341SRussell King 
15307b40341SRussell King 	trace->entries[trace->nr_entries++] = regs->ARM_pc;
15407b40341SRussell King 
155f16fb1ecSRussell King 	return trace->nr_entries >= trace->max_entries;
156f16fb1ecSRussell King }
157f16fb1ecSRussell King 
1583683f44cSRussell King /* This must be noinline to so that our skip calculation works correctly */
1593683f44cSRussell King static noinline void __save_stack_trace(struct task_struct *tsk,
1603683f44cSRussell King 	struct stack_trace *trace, unsigned int nosched)
161f16fb1ecSRussell King {
162f16fb1ecSRussell King 	struct stack_trace_data data;
1632d7c11bfSCatalin Marinas 	struct stackframe frame;
164f16fb1ecSRussell King 
165f16fb1ecSRussell King 	data.trace = trace;
166f16fb1ecSRussell King 	data.skip = trace->skip;
1673683f44cSRussell King 	data.no_sched_functions = nosched;
168f76e9154SNicolas Pitre 
169f76e9154SNicolas Pitre 	if (tsk != current) {
170f76e9154SNicolas Pitre #ifdef CONFIG_SMP
171f76e9154SNicolas Pitre 		/*
172d5996b2fSRussell King 		 * What guarantees do we have here that 'tsk' is not
173d5996b2fSRussell King 		 * running on another CPU?  For now, ignore it as we
174d5996b2fSRussell King 		 * can't guarantee we won't explode.
175f76e9154SNicolas Pitre 		 */
176d5996b2fSRussell King 		return;
177f76e9154SNicolas Pitre #else
1782d7c11bfSCatalin Marinas 		frame.fp = thread_saved_fp(tsk);
1792d7c11bfSCatalin Marinas 		frame.sp = thread_saved_sp(tsk);
1802d7c11bfSCatalin Marinas 		frame.lr = 0;		/* recovered from the stack */
1812d7c11bfSCatalin Marinas 		frame.pc = thread_saved_pc(tsk);
182f76e9154SNicolas Pitre #endif
183f76e9154SNicolas Pitre 	} else {
1843683f44cSRussell King 		/* We don't want this function nor the caller */
1853683f44cSRussell King 		data.skip += 2;
1862d7c11bfSCatalin Marinas 		frame.fp = (unsigned long)__builtin_frame_address(0);
18774dbeee0SBehan Webster 		frame.sp = current_stack_pointer;
1882d7c11bfSCatalin Marinas 		frame.lr = (unsigned long)__builtin_return_address(0);
189c46c2c9bSRussell King (Oracle) here:
190c46c2c9bSRussell King (Oracle) 		frame.pc = (unsigned long)&&here;
191f76e9154SNicolas Pitre 	}
192fed240d9SMasami Hiramatsu #ifdef CONFIG_KRETPROBES
193fed240d9SMasami Hiramatsu 	frame.kr_cur = NULL;
194fed240d9SMasami Hiramatsu 	frame.tsk = tsk;
195fed240d9SMasami Hiramatsu #endif
196f16fb1ecSRussell King 
1972d7c11bfSCatalin Marinas 	walk_stackframe(&frame, save_trace, &data);
198f76e9154SNicolas Pitre }
199f76e9154SNicolas Pitre 
2009c986661SLin Yongting void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
2019c986661SLin Yongting {
2029c986661SLin Yongting 	struct stack_trace_data data;
2039c986661SLin Yongting 	struct stackframe frame;
2049c986661SLin Yongting 
2059c986661SLin Yongting 	data.trace = trace;
2069c986661SLin Yongting 	data.skip = trace->skip;
2079c986661SLin Yongting 	data.no_sched_functions = 0;
2089c986661SLin Yongting 
2099c986661SLin Yongting 	frame.fp = regs->ARM_fp;
2109c986661SLin Yongting 	frame.sp = regs->ARM_sp;
2119c986661SLin Yongting 	frame.lr = regs->ARM_lr;
2129c986661SLin Yongting 	frame.pc = regs->ARM_pc;
213fed240d9SMasami Hiramatsu #ifdef CONFIG_KRETPROBES
214fed240d9SMasami Hiramatsu 	frame.kr_cur = NULL;
215fed240d9SMasami Hiramatsu 	frame.tsk = current;
216fed240d9SMasami Hiramatsu #endif
2179c986661SLin Yongting 
2189c986661SLin Yongting 	walk_stackframe(&frame, save_trace, &data);
2199c986661SLin Yongting }
2209c986661SLin Yongting 
2213683f44cSRussell King void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
2223683f44cSRussell King {
2233683f44cSRussell King 	__save_stack_trace(tsk, trace, 1);
2243683f44cSRussell King }
2259a3dc318SDustin Brown EXPORT_SYMBOL(save_stack_trace_tsk);
2263683f44cSRussell King 
227f76e9154SNicolas Pitre void save_stack_trace(struct stack_trace *trace)
228f76e9154SNicolas Pitre {
2293683f44cSRussell King 	__save_stack_trace(current, trace, 0);
230f16fb1ecSRussell King }
2317b4c9505SIngo Molnar EXPORT_SYMBOL_GPL(save_stack_trace);
232f16fb1ecSRussell King #endif
233