root/arch/arm/kernel/stacktrace.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. unwind_frame
  2. walk_stackframe
  3. save_trace
  4. __save_stack_trace
  5. save_stack_trace_regs
  6. save_stack_trace_tsk
  7. save_stack_trace

   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  * Note that with framepointer enabled, even the leaf functions have the same
  26  * prologue and epilogue, therefore we can ignore the LR value in this case.
  27  */
  28 int notrace unwind_frame(struct stackframe *frame)
  29 {
  30         unsigned long high, low;
  31         unsigned long fp = frame->fp;
  32 
  33         /* only go to a higher address on the stack */
  34         low = frame->sp;
  35         high = ALIGN(low, THREAD_SIZE);
  36 
  37         /* check current frame pointer is within bounds */
  38         if (fp < low + 12 || fp > high - 4)
  39                 return -EINVAL;
  40 
  41         /* restore the registers from the stack frame */
  42         frame->fp = *(unsigned long *)(fp - 12);
  43         frame->sp = *(unsigned long *)(fp - 8);
  44         frame->pc = *(unsigned long *)(fp - 4);
  45 
  46         return 0;
  47 }
  48 #endif
  49 
  50 void notrace walk_stackframe(struct stackframe *frame,
  51                      int (*fn)(struct stackframe *, void *), void *data)
  52 {
  53         while (1) {
  54                 int ret;
  55 
  56                 if (fn(frame, data))
  57                         break;
  58                 ret = unwind_frame(frame);
  59                 if (ret < 0)
  60                         break;
  61         }
  62 }
  63 EXPORT_SYMBOL(walk_stackframe);
  64 
  65 #ifdef CONFIG_STACKTRACE
  66 struct stack_trace_data {
  67         struct stack_trace *trace;
  68         unsigned int no_sched_functions;
  69         unsigned int skip;
  70 };
  71 
  72 static int save_trace(struct stackframe *frame, void *d)
  73 {
  74         struct stack_trace_data *data = d;
  75         struct stack_trace *trace = data->trace;
  76         struct pt_regs *regs;
  77         unsigned long addr = frame->pc;
  78 
  79         if (data->no_sched_functions && in_sched_functions(addr))
  80                 return 0;
  81         if (data->skip) {
  82                 data->skip--;
  83                 return 0;
  84         }
  85 
  86         trace->entries[trace->nr_entries++] = addr;
  87 
  88         if (trace->nr_entries >= trace->max_entries)
  89                 return 1;
  90 
  91         if (!in_entry_text(frame->pc))
  92                 return 0;
  93 
  94         regs = (struct pt_regs *)frame->sp;
  95 
  96         trace->entries[trace->nr_entries++] = regs->ARM_pc;
  97 
  98         return trace->nr_entries >= trace->max_entries;
  99 }
 100 
 101 /* This must be noinline to so that our skip calculation works correctly */
 102 static noinline void __save_stack_trace(struct task_struct *tsk,
 103         struct stack_trace *trace, unsigned int nosched)
 104 {
 105         struct stack_trace_data data;
 106         struct stackframe frame;
 107 
 108         data.trace = trace;
 109         data.skip = trace->skip;
 110         data.no_sched_functions = nosched;
 111 
 112         if (tsk != current) {
 113 #ifdef CONFIG_SMP
 114                 /*
 115                  * What guarantees do we have here that 'tsk' is not
 116                  * running on another CPU?  For now, ignore it as we
 117                  * can't guarantee we won't explode.
 118                  */
 119                 return;
 120 #else
 121                 frame.fp = thread_saved_fp(tsk);
 122                 frame.sp = thread_saved_sp(tsk);
 123                 frame.lr = 0;           /* recovered from the stack */
 124                 frame.pc = thread_saved_pc(tsk);
 125 #endif
 126         } else {
 127                 /* We don't want this function nor the caller */
 128                 data.skip += 2;
 129                 frame.fp = (unsigned long)__builtin_frame_address(0);
 130                 frame.sp = current_stack_pointer;
 131                 frame.lr = (unsigned long)__builtin_return_address(0);
 132                 frame.pc = (unsigned long)__save_stack_trace;
 133         }
 134 
 135         walk_stackframe(&frame, save_trace, &data);
 136 }
 137 
 138 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
 139 {
 140         struct stack_trace_data data;
 141         struct stackframe frame;
 142 
 143         data.trace = trace;
 144         data.skip = trace->skip;
 145         data.no_sched_functions = 0;
 146 
 147         frame.fp = regs->ARM_fp;
 148         frame.sp = regs->ARM_sp;
 149         frame.lr = regs->ARM_lr;
 150         frame.pc = regs->ARM_pc;
 151 
 152         walk_stackframe(&frame, save_trace, &data);
 153 }
 154 
 155 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
 156 {
 157         __save_stack_trace(tsk, trace, 1);
 158 }
 159 EXPORT_SYMBOL(save_stack_trace_tsk);
 160 
 161 void save_stack_trace(struct stack_trace *trace)
 162 {
 163         __save_stack_trace(current, trace, 0);
 164 }
 165 EXPORT_SYMBOL_GPL(save_stack_trace);
 166 #endif

/* [<][>][^][v][top][bottom][index][help] */