Root/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 */
38extern unsigned long _stext;
39extern unsigned long _etext;
40
41extern unsigned long __ocm_text_run_begin;
42extern 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 */
51unsigned 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 */
185void 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 */
221void 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}
230EXPORT_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 */
239void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
240{
241    stacktrace_save_entries(tsk, trace, tsk->thread.sp);
242}
243EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
244#endif /* CONFIG_STACKTRACE */
245

Archive Download this file



interactive