xref: /openbmc/linux/arch/xtensa/kernel/stacktrace.c (revision 5a86bf34)
1 /*
2  * arch/xtensa/kernel/stacktrace.c
3  *
4  * This file is subject to the terms and conditions of the GNU General Public
5  * License.  See the file "COPYING" in the main directory of this archive
6  * for more details.
7  *
8  * Copyright (C) 2001 - 2013 Tensilica Inc.
9  */
10 #include <linux/export.h>
11 #include <linux/sched.h>
12 #include <linux/stacktrace.h>
13 
14 #include <asm/stacktrace.h>
15 #include <asm/traps.h>
16 
17 void walk_stackframe(unsigned long *sp,
18 		int (*fn)(struct stackframe *frame, void *data),
19 		void *data)
20 {
21 	unsigned long a0, a1;
22 	unsigned long sp_end;
23 
24 	a1 = (unsigned long)sp;
25 	sp_end = ALIGN(a1, THREAD_SIZE);
26 
27 	spill_registers();
28 
29 	while (a1 < sp_end) {
30 		struct stackframe frame;
31 
32 		sp = (unsigned long *)a1;
33 
34 		a0 = *(sp - 4);
35 		a1 = *(sp - 3);
36 
37 		if (a1 <= (unsigned long)sp)
38 			break;
39 
40 		frame.pc = MAKE_PC_FROM_RA(a0, a1);
41 		frame.sp = a1;
42 
43 		if (fn(&frame, data))
44 			return;
45 	}
46 }
47 
48 #ifdef CONFIG_STACKTRACE
49 
50 struct stack_trace_data {
51 	struct stack_trace *trace;
52 	unsigned skip;
53 };
54 
55 static int stack_trace_cb(struct stackframe *frame, void *data)
56 {
57 	struct stack_trace_data *trace_data = data;
58 	struct stack_trace *trace = trace_data->trace;
59 
60 	if (trace_data->skip) {
61 		--trace_data->skip;
62 		return 0;
63 	}
64 	if (!kernel_text_address(frame->pc))
65 		return 0;
66 
67 	trace->entries[trace->nr_entries++] = frame->pc;
68 	return trace->nr_entries >= trace->max_entries;
69 }
70 
71 void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
72 {
73 	struct stack_trace_data trace_data = {
74 		.trace = trace,
75 		.skip = trace->skip,
76 	};
77 	walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data);
78 }
79 EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
80 
81 void save_stack_trace(struct stack_trace *trace)
82 {
83 	save_stack_trace_tsk(current, trace);
84 }
85 EXPORT_SYMBOL_GPL(save_stack_trace);
86 
87 #endif
88 
89 #ifdef CONFIG_FRAME_POINTER
90 
91 struct return_addr_data {
92 	unsigned long addr;
93 	unsigned skip;
94 };
95 
96 static int return_address_cb(struct stackframe *frame, void *data)
97 {
98 	struct return_addr_data *r = data;
99 
100 	if (r->skip) {
101 		--r->skip;
102 		return 0;
103 	}
104 	if (!kernel_text_address(frame->pc))
105 		return 0;
106 	r->addr = frame->pc;
107 	return 1;
108 }
109 
110 unsigned long return_address(unsigned level)
111 {
112 	struct return_addr_data r = {
113 		.skip = level + 1,
114 	};
115 	walk_stackframe(stack_pointer(NULL), return_address_cb, &r);
116 	return r.addr;
117 }
118 EXPORT_SYMBOL(return_address);
119 
120 #endif
121