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