162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#include <linux/export.h> 362306a36Sopenharmony_ci#include <linux/kprobes.h> 462306a36Sopenharmony_ci#include <linux/sched.h> 562306a36Sopenharmony_ci#include <linux/sched/debug.h> 662306a36Sopenharmony_ci#include <linux/stacktrace.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/sections.h> 962306a36Sopenharmony_ci#include <asm/stacktrace.h> 1062306a36Sopenharmony_ci#include <asm/traps.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "reboot.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * Unwind the current stack frame and store the new register values in the 1762306a36Sopenharmony_ci * structure passed as argument. Unwinding is equivalent to a function return, 1862306a36Sopenharmony_ci * hence the new PC value rather than LR should be used for backtrace. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * With framepointer enabled, a simple function prologue looks like this: 2162306a36Sopenharmony_ci * mov ip, sp 2262306a36Sopenharmony_ci * stmdb sp!, {fp, ip, lr, pc} 2362306a36Sopenharmony_ci * sub fp, ip, #4 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * A simple function epilogue looks like this: 2662306a36Sopenharmony_ci * ldm sp, {fp, sp, pc} 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * When compiled with clang, pc and sp are not pushed. A simple function 2962306a36Sopenharmony_ci * prologue looks like this when built with clang: 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * stmdb {..., fp, lr} 3262306a36Sopenharmony_ci * add fp, sp, #x 3362306a36Sopenharmony_ci * sub sp, sp, #y 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * A simple function epilogue looks like this when built with clang: 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * sub sp, fp, #x 3862306a36Sopenharmony_ci * ldm {..., fp, pc} 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Note that with framepointer enabled, even the leaf functions have the same 4262306a36Sopenharmony_ci * prologue and epilogue, therefore we can ignore the LR value in this case. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciextern unsigned long call_with_stack_end; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int frame_pointer_check(struct stackframe *frame) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci unsigned long high, low; 5062306a36Sopenharmony_ci unsigned long fp = frame->fp; 5162306a36Sopenharmony_ci unsigned long pc = frame->pc; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * call_with_stack() is the only place we allow SP to jump from one 5562306a36Sopenharmony_ci * stack to another, with FP and SP pointing to different stacks, 5662306a36Sopenharmony_ci * skipping the FP boundary check at this point. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci if (pc >= (unsigned long)&call_with_stack && 5962306a36Sopenharmony_ci pc < (unsigned long)&call_with_stack_end) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* only go to a higher address on the stack */ 6362306a36Sopenharmony_ci low = frame->sp; 6462306a36Sopenharmony_ci high = ALIGN(low, THREAD_SIZE); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* check current frame pointer is within bounds */ 6762306a36Sopenharmony_ci#ifdef CONFIG_CC_IS_CLANG 6862306a36Sopenharmony_ci if (fp < low + 4 || fp > high - 4) 6962306a36Sopenharmony_ci return -EINVAL; 7062306a36Sopenharmony_ci#else 7162306a36Sopenharmony_ci if (fp < low + 12 || fp > high - 4) 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci#endif 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciint notrace unwind_frame(struct stackframe *frame) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned long fp = frame->fp; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (frame_pointer_check(frame)) 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * When we unwind through an exception stack, include the saved PC 8762306a36Sopenharmony_ci * value into the stack trace. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (frame->ex_frame) { 9062306a36Sopenharmony_ci struct pt_regs *regs = (struct pt_regs *)frame->sp; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * We check that 'regs + sizeof(struct pt_regs)' (that is, 9462306a36Sopenharmony_ci * ®s[1]) does not exceed the bottom of the stack to avoid 9562306a36Sopenharmony_ci * accessing data outside the task's stack. This may happen 9662306a36Sopenharmony_ci * when frame->ex_frame is a false positive. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci if ((unsigned long)®s[1] > ALIGN(frame->sp, THREAD_SIZE)) 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci frame->pc = regs->ARM_pc; 10262306a36Sopenharmony_ci frame->ex_frame = false; 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* restore the registers from the stack frame */ 10762306a36Sopenharmony_ci#ifdef CONFIG_CC_IS_CLANG 10862306a36Sopenharmony_ci frame->sp = frame->fp; 10962306a36Sopenharmony_ci frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp)); 11062306a36Sopenharmony_ci frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 4)); 11162306a36Sopenharmony_ci#else 11262306a36Sopenharmony_ci frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 12)); 11362306a36Sopenharmony_ci frame->sp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 8)); 11462306a36Sopenharmony_ci frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 4)); 11562306a36Sopenharmony_ci#endif 11662306a36Sopenharmony_ci#ifdef CONFIG_KRETPROBES 11762306a36Sopenharmony_ci if (is_kretprobe_trampoline(frame->pc)) 11862306a36Sopenharmony_ci frame->pc = kretprobe_find_ret_addr(frame->tsk, 11962306a36Sopenharmony_ci (void *)frame->fp, &frame->kr_cur); 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (in_entry_text(frame->pc)) 12362306a36Sopenharmony_ci frame->ex_frame = true; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci#endif 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_civoid notrace walk_stackframe(struct stackframe *frame, 13062306a36Sopenharmony_ci bool (*fn)(void *, unsigned long), void *data) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci while (1) { 13362306a36Sopenharmony_ci int ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!fn(data, frame->pc)) 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci ret = unwind_frame(frame); 13862306a36Sopenharmony_ci if (ret < 0) 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ciEXPORT_SYMBOL(walk_stackframe); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#ifdef CONFIG_STACKTRACE 14562306a36Sopenharmony_cistatic void start_stack_trace(struct stackframe *frame, struct task_struct *task, 14662306a36Sopenharmony_ci unsigned long fp, unsigned long sp, 14762306a36Sopenharmony_ci unsigned long lr, unsigned long pc) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci frame->fp = fp; 15062306a36Sopenharmony_ci frame->sp = sp; 15162306a36Sopenharmony_ci frame->lr = lr; 15262306a36Sopenharmony_ci frame->pc = pc; 15362306a36Sopenharmony_ci#ifdef CONFIG_KRETPROBES 15462306a36Sopenharmony_ci frame->kr_cur = NULL; 15562306a36Sopenharmony_ci frame->tsk = task; 15662306a36Sopenharmony_ci#endif 15762306a36Sopenharmony_ci#ifdef CONFIG_UNWINDER_FRAME_POINTER 15862306a36Sopenharmony_ci frame->ex_frame = in_entry_text(frame->pc); 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_civoid arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, 16362306a36Sopenharmony_ci struct task_struct *task, struct pt_regs *regs) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct stackframe frame; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (regs) { 16862306a36Sopenharmony_ci start_stack_trace(&frame, NULL, regs->ARM_fp, regs->ARM_sp, 16962306a36Sopenharmony_ci regs->ARM_lr, regs->ARM_pc); 17062306a36Sopenharmony_ci } else if (task != current) { 17162306a36Sopenharmony_ci#ifdef CONFIG_SMP 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * What guarantees do we have here that 'tsk' is not 17462306a36Sopenharmony_ci * running on another CPU? For now, ignore it as we 17562306a36Sopenharmony_ci * can't guarantee we won't explode. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci#else 17962306a36Sopenharmony_ci start_stack_trace(&frame, task, thread_saved_fp(task), 18062306a36Sopenharmony_ci thread_saved_sp(task), 0, 18162306a36Sopenharmony_ci thread_saved_pc(task)); 18262306a36Sopenharmony_ci#endif 18362306a36Sopenharmony_ci } else { 18462306a36Sopenharmony_cihere: 18562306a36Sopenharmony_ci start_stack_trace(&frame, task, 18662306a36Sopenharmony_ci (unsigned long)__builtin_frame_address(0), 18762306a36Sopenharmony_ci current_stack_pointer, 18862306a36Sopenharmony_ci (unsigned long)__builtin_return_address(0), 18962306a36Sopenharmony_ci (unsigned long)&&here); 19062306a36Sopenharmony_ci /* skip this function */ 19162306a36Sopenharmony_ci if (unwind_frame(&frame)) 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci walk_stackframe(&frame, consume_entry, cookie); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci#endif 198