at v5.7 127 lines 3.1 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * linux/arch/unicore32/kernel/stacktrace.c 4 * 5 * Code specific to PKUnity SoC and UniCore ISA 6 * 7 * Copyright (C) 2001-2010 GUAN Xue-tao 8 */ 9#include <linux/module.h> 10#include <linux/sched.h> 11#include <linux/sched/debug.h> 12#include <linux/stacktrace.h> 13 14#include <asm/stacktrace.h> 15 16#if defined(CONFIG_FRAME_POINTER) 17/* 18 * Unwind the current stack frame and store the new register values in the 19 * structure passed as argument. Unwinding is equivalent to a function return, 20 * hence the new PC value rather than LR should be used for backtrace. 21 * 22 * With framepointer enabled, a simple function prologue looks like this: 23 * mov ip, sp 24 * stmdb sp!, {fp, ip, lr, pc} 25 * sub fp, ip, #4 26 * 27 * A simple function epilogue looks like this: 28 * ldm sp, {fp, sp, pc} 29 * 30 * Note that with framepointer enabled, even the leaf functions have the same 31 * prologue and epilogue, therefore we can ignore the LR value in this case. 32 */ 33int notrace unwind_frame(struct stackframe *frame) 34{ 35 unsigned long high, low; 36 unsigned long fp = frame->fp; 37 38 /* only go to a higher address on the stack */ 39 low = frame->sp; 40 high = ALIGN(low, THREAD_SIZE); 41 42 /* check current frame pointer is within bounds */ 43 if (fp < (low + 12) || fp + 4 >= high) 44 return -EINVAL; 45 46 /* restore the registers from the stack frame */ 47 frame->fp = *(unsigned long *)(fp - 12); 48 frame->sp = *(unsigned long *)(fp - 8); 49 frame->pc = *(unsigned long *)(fp - 4); 50 51 return 0; 52} 53#endif 54 55void notrace walk_stackframe(struct stackframe *frame, 56 int (*fn)(struct stackframe *, void *), void *data) 57{ 58 while (1) { 59 int ret; 60 61 if (fn(frame, data)) 62 break; 63 ret = unwind_frame(frame); 64 if (ret < 0) 65 break; 66 } 67} 68EXPORT_SYMBOL(walk_stackframe); 69 70#ifdef CONFIG_STACKTRACE 71struct stack_trace_data { 72 struct stack_trace *trace; 73 unsigned int no_sched_functions; 74 unsigned int skip; 75}; 76 77static int save_trace(struct stackframe *frame, void *d) 78{ 79 struct stack_trace_data *data = d; 80 struct stack_trace *trace = data->trace; 81 unsigned long addr = frame->pc; 82 83 if (data->no_sched_functions && in_sched_functions(addr)) 84 return 0; 85 if (data->skip) { 86 data->skip--; 87 return 0; 88 } 89 90 trace->entries[trace->nr_entries++] = addr; 91 92 return trace->nr_entries >= trace->max_entries; 93} 94 95void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 96{ 97 struct stack_trace_data data; 98 struct stackframe frame; 99 100 data.trace = trace; 101 data.skip = trace->skip; 102 103 if (tsk != current) { 104 data.no_sched_functions = 1; 105 frame.fp = thread_saved_fp(tsk); 106 frame.sp = thread_saved_sp(tsk); 107 frame.lr = 0; /* recovered from the stack */ 108 frame.pc = thread_saved_pc(tsk); 109 } else { 110 register unsigned long current_sp asm("sp"); 111 112 data.no_sched_functions = 0; 113 frame.fp = (unsigned long)__builtin_frame_address(0); 114 frame.sp = current_sp; 115 frame.lr = (unsigned long)__builtin_return_address(0); 116 frame.pc = (unsigned long)save_stack_trace_tsk; 117 } 118 119 walk_stackframe(&frame, save_trace, &data); 120} 121 122void save_stack_trace(struct stack_trace *trace) 123{ 124 save_stack_trace_tsk(current, trace); 125} 126EXPORT_SYMBOL_GPL(save_stack_trace); 127#endif