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

TOMOYO Linux Cross Reference
Linux/arch/parisc/kernel/unwind.c

Version: ~ [ linux-4.19-rc5 ] ~ [ linux-4.18.9 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.71 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.128 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.157 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.122 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.57 ] ~ [ 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.31.14 ] ~ [ linux-2.6.30.10 ] ~ [ linux-2.6.29.6 ] ~ [ linux-2.6.28.10 ] ~ [ linux-2.6.27.62 ] ~ [ 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 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * Kernel unwinding support
  4  *
  5  * (c) 2002-2004 Randolph Chung <tausq@debian.org>
  6  *
  7  * Derived partially from the IA64 implementation. The PA-RISC
  8  * Runtime Architecture Document is also a useful reference to
  9  * understand what is happening here
 10  */
 11 
 12 #include <linux/kernel.h>
 13 #include <linux/init.h>
 14 #include <linux/sched.h>
 15 #include <linux/slab.h>
 16 #include <linux/sort.h>
 17 
 18 #include <linux/uaccess.h>
 19 #include <asm/assembly.h>
 20 #include <asm/asm-offsets.h>
 21 #include <asm/ptrace.h>
 22 
 23 #include <asm/unwind.h>
 24 
 25 /* #define DEBUG 1 */
 26 #ifdef DEBUG
 27 #define dbg(x...) pr_debug(x)
 28 #else
 29 #define dbg(x...)
 30 #endif
 31 
 32 #define KERNEL_START (KERNEL_BINARY_TEXT_START)
 33 
 34 extern struct unwind_table_entry __start___unwind[];
 35 extern struct unwind_table_entry __stop___unwind[];
 36 
 37 static DEFINE_SPINLOCK(unwind_lock);
 38 /*
 39  * the kernel unwind block is not dynamically allocated so that
 40  * we can call unwind_init as early in the bootup process as 
 41  * possible (before the slab allocator is initialized)
 42  */
 43 static struct unwind_table kernel_unwind_table __read_mostly;
 44 static LIST_HEAD(unwind_tables);
 45 
 46 static inline const struct unwind_table_entry *
 47 find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
 48 {
 49         const struct unwind_table_entry *e = NULL;
 50         unsigned long lo, hi, mid;
 51 
 52         lo = 0; 
 53         hi = table->length - 1; 
 54         
 55         while (lo <= hi) {
 56                 mid = (hi - lo) / 2 + lo;
 57                 e = &table->table[mid];
 58                 if (addr < e->region_start)
 59                         hi = mid - 1;
 60                 else if (addr > e->region_end)
 61                         lo = mid + 1;
 62                 else
 63                         return e;
 64         }
 65 
 66         return NULL;
 67 }
 68 
 69 static const struct unwind_table_entry *
 70 find_unwind_entry(unsigned long addr)
 71 {
 72         struct unwind_table *table;
 73         const struct unwind_table_entry *e = NULL;
 74 
 75         if (addr >= kernel_unwind_table.start && 
 76             addr <= kernel_unwind_table.end)
 77                 e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
 78         else {
 79                 unsigned long flags;
 80 
 81                 spin_lock_irqsave(&unwind_lock, flags);
 82                 list_for_each_entry(table, &unwind_tables, list) {
 83                         if (addr >= table->start && 
 84                             addr <= table->end)
 85                                 e = find_unwind_entry_in_table(table, addr);
 86                         if (e) {
 87                                 /* Move-to-front to exploit common traces */
 88                                 list_move(&table->list, &unwind_tables);
 89                                 break;
 90                         }
 91                 }
 92                 spin_unlock_irqrestore(&unwind_lock, flags);
 93         }
 94 
 95         return e;
 96 }
 97 
 98 static void
 99 unwind_table_init(struct unwind_table *table, const char *name,
100                   unsigned long base_addr, unsigned long gp,
101                   void *table_start, void *table_end)
102 {
103         struct unwind_table_entry *start = table_start;
104         struct unwind_table_entry *end = 
105                 (struct unwind_table_entry *)table_end - 1;
106 
107         table->name = name;
108         table->base_addr = base_addr;
109         table->gp = gp;
110         table->start = base_addr + start->region_start;
111         table->end = base_addr + end->region_end;
112         table->table = (struct unwind_table_entry *)table_start;
113         table->length = end - start + 1;
114         INIT_LIST_HEAD(&table->list);
115 
116         for (; start <= end; start++) {
117                 if (start < end && 
118                     start->region_end > (start+1)->region_start) {
119                         pr_warn("Out of order unwind entry! %px and %px\n",
120                                 start, start+1);
121                 }
122 
123                 start->region_start += base_addr;
124                 start->region_end += base_addr;
125         }
126 }
127 
128 static int cmp_unwind_table_entry(const void *a, const void *b)
129 {
130         return ((const struct unwind_table_entry *)a)->region_start
131              - ((const struct unwind_table_entry *)b)->region_start;
132 }
133 
134 static void
135 unwind_table_sort(struct unwind_table_entry *start,
136                   struct unwind_table_entry *finish)
137 {
138         sort(start, finish - start, sizeof(struct unwind_table_entry),
139              cmp_unwind_table_entry, NULL);
140 }
141 
142 struct unwind_table *
143 unwind_table_add(const char *name, unsigned long base_addr, 
144                  unsigned long gp,
145                  void *start, void *end)
146 {
147         struct unwind_table *table;
148         unsigned long flags;
149         struct unwind_table_entry *s = (struct unwind_table_entry *)start;
150         struct unwind_table_entry *e = (struct unwind_table_entry *)end;
151 
152         unwind_table_sort(s, e);
153 
154         table = kmalloc(sizeof(struct unwind_table), GFP_USER);
155         if (table == NULL)
156                 return NULL;
157         unwind_table_init(table, name, base_addr, gp, start, end);
158         spin_lock_irqsave(&unwind_lock, flags);
159         list_add_tail(&table->list, &unwind_tables);
160         spin_unlock_irqrestore(&unwind_lock, flags);
161 
162         return table;
163 }
164 
165 void unwind_table_remove(struct unwind_table *table)
166 {
167         unsigned long flags;
168 
169         spin_lock_irqsave(&unwind_lock, flags);
170         list_del(&table->list);
171         spin_unlock_irqrestore(&unwind_lock, flags);
172 
173         kfree(table);
174 }
175 
176 /* Called from setup_arch to import the kernel unwind info */
177 int __init unwind_init(void)
178 {
179         long start, stop;
180         register unsigned long gp __asm__ ("r27");
181 
182         start = (long)&__start___unwind[0];
183         stop = (long)&__stop___unwind[0];
184 
185         dbg("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n",
186             start, stop,
187             (stop - start) / sizeof(struct unwind_table_entry));
188 
189         unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
190                           gp, 
191                           &__start___unwind[0], &__stop___unwind[0]);
192 #if 0
193         {
194                 int i;
195                 for (i = 0; i < 10; i++)
196                 {
197                         printk("region 0x%x-0x%x\n", 
198                                 __start___unwind[i].region_start, 
199                                 __start___unwind[i].region_end);
200                 }
201         }
202 #endif
203         return 0;
204 }
205 
206 static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size)
207 {
208         /*
209          * We have to use void * instead of a function pointer, because
210          * function pointers aren't a pointer to the function on 64-bit.
211          * Make them const so the compiler knows they live in .text
212          * Note: We could use dereference_kernel_function_descriptor()
213          * instead but we want to keep it simple here.
214          */
215         extern void * const handle_interruption;
216         extern void * const ret_from_kernel_thread;
217         extern void * const syscall_exit;
218         extern void * const intr_return;
219         extern void * const _switch_to_ret;
220 #ifdef CONFIG_IRQSTACKS
221         extern void * const _call_on_stack;
222 #endif /* CONFIG_IRQSTACKS */
223 
224         if (pc == (unsigned long) &handle_interruption) {
225                 struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN);
226                 dbg("Unwinding through handle_interruption()\n");
227                 info->prev_sp = regs->gr[30];
228                 info->prev_ip = regs->iaoq[0];
229                 return 1;
230         }
231 
232         if (pc == (unsigned long) &ret_from_kernel_thread ||
233             pc == (unsigned long) &syscall_exit) {
234                 info->prev_sp = info->prev_ip = 0;
235                 return 1;
236         }
237 
238         if (pc == (unsigned long) &intr_return) {
239                 struct pt_regs *regs;
240 
241                 dbg("Found intr_return()\n");
242                 regs = (struct pt_regs *)(info->sp - PT_SZ_ALGN);
243                 info->prev_sp = regs->gr[30];
244                 info->prev_ip = regs->iaoq[0];
245                 info->rp = regs->gr[2];
246                 return 1;
247         }
248 
249         if (pc == (unsigned long) &_switch_to_ret) {
250                 info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
251                 info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
252                 return 1;
253         }
254 
255 #ifdef CONFIG_IRQSTACKS
256         if (pc == (unsigned long) &_call_on_stack) {
257                 info->prev_sp = *(unsigned long *)(info->sp - FRAME_SIZE - REG_SZ);
258                 info->prev_ip = *(unsigned long *)(info->sp - FRAME_SIZE - RP_OFFSET);
259                 return 1;
260         }
261 #endif
262 
263         return 0;
264 }
265 
266 static void unwind_frame_regs(struct unwind_frame_info *info)
267 {
268         const struct unwind_table_entry *e;
269         unsigned long npc;
270         unsigned int insn;
271         long frame_size = 0;
272         int looking_for_rp, rpoffset = 0;
273 
274         e = find_unwind_entry(info->ip);
275         if (e == NULL) {
276                 unsigned long sp;
277 
278                 dbg("Cannot find unwind entry for %pS; forced unwinding\n",
279                         (void *) info->ip);
280 
281                 /* Since we are doing the unwinding blind, we don't know if
282                    we are adjusting the stack correctly or extracting the rp
283                    correctly. The rp is checked to see if it belongs to the
284                    kernel text section, if not we assume we don't have a 
285                    correct stack frame and we continue to unwind the stack.
286                    This is not quite correct, and will fail for loadable
287                    modules. */
288                 sp = info->sp & ~63;
289                 do {
290                         unsigned long tmp;
291 
292                         info->prev_sp = sp - 64;
293                         info->prev_ip = 0;
294 
295                         /* The stack is at the end inside the thread_union
296                          * struct. If we reach data, we have reached the
297                          * beginning of the stack and should stop unwinding. */
298                         if (info->prev_sp >= (unsigned long) task_thread_info(info->t) &&
299                             info->prev_sp < ((unsigned long) task_thread_info(info->t)
300                                                 + THREAD_SZ_ALGN)) {
301                                 info->prev_sp = 0;
302                                 break;
303                         }
304 
305                         if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) 
306                                 break;
307                         info->prev_ip = tmp;
308                         sp = info->prev_sp;
309                 } while (!kernel_text_address(info->prev_ip));
310 
311                 info->rp = 0;
312 
313                 dbg("analyzing func @ %lx with no unwind info, setting "
314                     "prev_sp=%lx prev_ip=%lx\n", info->ip, 
315                     info->prev_sp, info->prev_ip);
316         } else {
317                 dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
318                     "Save_RP = %d, Millicode = %d size = %u\n", 
319                     e->region_start, e->region_end, e->Save_SP, e->Save_RP, 
320                     e->Millicode, e->Total_frame_size);
321 
322                 looking_for_rp = e->Save_RP;
323 
324                 for (npc = e->region_start; 
325                      (frame_size < (e->Total_frame_size << 3) || 
326                       looking_for_rp) && 
327                      npc < info->ip; 
328                      npc += 4) {
329 
330                         insn = *(unsigned int *)npc;
331 
332                         if ((insn & 0xffffc001) == 0x37de0000 ||
333                             (insn & 0xffe00001) == 0x6fc00000) {
334                                 /* ldo X(sp), sp, or stwm X,D(sp) */
335                                 frame_size += (insn & 0x3fff) >> 1;
336                                 dbg("analyzing func @ %lx, insn=%08x @ "
337                                     "%lx, frame_size = %ld\n", info->ip,
338                                     insn, npc, frame_size);
339                         } else if ((insn & 0xffe00009) == 0x73c00008) {
340                                 /* std,ma X,D(sp) */
341                                 frame_size += ((insn >> 4) & 0x3ff) << 3;
342                                 dbg("analyzing func @ %lx, insn=%08x @ "
343                                     "%lx, frame_size = %ld\n", info->ip,
344                                     insn, npc, frame_size);
345                         } else if (insn == 0x6bc23fd9) { 
346                                 /* stw rp,-20(sp) */
347                                 rpoffset = 20;
348                                 looking_for_rp = 0;
349                                 dbg("analyzing func @ %lx, insn=stw rp,"
350                                     "-20(sp) @ %lx\n", info->ip, npc);
351                         } else if (insn == 0x0fc212c1) {
352                                 /* std rp,-16(sr0,sp) */
353                                 rpoffset = 16;
354                                 looking_for_rp = 0;
355                                 dbg("analyzing func @ %lx, insn=std rp,"
356                                     "-16(sp) @ %lx\n", info->ip, npc);
357                         }
358                 }
359 
360                 if (frame_size > e->Total_frame_size << 3)
361                         frame_size = e->Total_frame_size << 3;
362 
363                 if (!unwind_special(info, e->region_start, frame_size)) {
364                         info->prev_sp = info->sp - frame_size;
365                         if (e->Millicode)
366                                 info->rp = info->r31;
367                         else if (rpoffset)
368                                 info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
369                         info->prev_ip = info->rp;
370                         info->rp = 0;
371                 }
372 
373                 dbg("analyzing func @ %lx, setting prev_sp=%lx "
374                     "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, 
375                     info->prev_ip, npc);
376         }
377 }
378 
379 void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, 
380                        struct pt_regs *regs)
381 {
382         memset(info, 0, sizeof(struct unwind_frame_info));
383         info->t = t;
384         info->sp = regs->gr[30];
385         info->ip = regs->iaoq[0];
386         info->rp = regs->gr[2];
387         info->r31 = regs->gr[31];
388 
389         dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", 
390             t ? (int)t->pid : -1, info->sp, info->ip);
391 }
392 
393 void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
394 {
395         struct pt_regs *r = &t->thread.regs;
396         struct pt_regs *r2;
397 
398         r2 = kmalloc(sizeof(struct pt_regs), GFP_ATOMIC);
399         if (!r2)
400                 return;
401         *r2 = *r;
402         r2->gr[30] = r->ksp;
403         r2->iaoq[0] = r->kpc;
404         unwind_frame_init(info, t, r2);
405         kfree(r2);
406 }
407 
408 #define get_parisc_stackpointer() ({ \
409         unsigned long sp; \
410         __asm__("copy %%r30, %0" : "=r"(sp)); \
411         (sp); \
412 })
413 
414 void unwind_frame_init_task(struct unwind_frame_info *info,
415         struct task_struct *task, struct pt_regs *regs)
416 {
417         task = task ? task : current;
418 
419         if (task == current) {
420                 struct pt_regs r;
421 
422                 if (!regs) {
423                         memset(&r, 0, sizeof(r));
424                         r.iaoq[0] =  _THIS_IP_;
425                         r.gr[2] = _RET_IP_;
426                         r.gr[30] = get_parisc_stackpointer();
427                         regs = &r;
428                 }
429                 unwind_frame_init(info, task, &r);
430         } else {
431                 unwind_frame_init_from_blocked_task(info, task);
432         }
433 }
434 
435 int unwind_once(struct unwind_frame_info *next_frame)
436 {
437         unwind_frame_regs(next_frame);
438 
439         if (next_frame->prev_sp == 0 ||
440             next_frame->prev_ip == 0)
441                 return -1;
442 
443         next_frame->sp = next_frame->prev_sp;
444         next_frame->ip = next_frame->prev_ip;
445         next_frame->prev_sp = 0;
446         next_frame->prev_ip = 0;
447 
448         dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", 
449             next_frame->t ? (int)next_frame->t->pid : -1, 
450             next_frame->sp, next_frame->ip);
451 
452         return 0;
453 }
454 
455 int unwind_to_user(struct unwind_frame_info *info)
456 {
457         int ret;
458         
459         do {
460                 ret = unwind_once(info);
461         } while (!ret && !(info->ip & 3));
462 
463         return ret;
464 }
465 
466 unsigned long return_address(unsigned int level)
467 {
468         struct unwind_frame_info info;
469 
470         /* initialize unwind info */
471         unwind_frame_init_task(&info, current, NULL);
472 
473         /* unwind stack */
474         level += 2;
475         do {
476                 if (unwind_once(&info) < 0 || info.ip == 0)
477                         return 0;
478                 if (!kernel_text_address(info.ip))
479                         return 0;
480         } while (info.ip && level--);
481 
482         return info.ip;
483 }
484 

~ [ 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