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 if ((unsigned long)®s[1] > ALIGN(frame->sp, THREAD_SIZE)) 96 return 0; 97 98 trace->entries[trace->nr_entries++] = regs->ARM_pc; 99 100 return trace->nr_entries >= trace->max_entries; 101 } 102 103 /* This must be noinline to so that our skip calculation works correctly */ 104 static noinline void __save_stack_trace(struct task_struct *tsk, 105 struct stack_trace *trace, unsigned int nosched) 106 { 107 struct stack_trace_data data; 108 struct stackframe frame; 109 110 data.trace = trace; 111 data.skip = trace->skip; 112 data.no_sched_functions = nosched; 113 114 if (tsk != current) { 115 #ifdef CONFIG_SMP 116 /* 117 * What guarantees do we have here that 'tsk' is not 118 * running on another CPU? For now, ignore it as we 119 * can't guarantee we won't explode. 120 */ 121 return; 122 #else 123 frame.fp = thread_saved_fp(tsk); 124 frame.sp = thread_saved_sp(tsk); 125 frame.lr = 0; /* recovered from the stack */ 126 frame.pc = thread_saved_pc(tsk); 127 #endif 128 } else { 129 /* We don't want this function nor the caller */ 130 data.skip += 2; 131 frame.fp = (unsigned long)__builtin_frame_address(0); 132 frame.sp = current_stack_pointer; 133 frame.lr = (unsigned long)__builtin_return_address(0); 134 frame.pc = (unsigned long)__save_stack_trace; 135 } 136 137 walk_stackframe(&frame, save_trace, &data); 138 } 139 140 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 141 { 142 struct stack_trace_data data; 143 struct stackframe frame; 144 145 data.trace = trace; 146 data.skip = trace->skip; 147 data.no_sched_functions = 0; 148 149 frame.fp = regs->ARM_fp; 150 frame.sp = regs->ARM_sp; 151 frame.lr = regs->ARM_lr; 152 frame.pc = regs->ARM_pc; 153 154 walk_stackframe(&frame, save_trace, &data); 155 } 156 157 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 158 { 159 __save_stack_trace(tsk, trace, 1); 160 } 161 EXPORT_SYMBOL(save_stack_trace_tsk); 162 163 void save_stack_trace(struct stack_trace *trace) 164 { 165 __save_stack_trace(current, trace, 0); 166 } 167 EXPORT_SYMBOL_GPL(save_stack_trace); 168 #endif 169