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