~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/xtensa/kernel/stacktrace.c

Version: ~ [ linux-5.2-rc1 ] ~ [ linux-5.1.2 ] ~ [ linux-5.0.16 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.43 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.119 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.176 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.179 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.139 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.67 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Kernel and userspace stack tracing.
  3  *
  4  * This file is subject to the terms and conditions of the GNU General Public
  5  * License.  See the file "COPYING" in the main directory of this archive
  6  * for more details.
  7  *
  8  * Copyright (C) 2001 - 2013 Tensilica Inc.
  9  * Copyright (C) 2015 Cadence Design Systems Inc.
 10  */
 11 #include <linux/export.h>
 12 #include <linux/sched.h>
 13 #include <linux/stacktrace.h>
 14 
 15 #include <asm/stacktrace.h>
 16 #include <asm/traps.h>
 17 #include <linux/uaccess.h>
 18 
 19 #if IS_ENABLED(CONFIG_OPROFILE) || IS_ENABLED(CONFIG_PERF_EVENTS)
 20 
 21 /* Address of common_exception_return, used to check the
 22  * transition from kernel to user space.
 23  */
 24 extern int common_exception_return;
 25 
 26 void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
 27                            int (*ufn)(struct stackframe *frame, void *data),
 28                            void *data)
 29 {
 30         unsigned long windowstart = regs->windowstart;
 31         unsigned long windowbase = regs->windowbase;
 32         unsigned long a0 = regs->areg[0];
 33         unsigned long a1 = regs->areg[1];
 34         unsigned long pc = regs->pc;
 35         struct stackframe frame;
 36         int index;
 37 
 38         if (!depth--)
 39                 return;
 40 
 41         frame.pc = pc;
 42         frame.sp = a1;
 43 
 44         if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
 45                 return;
 46 
 47         /* Two steps:
 48          *
 49          * 1. Look through the register window for the
 50          * previous PCs in the call trace.
 51          *
 52          * 2. Look on the stack.
 53          */
 54 
 55         /* Step 1.  */
 56         /* Rotate WINDOWSTART to move the bit corresponding to
 57          * the current window to the bit #0.
 58          */
 59         windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
 60 
 61         /* Look for bits that are set, they correspond to
 62          * valid windows.
 63          */
 64         for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
 65                 if (windowstart & (1 << index)) {
 66                         /* Get the PC from a0 and a1. */
 67                         pc = MAKE_PC_FROM_RA(a0, pc);
 68                         /* Read a0 and a1 from the
 69                          * corresponding position in AREGs.
 70                          */
 71                         a0 = regs->areg[index * 4];
 72                         a1 = regs->areg[index * 4 + 1];
 73 
 74                         frame.pc = pc;
 75                         frame.sp = a1;
 76 
 77                         if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
 78                                 return;
 79                 }
 80 
 81         /* Step 2. */
 82         /* We are done with the register window, we need to
 83          * look through the stack.
 84          */
 85         if (!depth)
 86                 return;
 87 
 88         /* Start from the a1 register. */
 89         /* a1 = regs->areg[1]; */
 90         while (a0 != 0 && depth--) {
 91                 pc = MAKE_PC_FROM_RA(a0, pc);
 92 
 93                 /* Check if the region is OK to access. */
 94                 if (!access_ok(VERIFY_READ, &SPILL_SLOT(a1, 0), 8))
 95                         return;
 96                 /* Copy a1, a0 from user space stack frame. */
 97                 if (__get_user(a0, &SPILL_SLOT(a1, 0)) ||
 98                     __get_user(a1, &SPILL_SLOT(a1, 1)))
 99                         return;
100 
101                 frame.pc = pc;
102                 frame.sp = a1;
103 
104                 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
105                         return;
106         }
107 }
108 EXPORT_SYMBOL(xtensa_backtrace_user);
109 
110 void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
111                              int (*kfn)(struct stackframe *frame, void *data),
112                              int (*ufn)(struct stackframe *frame, void *data),
113                              void *data)
114 {
115         unsigned long pc = regs->depc > VALID_DOUBLE_EXCEPTION_ADDRESS ?
116                 regs->depc : regs->pc;
117         unsigned long sp_start, sp_end;
118         unsigned long a0 = regs->areg[0];
119         unsigned long a1 = regs->areg[1];
120 
121         sp_start = a1 & ~(THREAD_SIZE - 1);
122         sp_end = sp_start + THREAD_SIZE;
123 
124         /* Spill the register window to the stack first. */
125         spill_registers();
126 
127         /* Read the stack frames one by one and create the PC
128          * from the a0 and a1 registers saved there.
129          */
130         while (a1 > sp_start && a1 < sp_end && depth--) {
131                 struct stackframe frame;
132 
133                 frame.pc = pc;
134                 frame.sp = a1;
135 
136                 if (kernel_text_address(pc) && kfn(&frame, data))
137                         return;
138 
139                 if (pc == (unsigned long)&common_exception_return) {
140                         regs = (struct pt_regs *)a1;
141                         if (user_mode(regs)) {
142                                 if (ufn == NULL)
143                                         return;
144                                 xtensa_backtrace_user(regs, depth, ufn, data);
145                                 return;
146                         }
147                         a0 = regs->areg[0];
148                         a1 = regs->areg[1];
149                         continue;
150                 }
151 
152                 sp_start = a1;
153 
154                 pc = MAKE_PC_FROM_RA(a0, pc);
155                 a0 = SPILL_SLOT(a1, 0);
156                 a1 = SPILL_SLOT(a1, 1);
157         }
158 }
159 EXPORT_SYMBOL(xtensa_backtrace_kernel);
160 
161 #endif
162 
163 void walk_stackframe(unsigned long *sp,
164                 int (*fn)(struct stackframe *frame, void *data),
165                 void *data)
166 {
167         unsigned long a0, a1;
168         unsigned long sp_end;
169 
170         a1 = (unsigned long)sp;
171         sp_end = ALIGN(a1, THREAD_SIZE);
172 
173         spill_registers();
174 
175         while (a1 < sp_end) {
176                 struct stackframe frame;
177 
178                 sp = (unsigned long *)a1;
179 
180                 a0 = SPILL_SLOT(a1, 0);
181                 a1 = SPILL_SLOT(a1, 1);
182 
183                 if (a1 <= (unsigned long)sp)
184                         break;
185 
186                 frame.pc = MAKE_PC_FROM_RA(a0, a1);
187                 frame.sp = a1;
188 
189                 if (fn(&frame, data))
190                         return;
191         }
192 }
193 
194 #ifdef CONFIG_STACKTRACE
195 
196 struct stack_trace_data {
197         struct stack_trace *trace;
198         unsigned skip;
199 };
200 
201 static int stack_trace_cb(struct stackframe *frame, void *data)
202 {
203         struct stack_trace_data *trace_data = data;
204         struct stack_trace *trace = trace_data->trace;
205 
206         if (trace_data->skip) {
207                 --trace_data->skip;
208                 return 0;
209         }
210         if (!kernel_text_address(frame->pc))
211                 return 0;
212 
213         trace->entries[trace->nr_entries++] = frame->pc;
214         return trace->nr_entries >= trace->max_entries;
215 }
216 
217 void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
218 {
219         struct stack_trace_data trace_data = {
220                 .trace = trace,
221                 .skip = trace->skip,
222         };
223         walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data);
224 }
225 EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
226 
227 void save_stack_trace(struct stack_trace *trace)
228 {
229         save_stack_trace_tsk(current, trace);
230 }
231 EXPORT_SYMBOL_GPL(save_stack_trace);
232 
233 #endif
234 
235 #ifdef CONFIG_FRAME_POINTER
236 
237 struct return_addr_data {
238         unsigned long addr;
239         unsigned skip;
240 };
241 
242 static int return_address_cb(struct stackframe *frame, void *data)
243 {
244         struct return_addr_data *r = data;
245 
246         if (r->skip) {
247                 --r->skip;
248                 return 0;
249         }
250         if (!kernel_text_address(frame->pc))
251                 return 0;
252         r->addr = frame->pc;
253         return 1;
254 }
255 
256 unsigned long return_address(unsigned level)
257 {
258         struct return_addr_data r = {
259                 .skip = level + 1,
260         };
261         walk_stackframe(stack_pointer(NULL), return_address_cb, &r);
262         return r.addr;
263 }
264 EXPORT_SYMBOL(return_address);
265 
266 #endif
267 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp