1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2ecea4ab6SPaul Gortmaker #include <linux/export.h> 3fed240d9SMasami Hiramatsu #include <linux/kprobes.h> 4f16fb1ecSRussell King #include <linux/sched.h> 5b17b0153SIngo Molnar #include <linux/sched/debug.h> 6f16fb1ecSRussell King #include <linux/stacktrace.h> 7f16fb1ecSRussell King 8c6089061SRussell King #include <asm/sections.h> 92d7c11bfSCatalin Marinas #include <asm/stacktrace.h> 1007b40341SRussell King #include <asm/traps.h> 11f16fb1ecSRussell King 12*5854e4d8SLi Huafei #include "reboot.h" 13*5854e4d8SLi Huafei 142d7c11bfSCatalin Marinas #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 152d7c11bfSCatalin Marinas /* 162d7c11bfSCatalin Marinas * Unwind the current stack frame and store the new register values in the 172d7c11bfSCatalin Marinas * structure passed as argument. Unwinding is equivalent to a function return, 182d7c11bfSCatalin Marinas * hence the new PC value rather than LR should be used for backtrace. 192d7c11bfSCatalin Marinas * 202d7c11bfSCatalin Marinas * With framepointer enabled, a simple function prologue looks like this: 212d7c11bfSCatalin Marinas * mov ip, sp 222d7c11bfSCatalin Marinas * stmdb sp!, {fp, ip, lr, pc} 232d7c11bfSCatalin Marinas * sub fp, ip, #4 242d7c11bfSCatalin Marinas * 252d7c11bfSCatalin Marinas * A simple function epilogue looks like this: 262d7c11bfSCatalin Marinas * ldm sp, {fp, sp, pc} 272d7c11bfSCatalin Marinas * 28b4d5ec9bSNathan Huckleberry * When compiled with clang, pc and sp are not pushed. A simple function 29b4d5ec9bSNathan Huckleberry * prologue looks like this when built with clang: 30b4d5ec9bSNathan Huckleberry * 31b4d5ec9bSNathan Huckleberry * stmdb {..., fp, lr} 32b4d5ec9bSNathan Huckleberry * add fp, sp, #x 33b4d5ec9bSNathan Huckleberry * sub sp, sp, #y 34b4d5ec9bSNathan Huckleberry * 35b4d5ec9bSNathan Huckleberry * A simple function epilogue looks like this when built with clang: 36b4d5ec9bSNathan Huckleberry * 37b4d5ec9bSNathan Huckleberry * sub sp, fp, #x 38b4d5ec9bSNathan Huckleberry * ldm {..., fp, pc} 39b4d5ec9bSNathan Huckleberry * 40b4d5ec9bSNathan Huckleberry * 412d7c11bfSCatalin Marinas * Note that with framepointer enabled, even the leaf functions have the same 422d7c11bfSCatalin Marinas * prologue and epilogue, therefore we can ignore the LR value in this case. 432d7c11bfSCatalin Marinas */ 44*5854e4d8SLi Huafei 45*5854e4d8SLi Huafei extern unsigned long call_with_stack_end; 46*5854e4d8SLi Huafei 47*5854e4d8SLi Huafei static int frame_pointer_check(struct stackframe *frame) 482d7c11bfSCatalin Marinas { 492d7c11bfSCatalin Marinas unsigned long high, low; 502d7c11bfSCatalin Marinas unsigned long fp = frame->fp; 51*5854e4d8SLi Huafei unsigned long pc = frame->pc; 52*5854e4d8SLi Huafei 53*5854e4d8SLi Huafei /* 54*5854e4d8SLi Huafei * call_with_stack() is the only place we allow SP to jump from one 55*5854e4d8SLi Huafei * stack to another, with FP and SP pointing to different stacks, 56*5854e4d8SLi Huafei * skipping the FP boundary check at this point. 57*5854e4d8SLi Huafei */ 58*5854e4d8SLi Huafei if (pc >= (unsigned long)&call_with_stack && 59*5854e4d8SLi Huafei pc < (unsigned long)&call_with_stack_end) 60*5854e4d8SLi Huafei return 0; 612d7c11bfSCatalin Marinas 622d7c11bfSCatalin Marinas /* only go to a higher address on the stack */ 632d7c11bfSCatalin Marinas low = frame->sp; 64d33aadbfSWill Deacon high = ALIGN(low, THREAD_SIZE); 652d7c11bfSCatalin Marinas 66b4d5ec9bSNathan Huckleberry /* check current frame pointer is within bounds */ 67*5854e4d8SLi Huafei #ifdef CONFIG_CC_IS_CLANG 68b4d5ec9bSNathan Huckleberry if (fp < low + 4 || fp > high - 4) 69b4d5ec9bSNathan Huckleberry return -EINVAL; 70*5854e4d8SLi Huafei #else 71*5854e4d8SLi Huafei if (fp < low + 12 || fp > high - 4) 72*5854e4d8SLi Huafei return -EINVAL; 73*5854e4d8SLi Huafei #endif 74b4d5ec9bSNathan Huckleberry 75*5854e4d8SLi Huafei return 0; 76*5854e4d8SLi Huafei } 77*5854e4d8SLi Huafei 78*5854e4d8SLi Huafei int notrace unwind_frame(struct stackframe *frame) 79*5854e4d8SLi Huafei { 80*5854e4d8SLi Huafei unsigned long fp = frame->fp; 81*5854e4d8SLi Huafei 82*5854e4d8SLi Huafei if (frame_pointer_check(frame)) 83*5854e4d8SLi Huafei return -EINVAL; 84*5854e4d8SLi Huafei 85*5854e4d8SLi Huafei /* restore the registers from the stack frame */ 86*5854e4d8SLi Huafei #ifdef CONFIG_CC_IS_CLANG 87b4d5ec9bSNathan Huckleberry frame->sp = frame->fp; 889be4c88bSlinyujun frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp)); 899be4c88bSlinyujun frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 4)); 90b4d5ec9bSNathan Huckleberry #else 919be4c88bSlinyujun frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 12)); 929be4c88bSlinyujun frame->sp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 8)); 939be4c88bSlinyujun frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 4)); 94b4d5ec9bSNathan Huckleberry #endif 95fed240d9SMasami Hiramatsu #ifdef CONFIG_KRETPROBES 96fed240d9SMasami Hiramatsu if (is_kretprobe_trampoline(frame->pc)) 97fed240d9SMasami Hiramatsu frame->pc = kretprobe_find_ret_addr(frame->tsk, 98fed240d9SMasami Hiramatsu (void *)frame->fp, &frame->kr_cur); 99fed240d9SMasami Hiramatsu #endif 1002d7c11bfSCatalin Marinas 1012d7c11bfSCatalin Marinas return 0; 1022d7c11bfSCatalin Marinas } 1032d7c11bfSCatalin Marinas #endif 1042d7c11bfSCatalin Marinas 1054bf1fa5aSUwe Kleine-König void notrace walk_stackframe(struct stackframe *frame, 106f16fb1ecSRussell King int (*fn)(struct stackframe *, void *), void *data) 107f16fb1ecSRussell King { 1082d7c11bfSCatalin Marinas while (1) { 1092d7c11bfSCatalin Marinas int ret; 110f16fb1ecSRussell King 111f16fb1ecSRussell King if (fn(frame, data)) 112f16fb1ecSRussell King break; 1132d7c11bfSCatalin Marinas ret = unwind_frame(frame); 1142d7c11bfSCatalin Marinas if (ret < 0) 1152d7c11bfSCatalin Marinas break; 1162d7c11bfSCatalin Marinas } 117f16fb1ecSRussell King } 1187b104bcbSAl Viro EXPORT_SYMBOL(walk_stackframe); 119f16fb1ecSRussell King 120f16fb1ecSRussell King #ifdef CONFIG_STACKTRACE 121f16fb1ecSRussell King struct stack_trace_data { 122f16fb1ecSRussell King struct stack_trace *trace; 123f76e9154SNicolas Pitre unsigned int no_sched_functions; 124f16fb1ecSRussell King unsigned int skip; 125f16fb1ecSRussell King }; 126f16fb1ecSRussell King 127f16fb1ecSRussell King static int save_trace(struct stackframe *frame, void *d) 128f16fb1ecSRussell King { 129f16fb1ecSRussell King struct stack_trace_data *data = d; 130f16fb1ecSRussell King struct stack_trace *trace = data->trace; 13107b40341SRussell King struct pt_regs *regs; 1322d7c11bfSCatalin Marinas unsigned long addr = frame->pc; 133f16fb1ecSRussell King 134f76e9154SNicolas Pitre if (data->no_sched_functions && in_sched_functions(addr)) 135f76e9154SNicolas Pitre return 0; 136f16fb1ecSRussell King if (data->skip) { 137f16fb1ecSRussell King data->skip--; 138f16fb1ecSRussell King return 0; 139f16fb1ecSRussell King } 140f16fb1ecSRussell King 141f76e9154SNicolas Pitre trace->entries[trace->nr_entries++] = addr; 142f16fb1ecSRussell King 14307b40341SRussell King if (trace->nr_entries >= trace->max_entries) 14407b40341SRussell King return 1; 14507b40341SRussell King 146c6089061SRussell King if (!in_entry_text(frame->pc)) 14707b40341SRussell King return 0; 14807b40341SRussell King 14907b40341SRussell King regs = (struct pt_regs *)frame->sp; 15040ff1ddbSVincent Whitchurch if ((unsigned long)®s[1] > ALIGN(frame->sp, THREAD_SIZE)) 15140ff1ddbSVincent Whitchurch return 0; 15207b40341SRussell King 15307b40341SRussell King trace->entries[trace->nr_entries++] = regs->ARM_pc; 15407b40341SRussell King 155f16fb1ecSRussell King return trace->nr_entries >= trace->max_entries; 156f16fb1ecSRussell King } 157f16fb1ecSRussell King 1583683f44cSRussell King /* This must be noinline to so that our skip calculation works correctly */ 1593683f44cSRussell King static noinline void __save_stack_trace(struct task_struct *tsk, 1603683f44cSRussell King struct stack_trace *trace, unsigned int nosched) 161f16fb1ecSRussell King { 162f16fb1ecSRussell King struct stack_trace_data data; 1632d7c11bfSCatalin Marinas struct stackframe frame; 164f16fb1ecSRussell King 165f16fb1ecSRussell King data.trace = trace; 166f16fb1ecSRussell King data.skip = trace->skip; 1673683f44cSRussell King data.no_sched_functions = nosched; 168f76e9154SNicolas Pitre 169f76e9154SNicolas Pitre if (tsk != current) { 170f76e9154SNicolas Pitre #ifdef CONFIG_SMP 171f76e9154SNicolas Pitre /* 172d5996b2fSRussell King * What guarantees do we have here that 'tsk' is not 173d5996b2fSRussell King * running on another CPU? For now, ignore it as we 174d5996b2fSRussell King * can't guarantee we won't explode. 175f76e9154SNicolas Pitre */ 176d5996b2fSRussell King return; 177f76e9154SNicolas Pitre #else 1782d7c11bfSCatalin Marinas frame.fp = thread_saved_fp(tsk); 1792d7c11bfSCatalin Marinas frame.sp = thread_saved_sp(tsk); 1802d7c11bfSCatalin Marinas frame.lr = 0; /* recovered from the stack */ 1812d7c11bfSCatalin Marinas frame.pc = thread_saved_pc(tsk); 182f76e9154SNicolas Pitre #endif 183f76e9154SNicolas Pitre } else { 1843683f44cSRussell King /* We don't want this function nor the caller */ 1853683f44cSRussell King data.skip += 2; 1862d7c11bfSCatalin Marinas frame.fp = (unsigned long)__builtin_frame_address(0); 18774dbeee0SBehan Webster frame.sp = current_stack_pointer; 1882d7c11bfSCatalin Marinas frame.lr = (unsigned long)__builtin_return_address(0); 189c46c2c9bSRussell King (Oracle) here: 190c46c2c9bSRussell King (Oracle) frame.pc = (unsigned long)&&here; 191f76e9154SNicolas Pitre } 192fed240d9SMasami Hiramatsu #ifdef CONFIG_KRETPROBES 193fed240d9SMasami Hiramatsu frame.kr_cur = NULL; 194fed240d9SMasami Hiramatsu frame.tsk = tsk; 195fed240d9SMasami Hiramatsu #endif 196f16fb1ecSRussell King 1972d7c11bfSCatalin Marinas walk_stackframe(&frame, save_trace, &data); 198f76e9154SNicolas Pitre } 199f76e9154SNicolas Pitre 2009c986661SLin Yongting void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 2019c986661SLin Yongting { 2029c986661SLin Yongting struct stack_trace_data data; 2039c986661SLin Yongting struct stackframe frame; 2049c986661SLin Yongting 2059c986661SLin Yongting data.trace = trace; 2069c986661SLin Yongting data.skip = trace->skip; 2079c986661SLin Yongting data.no_sched_functions = 0; 2089c986661SLin Yongting 2099c986661SLin Yongting frame.fp = regs->ARM_fp; 2109c986661SLin Yongting frame.sp = regs->ARM_sp; 2119c986661SLin Yongting frame.lr = regs->ARM_lr; 2129c986661SLin Yongting frame.pc = regs->ARM_pc; 213fed240d9SMasami Hiramatsu #ifdef CONFIG_KRETPROBES 214fed240d9SMasami Hiramatsu frame.kr_cur = NULL; 215fed240d9SMasami Hiramatsu frame.tsk = current; 216fed240d9SMasami Hiramatsu #endif 2179c986661SLin Yongting 2189c986661SLin Yongting walk_stackframe(&frame, save_trace, &data); 2199c986661SLin Yongting } 2209c986661SLin Yongting 2213683f44cSRussell King void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 2223683f44cSRussell King { 2233683f44cSRussell King __save_stack_trace(tsk, trace, 1); 2243683f44cSRussell King } 2259a3dc318SDustin Brown EXPORT_SYMBOL(save_stack_trace_tsk); 2263683f44cSRussell King 227f76e9154SNicolas Pitre void save_stack_trace(struct stack_trace *trace) 228f76e9154SNicolas Pitre { 2293683f44cSRussell King __save_stack_trace(current, trace, 0); 230f16fb1ecSRussell King } 2317b4c9505SIngo Molnar EXPORT_SYMBOL_GPL(save_stack_trace); 232f16fb1ecSRussell King #endif 233