xref: /openbmc/linux/arch/arm/kernel/stacktrace.c (revision ecea4ab6d3d8bb4122522398200f1cd2a06af6d5)
1*ecea4ab6SPaul Gortmaker #include <linux/export.h>
2f16fb1ecSRussell King #include <linux/sched.h>
3f16fb1ecSRussell King #include <linux/stacktrace.h>
4f16fb1ecSRussell King 
52d7c11bfSCatalin Marinas #include <asm/stacktrace.h>
6f16fb1ecSRussell King 
72d7c11bfSCatalin Marinas #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
82d7c11bfSCatalin Marinas /*
92d7c11bfSCatalin Marinas  * Unwind the current stack frame and store the new register values in the
102d7c11bfSCatalin Marinas  * structure passed as argument. Unwinding is equivalent to a function return,
112d7c11bfSCatalin Marinas  * hence the new PC value rather than LR should be used for backtrace.
122d7c11bfSCatalin Marinas  *
132d7c11bfSCatalin Marinas  * With framepointer enabled, a simple function prologue looks like this:
142d7c11bfSCatalin Marinas  *	mov	ip, sp
152d7c11bfSCatalin Marinas  *	stmdb	sp!, {fp, ip, lr, pc}
162d7c11bfSCatalin Marinas  *	sub	fp, ip, #4
172d7c11bfSCatalin Marinas  *
182d7c11bfSCatalin Marinas  * A simple function epilogue looks like this:
192d7c11bfSCatalin Marinas  *	ldm	sp, {fp, sp, pc}
202d7c11bfSCatalin Marinas  *
212d7c11bfSCatalin Marinas  * Note that with framepointer enabled, even the leaf functions have the same
222d7c11bfSCatalin Marinas  * prologue and epilogue, therefore we can ignore the LR value in this case.
232d7c11bfSCatalin Marinas  */
244bf1fa5aSUwe Kleine-König int notrace unwind_frame(struct stackframe *frame)
252d7c11bfSCatalin Marinas {
262d7c11bfSCatalin Marinas 	unsigned long high, low;
272d7c11bfSCatalin Marinas 	unsigned long fp = frame->fp;
282d7c11bfSCatalin Marinas 
292d7c11bfSCatalin Marinas 	/* only go to a higher address on the stack */
302d7c11bfSCatalin Marinas 	low = frame->sp;
31d33aadbfSWill Deacon 	high = ALIGN(low, THREAD_SIZE);
322d7c11bfSCatalin Marinas 
332d7c11bfSCatalin Marinas 	/* check current frame pointer is within bounds */
342d7c11bfSCatalin Marinas 	if (fp < (low + 12) || fp + 4 >= high)
352d7c11bfSCatalin Marinas 		return -EINVAL;
362d7c11bfSCatalin Marinas 
372d7c11bfSCatalin Marinas 	/* restore the registers from the stack frame */
382d7c11bfSCatalin Marinas 	frame->fp = *(unsigned long *)(fp - 12);
392d7c11bfSCatalin Marinas 	frame->sp = *(unsigned long *)(fp - 8);
402d7c11bfSCatalin Marinas 	frame->pc = *(unsigned long *)(fp - 4);
412d7c11bfSCatalin Marinas 
422d7c11bfSCatalin Marinas 	return 0;
432d7c11bfSCatalin Marinas }
442d7c11bfSCatalin Marinas #endif
452d7c11bfSCatalin Marinas 
464bf1fa5aSUwe Kleine-König void notrace walk_stackframe(struct stackframe *frame,
47f16fb1ecSRussell King 		     int (*fn)(struct stackframe *, void *), void *data)
48f16fb1ecSRussell King {
492d7c11bfSCatalin Marinas 	while (1) {
502d7c11bfSCatalin Marinas 		int ret;
51f16fb1ecSRussell King 
52f16fb1ecSRussell King 		if (fn(frame, data))
53f16fb1ecSRussell King 			break;
542d7c11bfSCatalin Marinas 		ret = unwind_frame(frame);
552d7c11bfSCatalin Marinas 		if (ret < 0)
562d7c11bfSCatalin Marinas 			break;
572d7c11bfSCatalin Marinas 	}
58f16fb1ecSRussell King }
597b104bcbSAl Viro EXPORT_SYMBOL(walk_stackframe);
60f16fb1ecSRussell King 
61f16fb1ecSRussell King #ifdef CONFIG_STACKTRACE
62f16fb1ecSRussell King struct stack_trace_data {
63f16fb1ecSRussell King 	struct stack_trace *trace;
64f76e9154SNicolas Pitre 	unsigned int no_sched_functions;
65f16fb1ecSRussell King 	unsigned int skip;
66f16fb1ecSRussell King };
67f16fb1ecSRussell King 
68f16fb1ecSRussell King static int save_trace(struct stackframe *frame, void *d)
69f16fb1ecSRussell King {
70f16fb1ecSRussell King 	struct stack_trace_data *data = d;
71f16fb1ecSRussell King 	struct stack_trace *trace = data->trace;
722d7c11bfSCatalin Marinas 	unsigned long addr = frame->pc;
73f16fb1ecSRussell King 
74f76e9154SNicolas Pitre 	if (data->no_sched_functions && in_sched_functions(addr))
75f76e9154SNicolas Pitre 		return 0;
76f16fb1ecSRussell King 	if (data->skip) {
77f16fb1ecSRussell King 		data->skip--;
78f16fb1ecSRussell King 		return 0;
79f16fb1ecSRussell King 	}
80f16fb1ecSRussell King 
81f76e9154SNicolas Pitre 	trace->entries[trace->nr_entries++] = addr;
82f16fb1ecSRussell King 
83f16fb1ecSRussell King 	return trace->nr_entries >= trace->max_entries;
84f16fb1ecSRussell King }
85f16fb1ecSRussell King 
86f76e9154SNicolas Pitre void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
87f16fb1ecSRussell King {
88f16fb1ecSRussell King 	struct stack_trace_data data;
892d7c11bfSCatalin Marinas 	struct stackframe frame;
90f16fb1ecSRussell King 
91f16fb1ecSRussell King 	data.trace = trace;
92f16fb1ecSRussell King 	data.skip = trace->skip;
93f76e9154SNicolas Pitre 
94f76e9154SNicolas Pitre 	if (tsk != current) {
95f76e9154SNicolas Pitre #ifdef CONFIG_SMP
96f76e9154SNicolas Pitre 		/*
97d5996b2fSRussell King 		 * What guarantees do we have here that 'tsk' is not
98d5996b2fSRussell King 		 * running on another CPU?  For now, ignore it as we
99d5996b2fSRussell King 		 * can't guarantee we won't explode.
100f76e9154SNicolas Pitre 		 */
101d5996b2fSRussell King 		if (trace->nr_entries < trace->max_entries)
102d5996b2fSRussell King 			trace->entries[trace->nr_entries++] = ULONG_MAX;
103d5996b2fSRussell King 		return;
104f76e9154SNicolas Pitre #else
105f76e9154SNicolas Pitre 		data.no_sched_functions = 1;
1062d7c11bfSCatalin Marinas 		frame.fp = thread_saved_fp(tsk);
1072d7c11bfSCatalin Marinas 		frame.sp = thread_saved_sp(tsk);
1082d7c11bfSCatalin Marinas 		frame.lr = 0;		/* recovered from the stack */
1092d7c11bfSCatalin Marinas 		frame.pc = thread_saved_pc(tsk);
110f76e9154SNicolas Pitre #endif
111f76e9154SNicolas Pitre 	} else {
1122d7c11bfSCatalin Marinas 		register unsigned long current_sp asm ("sp");
1132d7c11bfSCatalin Marinas 
114f76e9154SNicolas Pitre 		data.no_sched_functions = 0;
1152d7c11bfSCatalin Marinas 		frame.fp = (unsigned long)__builtin_frame_address(0);
1162d7c11bfSCatalin Marinas 		frame.sp = current_sp;
1172d7c11bfSCatalin Marinas 		frame.lr = (unsigned long)__builtin_return_address(0);
1182d7c11bfSCatalin Marinas 		frame.pc = (unsigned long)save_stack_trace_tsk;
119f76e9154SNicolas Pitre 	}
120f16fb1ecSRussell King 
1212d7c11bfSCatalin Marinas 	walk_stackframe(&frame, save_trace, &data);
122f76e9154SNicolas Pitre 	if (trace->nr_entries < trace->max_entries)
123f76e9154SNicolas Pitre 		trace->entries[trace->nr_entries++] = ULONG_MAX;
124f76e9154SNicolas Pitre }
125f76e9154SNicolas Pitre 
126f76e9154SNicolas Pitre void save_stack_trace(struct stack_trace *trace)
127f76e9154SNicolas Pitre {
128f76e9154SNicolas Pitre 	save_stack_trace_tsk(current, trace);
129f16fb1ecSRussell King }
1307b4c9505SIngo Molnar EXPORT_SYMBOL_GPL(save_stack_trace);
131f16fb1ecSRussell King #endif
132