Merge branch 'kvm-updates/2.6.39' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[pandora-kernel.git] / arch / unicore32 / kernel / stacktrace.c
1 /*
2  * linux/arch/unicore32/kernel/stacktrace.c
3  *
4  * Code specific to PKUnity SoC and UniCore ISA
5  *
6  * Copyright (C) 2001-2010 GUAN Xue-tao
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12 #include <linux/module.h>
13 #include <linux/sched.h>
14 #include <linux/stacktrace.h>
15
16 #include <asm/stacktrace.h>
17
18 #if defined(CONFIG_FRAME_POINTER)
19 /*
20  * Unwind the current stack frame and store the new register values in the
21  * structure passed as argument. Unwinding is equivalent to a function return,
22  * hence the new PC value rather than LR should be used for backtrace.
23  *
24  * With framepointer enabled, a simple function prologue looks like this:
25  *      mov     ip, sp
26  *      stmdb   sp!, {fp, ip, lr, pc}
27  *      sub     fp, ip, #4
28  *
29  * A simple function epilogue looks like this:
30  *      ldm     sp, {fp, sp, pc}
31  *
32  * Note that with framepointer enabled, even the leaf functions have the same
33  * prologue and epilogue, therefore we can ignore the LR value in this case.
34  */
35 int notrace unwind_frame(struct stackframe *frame)
36 {
37         unsigned long high, low;
38         unsigned long fp = frame->fp;
39
40         /* only go to a higher address on the stack */
41         low = frame->sp;
42         high = ALIGN(low, THREAD_SIZE);
43
44         /* check current frame pointer is within bounds */
45         if (fp < (low + 12) || fp + 4 >= high)
46                 return -EINVAL;
47
48         /* restore the registers from the stack frame */
49         frame->fp = *(unsigned long *)(fp - 12);
50         frame->sp = *(unsigned long *)(fp - 8);
51         frame->pc = *(unsigned long *)(fp - 4);
52
53         return 0;
54 }
55 #endif
56
57 void notrace walk_stackframe(struct stackframe *frame,
58                      int (*fn)(struct stackframe *, void *), void *data)
59 {
60         while (1) {
61                 int ret;
62
63                 if (fn(frame, data))
64                         break;
65                 ret = unwind_frame(frame);
66                 if (ret < 0)
67                         break;
68         }
69 }
70 EXPORT_SYMBOL(walk_stackframe);
71
72 #ifdef CONFIG_STACKTRACE
73 struct stack_trace_data {
74         struct stack_trace *trace;
75         unsigned int no_sched_functions;
76         unsigned int skip;
77 };
78
79 static int save_trace(struct stackframe *frame, void *d)
80 {
81         struct stack_trace_data *data = d;
82         struct stack_trace *trace = data->trace;
83         unsigned long addr = frame->pc;
84
85         if (data->no_sched_functions && in_sched_functions(addr))
86                 return 0;
87         if (data->skip) {
88                 data->skip--;
89                 return 0;
90         }
91
92         trace->entries[trace->nr_entries++] = addr;
93
94         return trace->nr_entries >= trace->max_entries;
95 }
96
97 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
98 {
99         struct stack_trace_data data;
100         struct stackframe frame;
101
102         data.trace = trace;
103         data.skip = trace->skip;
104
105         if (tsk != current) {
106                 data.no_sched_functions = 1;
107                 frame.fp = thread_saved_fp(tsk);
108                 frame.sp = thread_saved_sp(tsk);
109                 frame.lr = 0;           /* recovered from the stack */
110                 frame.pc = thread_saved_pc(tsk);
111         } else {
112                 register unsigned long current_sp asm("sp");
113
114                 data.no_sched_functions = 0;
115                 frame.fp = (unsigned long)__builtin_frame_address(0);
116                 frame.sp = current_sp;
117                 frame.lr = (unsigned long)__builtin_return_address(0);
118                 frame.pc = (unsigned long)save_stack_trace_tsk;
119         }
120
121         walk_stackframe(&frame, save_trace, &data);
122         if (trace->nr_entries < trace->max_entries)
123                 trace->entries[trace->nr_entries++] = ULONG_MAX;
124 }
125
126 void save_stack_trace(struct stack_trace *trace)
127 {
128         save_stack_trace_tsk(current, trace);
129 }
130 EXPORT_SYMBOL_GPL(save_stack_trace);
131 #endif