2a10e3f4f1494e30b6c0637fbb6dd2d4a7ed8a40
2 * arch/ubicom32/kernel/stacktrace.c
3 * Ubicom32 architecture stack back trace implementation.
5 * (C) Copyright 2009, Ubicom, Inc.
7 * This file is part of the Ubicom32 Linux Kernel Port.
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.
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.
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/>.
23 * Ubicom32 implementation derived from (with many thanks):
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>
36 * These symbols are filled in by the linker.
38 extern unsigned long _stext
;
39 extern unsigned long _etext
;
41 extern unsigned long __ocm_text_run_begin
;
42 extern unsigned long __data_begin
;
45 * stacktrace_iterate()
46 * Walk the stack looking for call and calli instructions on an aligned
49 * Trace must point to the top of the current stack frame.
51 unsigned long stacktrace_iterate(unsigned long **trace
,
54 unsigned long ocm_stext
,
55 unsigned long ocm_etext
,
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
;
66 * Exclude the current thread from being monitored for traps.
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"
74 " and.4 MT_TRAP_EN, MT_TRAP_EN, d15 \n\t"
76 : "=r" (thread_trap_en
)
81 while (limit
++ < 256) {
83 * See if we have a valid stack.
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
);
98 * Get the value off the stack and back up 4 bytes to what
99 * should be the address of a call or calli.
101 address
= (*sp
++) - 4;
104 * If the address is not within the text segment, skip this
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
,
116 (unsigned int)ocm_stext
,
117 (unsigned int)ocm_etext
);
124 * If the address is not on an aligned boundary it can not be a
132 * Read the probable instruction.
134 instruction
= *(unsigned int *)address
;
137 * Is this a call instruction?
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
);
151 * Is this a calli instruction?
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
);
166 * Restore the current thread to be monitored for traps.
168 if (thread_trap_en
) {
170 " thread_get_self_mask d15 \n\t"
171 " or.4 MT_TRAP_EN, MT_TRAP_EN, d15 \n\t"
180 #ifdef CONFIG_STACKTRACE
182 * stacktrace_save_entries()
183 * Save stack back trace information into the provided trace structure.
185 void stacktrace_save_entries(struct task_struct
*tsk
,
186 struct stack_trace
*trace
,
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
;
199 handle
= (unsigned long *)stack
;
200 while (idx
< trace
->max_entries
) {
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) {
218 * Save the specified amount of the kernel stack trace information
219 * for the current task.
221 void save_stack_trace(struct stack_trace
*trace
)
223 unsigned long sp
= 0;
225 " move.4 %0, SP \n\t"
228 stacktrace_save_entries(current
, trace
, sp
);
230 EXPORT_SYMBOL_GPL(save_stack_trace
);
233 * save_stack_trace_tsk()
234 * Save the specified amount of the kernel stack trace information
235 * for the specified task.
237 * Note: We assume the specified task is not currently running.
239 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
241 stacktrace_save_entries(tsk
, trace
, tsk
->thread
.sp
);
243 EXPORT_SYMBOL_GPL(save_stack_trace_tsk
);
244 #endif /* CONFIG_STACKTRACE */