2a10e3f4f1494e30b6c0637fbb6dd2d4a7ed8a40
[openwrt/openwrt.git] / target / linux / ubicom32 / files / arch / ubicom32 / kernel / stacktrace.c
1 /*
2 * arch/ubicom32/kernel/stacktrace.c
3 * Ubicom32 architecture stack back trace implementation.
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 *
7 * This file is part of the Ubicom32 Linux Kernel Port.
8 *
9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10 * it and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with the Ubicom32 Linux Kernel Port. If not,
21 * see <http://www.gnu.org/licenses/>.
22 *
23 * Ubicom32 implementation derived from (with many thanks):
24 * arch/m68knommu
25 * arch/blackfin
26 * arch/parisc
27 */
28 #include <linux/sched.h>
29 #include <linux/stacktrace.h>
30 #include <linux/module.h>
31 #include <asm/stacktrace.h>
32 #include <asm/thread.h>
33 #include <asm/ip5000.h>
34
35 /*
36 * These symbols are filled in by the linker.
37 */
38 extern unsigned long _stext;
39 extern unsigned long _etext;
40
41 extern unsigned long __ocm_text_run_begin;
42 extern unsigned long __data_begin;
43
44 /*
45 * stacktrace_iterate()
46 * Walk the stack looking for call and calli instructions on an aligned
47 * boundary.
48 *
49 * Trace must point to the top of the current stack frame.
50 */
51 unsigned long stacktrace_iterate(unsigned long **trace,
52 unsigned long stext,
53 unsigned long etext,
54 unsigned long ocm_stext,
55 unsigned long ocm_etext,
56 unsigned long sstack,
57 unsigned long estack)
58 {
59 unsigned int thread_trap_en, instruction;
60 unsigned long address;
61 unsigned int limit = 0;
62 unsigned long result = 0;
63 unsigned long *sp = *trace;
64
65 /*
66 * Exclude the current thread from being monitored for traps.
67 */
68 asm volatile(
69 " thread_get_self_mask d15 \n\t"
70 /* save current trap status */
71 " and.4 %0, MT_TRAP_EN, d15 \n\t"
72 " not.4 d15, d15 \n\t"
73 /* disable trap */
74 " and.4 MT_TRAP_EN, MT_TRAP_EN, d15 \n\t"
75 " pipe_flush 0 \n\t"
76 : "=r" (thread_trap_en)
77 :
78 : "d15", "cc"
79 );
80
81 while (limit++ < 256) {
82 /*
83 * See if we have a valid stack.
84 */
85 if (!between((unsigned long)sp, sstack, estack)) {
86 #ifdef TRAP_DEBUG_STACK_TRACE
87 printk(KERN_EMERG "stack address is out of range - "
88 "sp: %x, sstack: %x, estack: %x\n",
89 (unsigned int)sp, (unsigned int)sstack,
90 (unsigned int)estack);
91 #endif
92 result = 0;
93 *trace = 0;
94 break;
95 }
96
97 /*
98 * Get the value off the stack and back up 4 bytes to what
99 * should be the address of a call or calli.
100 */
101 address = (*sp++) - 4;
102
103 /*
104 * If the address is not within the text segment, skip this
105 * value.
106 */
107 if (!between(address, stext, etext) &&
108 !between(address, ocm_stext, ocm_etext)) {
109 #ifdef TRAP_DEBUG_STACK_TRACE
110 printk(KERN_EMERG "not a text address - "
111 "address: %08x, stext: %08x, etext: %08x\n"
112 "ocm_stext: %08x, ocm_etext: %08x\n",
113 (unsigned int)address,
114 (unsigned int)stext,
115 (unsigned int)etext,
116 (unsigned int)ocm_stext,
117 (unsigned int)ocm_etext);
118 #endif
119 continue;
120
121 }
122
123 /*
124 * If the address is not on an aligned boundary it can not be a
125 * return address.
126 */
127 if (address & 0x3) {
128 continue;
129 }
130
131 /*
132 * Read the probable instruction.
133 */
134 instruction = *(unsigned int *)address;
135
136 /*
137 * Is this a call instruction?
138 */
139 if ((instruction & 0xF8000000) == (u32_t)(0x1B << 27)) {
140 #ifdef TRAP_DEBUG_STACK_TRACE
141 printk(KERN_EMERG "call inst. result: %x, "
142 "test: %x\n", (unsigned int)address,
143 (unsigned int)instruction);
144 #endif
145 *trace = sp;
146 result = address;
147 break;
148 }
149
150 /*
151 * Is this a calli instruction?
152 */
153 if ((instruction & 0xF8000000) == (u32_t)(0x1E << 27)) {
154 #ifdef TRAP_DEBUG_STACK_TRACE
155 printk(KERN_EMERG "calli inst. result: %x, "
156 "test: %x\n", (unsigned int)address,
157 (unsigned int)instruction);
158 #endif
159 *trace = sp;
160 result = address;
161 break;
162 }
163 }
164
165 /*
166 * Restore the current thread to be monitored for traps.
167 */
168 if (thread_trap_en) {
169 asm volatile(
170 " thread_get_self_mask d15 \n\t"
171 " or.4 MT_TRAP_EN, MT_TRAP_EN, d15 \n\t"
172 :
173 :
174 : "d15", "cc"
175 );
176 }
177 return result;
178 }
179
180 #ifdef CONFIG_STACKTRACE
181 /*
182 * stacktrace_save_entries()
183 * Save stack back trace information into the provided trace structure.
184 */
185 void stacktrace_save_entries(struct task_struct *tsk,
186 struct stack_trace *trace,
187 unsigned long sp)
188 {
189 unsigned long code_start = (unsigned long)&_stext;
190 unsigned long code_end = (unsigned long)&_etext;
191 unsigned long ocm_code_start = (unsigned long)&__ocm_text_run_begin;
192 unsigned long ocm_code_end = (unsigned long)&__data_begin;
193 unsigned long stack_end = (unsigned long)(tsk->stack + THREAD_SIZE - 8);
194 unsigned long stack = (unsigned long)sp;
195 unsigned int idx = 0;
196 unsigned long *handle;
197 int skip = trace->skip;
198
199 handle = (unsigned long *)stack;
200 while (idx < trace->max_entries) {
201 if (skip) {
202 skip--;
203 continue;
204 }
205 trace->entries[idx] = stacktrace_iterate(&handle,
206 code_start, code_end,
207 ocm_code_start, ocm_code_end,
208 (unsigned long)stack, stack_end);
209 if (trace->entries[idx] == 0) {
210 break;
211 }
212 idx++;
213 }
214 }
215
216 /*
217 * save_stack_trace()
218 * Save the specified amount of the kernel stack trace information
219 * for the current task.
220 */
221 void save_stack_trace(struct stack_trace *trace)
222 {
223 unsigned long sp = 0;
224 asm volatile (
225 " move.4 %0, SP \n\t"
226 : "=r" (sp)
227 );
228 stacktrace_save_entries(current, trace, sp);
229 }
230 EXPORT_SYMBOL_GPL(save_stack_trace);
231
232 /*
233 * save_stack_trace_tsk()
234 * Save the specified amount of the kernel stack trace information
235 * for the specified task.
236 *
237 * Note: We assume the specified task is not currently running.
238 */
239 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
240 {
241 stacktrace_save_entries(tsk, trace, tsk->thread.sp);
242 }
243 EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
244 #endif /* CONFIG_STACKTRACE */