1ecea4ab6SPaul Gortmaker #include <linux/export.h> 2f16fb1ecSRussell King #include <linux/sched.h> 3f16fb1ecSRussell King #include <linux/stacktrace.h> 4f16fb1ecSRussell King 52d7c11bfSCatalin Marinas #include <asm/stacktrace.h> 6*07b40341SRussell King #include <asm/traps.h> 7f16fb1ecSRussell King 82d7c11bfSCatalin Marinas #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 92d7c11bfSCatalin Marinas /* 102d7c11bfSCatalin Marinas * Unwind the current stack frame and store the new register values in the 112d7c11bfSCatalin Marinas * structure passed as argument. Unwinding is equivalent to a function return, 122d7c11bfSCatalin Marinas * hence the new PC value rather than LR should be used for backtrace. 132d7c11bfSCatalin Marinas * 142d7c11bfSCatalin Marinas * With framepointer enabled, a simple function prologue looks like this: 152d7c11bfSCatalin Marinas * mov ip, sp 162d7c11bfSCatalin Marinas * stmdb sp!, {fp, ip, lr, pc} 172d7c11bfSCatalin Marinas * sub fp, ip, #4 182d7c11bfSCatalin Marinas * 192d7c11bfSCatalin Marinas * A simple function epilogue looks like this: 202d7c11bfSCatalin Marinas * ldm sp, {fp, sp, pc} 212d7c11bfSCatalin Marinas * 222d7c11bfSCatalin Marinas * Note that with framepointer enabled, even the leaf functions have the same 232d7c11bfSCatalin Marinas * prologue and epilogue, therefore we can ignore the LR value in this case. 242d7c11bfSCatalin Marinas */ 254bf1fa5aSUwe Kleine-König int notrace unwind_frame(struct stackframe *frame) 262d7c11bfSCatalin Marinas { 272d7c11bfSCatalin Marinas unsigned long high, low; 282d7c11bfSCatalin Marinas unsigned long fp = frame->fp; 292d7c11bfSCatalin Marinas 302d7c11bfSCatalin Marinas /* only go to a higher address on the stack */ 312d7c11bfSCatalin Marinas low = frame->sp; 32d33aadbfSWill Deacon high = ALIGN(low, THREAD_SIZE); 332d7c11bfSCatalin Marinas 342d7c11bfSCatalin Marinas /* check current frame pointer is within bounds */ 353abb6671SKonstantin Khlebnikov if (fp < low + 12 || fp > high - 4) 362d7c11bfSCatalin Marinas return -EINVAL; 372d7c11bfSCatalin Marinas 382d7c11bfSCatalin Marinas /* restore the registers from the stack frame */ 392d7c11bfSCatalin Marinas frame->fp = *(unsigned long *)(fp - 12); 402d7c11bfSCatalin Marinas frame->sp = *(unsigned long *)(fp - 8); 412d7c11bfSCatalin Marinas frame->pc = *(unsigned long *)(fp - 4); 422d7c11bfSCatalin Marinas 432d7c11bfSCatalin Marinas return 0; 442d7c11bfSCatalin Marinas } 452d7c11bfSCatalin Marinas #endif 462d7c11bfSCatalin Marinas 474bf1fa5aSUwe Kleine-König void notrace walk_stackframe(struct stackframe *frame, 48f16fb1ecSRussell King int (*fn)(struct stackframe *, void *), void *data) 49f16fb1ecSRussell King { 502d7c11bfSCatalin Marinas while (1) { 512d7c11bfSCatalin Marinas int ret; 52f16fb1ecSRussell King 53f16fb1ecSRussell King if (fn(frame, data)) 54f16fb1ecSRussell King break; 552d7c11bfSCatalin Marinas ret = unwind_frame(frame); 562d7c11bfSCatalin Marinas if (ret < 0) 572d7c11bfSCatalin Marinas break; 582d7c11bfSCatalin Marinas } 59f16fb1ecSRussell King } 607b104bcbSAl Viro EXPORT_SYMBOL(walk_stackframe); 61f16fb1ecSRussell King 62f16fb1ecSRussell King #ifdef CONFIG_STACKTRACE 63f16fb1ecSRussell King struct stack_trace_data { 64f16fb1ecSRussell King struct stack_trace *trace; 65*07b40341SRussell King unsigned long last_pc; 66f76e9154SNicolas Pitre unsigned int no_sched_functions; 67f16fb1ecSRussell King unsigned int skip; 68f16fb1ecSRussell King }; 69f16fb1ecSRussell King 70f16fb1ecSRussell King static int save_trace(struct stackframe *frame, void *d) 71f16fb1ecSRussell King { 72f16fb1ecSRussell King struct stack_trace_data *data = d; 73f16fb1ecSRussell King struct stack_trace *trace = data->trace; 74*07b40341SRussell King struct pt_regs *regs; 752d7c11bfSCatalin Marinas unsigned long addr = frame->pc; 76f16fb1ecSRussell King 77f76e9154SNicolas Pitre if (data->no_sched_functions && in_sched_functions(addr)) 78f76e9154SNicolas Pitre return 0; 79f16fb1ecSRussell King if (data->skip) { 80f16fb1ecSRussell King data->skip--; 81f16fb1ecSRussell King return 0; 82f16fb1ecSRussell King } 83f16fb1ecSRussell King 84f76e9154SNicolas Pitre trace->entries[trace->nr_entries++] = addr; 85f16fb1ecSRussell King 86*07b40341SRussell King if (trace->nr_entries >= trace->max_entries) 87*07b40341SRussell King return 1; 88*07b40341SRussell King 89*07b40341SRussell King /* 90*07b40341SRussell King * in_exception_text() is designed to test if the PC is one of 91*07b40341SRussell King * the functions which has an exception stack above it, but 92*07b40341SRussell King * unfortunately what is in frame->pc is the return LR value, 93*07b40341SRussell King * not the saved PC value. So, we need to track the previous 94*07b40341SRussell King * frame PC value when doing this. 95*07b40341SRussell King */ 96*07b40341SRussell King addr = data->last_pc; 97*07b40341SRussell King data->last_pc = frame->pc; 98*07b40341SRussell King if (!in_exception_text(addr)) 99*07b40341SRussell King return 0; 100*07b40341SRussell King 101*07b40341SRussell King regs = (struct pt_regs *)frame->sp; 102*07b40341SRussell King 103*07b40341SRussell King trace->entries[trace->nr_entries++] = regs->ARM_pc; 104*07b40341SRussell King 105f16fb1ecSRussell King return trace->nr_entries >= trace->max_entries; 106f16fb1ecSRussell King } 107f16fb1ecSRussell King 1083683f44cSRussell King /* This must be noinline to so that our skip calculation works correctly */ 1093683f44cSRussell King static noinline void __save_stack_trace(struct task_struct *tsk, 1103683f44cSRussell King struct stack_trace *trace, unsigned int nosched) 111f16fb1ecSRussell King { 112f16fb1ecSRussell King struct stack_trace_data data; 1132d7c11bfSCatalin Marinas struct stackframe frame; 114f16fb1ecSRussell King 115f16fb1ecSRussell King data.trace = trace; 116*07b40341SRussell King data.last_pc = ULONG_MAX; 117f16fb1ecSRussell King data.skip = trace->skip; 1183683f44cSRussell King data.no_sched_functions = nosched; 119f76e9154SNicolas Pitre 120f76e9154SNicolas Pitre if (tsk != current) { 121f76e9154SNicolas Pitre #ifdef CONFIG_SMP 122f76e9154SNicolas Pitre /* 123d5996b2fSRussell King * What guarantees do we have here that 'tsk' is not 124d5996b2fSRussell King * running on another CPU? For now, ignore it as we 125d5996b2fSRussell King * can't guarantee we won't explode. 126f76e9154SNicolas Pitre */ 127d5996b2fSRussell King if (trace->nr_entries < trace->max_entries) 128d5996b2fSRussell King trace->entries[trace->nr_entries++] = ULONG_MAX; 129d5996b2fSRussell King return; 130f76e9154SNicolas Pitre #else 1312d7c11bfSCatalin Marinas frame.fp = thread_saved_fp(tsk); 1322d7c11bfSCatalin Marinas frame.sp = thread_saved_sp(tsk); 1332d7c11bfSCatalin Marinas frame.lr = 0; /* recovered from the stack */ 1342d7c11bfSCatalin Marinas frame.pc = thread_saved_pc(tsk); 135f76e9154SNicolas Pitre #endif 136f76e9154SNicolas Pitre } else { 1372d7c11bfSCatalin Marinas register unsigned long current_sp asm ("sp"); 1382d7c11bfSCatalin Marinas 1393683f44cSRussell King /* We don't want this function nor the caller */ 1403683f44cSRussell King data.skip += 2; 1412d7c11bfSCatalin Marinas frame.fp = (unsigned long)__builtin_frame_address(0); 1422d7c11bfSCatalin Marinas frame.sp = current_sp; 1432d7c11bfSCatalin Marinas frame.lr = (unsigned long)__builtin_return_address(0); 1443683f44cSRussell King frame.pc = (unsigned long)__save_stack_trace; 145f76e9154SNicolas Pitre } 146f16fb1ecSRussell King 1472d7c11bfSCatalin Marinas walk_stackframe(&frame, save_trace, &data); 148f76e9154SNicolas Pitre if (trace->nr_entries < trace->max_entries) 149f76e9154SNicolas Pitre trace->entries[trace->nr_entries++] = ULONG_MAX; 150f76e9154SNicolas Pitre } 151f76e9154SNicolas Pitre 1523683f44cSRussell King void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 1533683f44cSRussell King { 1543683f44cSRussell King __save_stack_trace(tsk, trace, 1); 1553683f44cSRussell King } 1563683f44cSRussell King 157f76e9154SNicolas Pitre void save_stack_trace(struct stack_trace *trace) 158f76e9154SNicolas Pitre { 1593683f44cSRussell King __save_stack_trace(current, trace, 0); 160f16fb1ecSRussell King } 1617b4c9505SIngo Molnar EXPORT_SYMBOL_GPL(save_stack_trace); 162f16fb1ecSRussell King #endif 163