Merge tag 'for-5.15/parisc' of git://git.kernel.org/pub/scm/linux/kernel/git/deller...
[linux-2.6-microblaze.git] / arch / arm / kernel / stacktrace.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/export.h>
3 #include <linux/sched.h>
4 #include <linux/sched/debug.h>
5 #include <linux/stacktrace.h>
6
7 #include <asm/sections.h>
8 #include <asm/stacktrace.h>
9 #include <asm/traps.h>
10
11 #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
12 /*
13  * Unwind the current stack frame and store the new register values in the
14  * structure passed as argument. Unwinding is equivalent to a function return,
15  * hence the new PC value rather than LR should be used for backtrace.
16  *
17  * With framepointer enabled, a simple function prologue looks like this:
18  *      mov     ip, sp
19  *      stmdb   sp!, {fp, ip, lr, pc}
20  *      sub     fp, ip, #4
21  *
22  * A simple function epilogue looks like this:
23  *      ldm     sp, {fp, sp, pc}
24  *
25  * When compiled with clang, pc and sp are not pushed. A simple function
26  * prologue looks like this when built with clang:
27  *
28  *      stmdb   {..., fp, lr}
29  *      add     fp, sp, #x
30  *      sub     sp, sp, #y
31  *
32  * A simple function epilogue looks like this when built with clang:
33  *
34  *      sub     sp, fp, #x
35  *      ldm     {..., fp, pc}
36  *
37  *
38  * Note that with framepointer enabled, even the leaf functions have the same
39  * prologue and epilogue, therefore we can ignore the LR value in this case.
40  */
41 int notrace unwind_frame(struct stackframe *frame)
42 {
43         unsigned long high, low;
44         unsigned long fp = frame->fp;
45
46         /* only go to a higher address on the stack */
47         low = frame->sp;
48         high = ALIGN(low, THREAD_SIZE);
49
50 #ifdef CONFIG_CC_IS_CLANG
51         /* check current frame pointer is within bounds */
52         if (fp < low + 4 || fp > high - 4)
53                 return -EINVAL;
54
55         frame->sp = frame->fp;
56         frame->fp = *(unsigned long *)(fp);
57         frame->pc = frame->lr;
58         frame->lr = *(unsigned long *)(fp + 4);
59 #else
60         /* check current frame pointer is within bounds */
61         if (fp < low + 12 || fp > high - 4)
62                 return -EINVAL;
63
64         /* restore the registers from the stack frame */
65         frame->fp = *(unsigned long *)(fp - 12);
66         frame->sp = *(unsigned long *)(fp - 8);
67         frame->pc = *(unsigned long *)(fp - 4);
68 #endif
69
70         return 0;
71 }
72 #endif
73
74 void notrace walk_stackframe(struct stackframe *frame,
75                      int (*fn)(struct stackframe *, void *), void *data)
76 {
77         while (1) {
78                 int ret;
79
80                 if (fn(frame, data))
81                         break;
82                 ret = unwind_frame(frame);
83                 if (ret < 0)
84                         break;
85         }
86 }
87 EXPORT_SYMBOL(walk_stackframe);
88
89 #ifdef CONFIG_STACKTRACE
90 struct stack_trace_data {
91         struct stack_trace *trace;
92         unsigned int no_sched_functions;
93         unsigned int skip;
94 };
95
96 static int save_trace(struct stackframe *frame, void *d)
97 {
98         struct stack_trace_data *data = d;
99         struct stack_trace *trace = data->trace;
100         struct pt_regs *regs;
101         unsigned long addr = frame->pc;
102
103         if (data->no_sched_functions && in_sched_functions(addr))
104                 return 0;
105         if (data->skip) {
106                 data->skip--;
107                 return 0;
108         }
109
110         trace->entries[trace->nr_entries++] = addr;
111
112         if (trace->nr_entries >= trace->max_entries)
113                 return 1;
114
115         if (!in_entry_text(frame->pc))
116                 return 0;
117
118         regs = (struct pt_regs *)frame->sp;
119         if ((unsigned long)&regs[1] > ALIGN(frame->sp, THREAD_SIZE))
120                 return 0;
121
122         trace->entries[trace->nr_entries++] = regs->ARM_pc;
123
124         return trace->nr_entries >= trace->max_entries;
125 }
126
127 /* This must be noinline to so that our skip calculation works correctly */
128 static noinline void __save_stack_trace(struct task_struct *tsk,
129         struct stack_trace *trace, unsigned int nosched)
130 {
131         struct stack_trace_data data;
132         struct stackframe frame;
133
134         data.trace = trace;
135         data.skip = trace->skip;
136         data.no_sched_functions = nosched;
137
138         if (tsk != current) {
139 #ifdef CONFIG_SMP
140                 /*
141                  * What guarantees do we have here that 'tsk' is not
142                  * running on another CPU?  For now, ignore it as we
143                  * can't guarantee we won't explode.
144                  */
145                 return;
146 #else
147                 frame.fp = thread_saved_fp(tsk);
148                 frame.sp = thread_saved_sp(tsk);
149                 frame.lr = 0;           /* recovered from the stack */
150                 frame.pc = thread_saved_pc(tsk);
151 #endif
152         } else {
153                 /* We don't want this function nor the caller */
154                 data.skip += 2;
155                 frame.fp = (unsigned long)__builtin_frame_address(0);
156                 frame.sp = current_stack_pointer;
157                 frame.lr = (unsigned long)__builtin_return_address(0);
158                 frame.pc = (unsigned long)__save_stack_trace;
159         }
160
161         walk_stackframe(&frame, save_trace, &data);
162 }
163
164 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
165 {
166         struct stack_trace_data data;
167         struct stackframe frame;
168
169         data.trace = trace;
170         data.skip = trace->skip;
171         data.no_sched_functions = 0;
172
173         frame.fp = regs->ARM_fp;
174         frame.sp = regs->ARM_sp;
175         frame.lr = regs->ARM_lr;
176         frame.pc = regs->ARM_pc;
177
178         walk_stackframe(&frame, save_trace, &data);
179 }
180
181 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
182 {
183         __save_stack_trace(tsk, trace, 1);
184 }
185 EXPORT_SYMBOL(save_stack_trace_tsk);
186
187 void save_stack_trace(struct stack_trace *trace)
188 {
189         __save_stack_trace(current, trace, 0);
190 }
191 EXPORT_SYMBOL_GPL(save_stack_trace);
192 #endif