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
17void 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
50struct stack_trace_data {
51	struct stack_trace *trace;
52	unsigned skip;
53};
54
55static 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
71void 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}
79EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
80
81void save_stack_trace(struct stack_trace *trace)
82{
83	save_stack_trace_tsk(current, trace);
84}
85EXPORT_SYMBOL_GPL(save_stack_trace);
86
87#endif
88
89#ifdef CONFIG_FRAME_POINTER
90
91struct return_addr_data {
92	unsigned long addr;
93	unsigned skip;
94};
95
96static 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
110unsigned 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}
118EXPORT_SYMBOL(return_address);
119
120#endif
121