1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Stack tracing support 4 * 5 * Copyright (C) 2012 ARM Ltd. 6 */ 7 #include <linux/kernel.h> 8 #include <linux/export.h> 9 #include <linux/ftrace.h> 10 #include <linux/sched.h> 11 #include <linux/sched/debug.h> 12 #include <linux/sched/task_stack.h> 13 #include <linux/stacktrace.h> 14 15 #include <asm/irq.h> 16 #include <asm/stack_pointer.h> 17 #include <asm/stacktrace.h> 18 19 /* 20 * AArch64 PCS assigns the frame pointer to x29. 21 * 22 * A simple function prologue looks like this: 23 * sub sp, sp, #0x10 24 * stp x29, x30, [sp] 25 * mov x29, sp 26 * 27 * A simple function epilogue looks like this: 28 * mov sp, x29 29 * ldp x29, x30, [sp] 30 * add sp, sp, #0x10 31 */ 32 int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) 33 { 34 unsigned long fp = frame->fp; 35 36 if (fp & 0xf) 37 return -EINVAL; 38 39 if (!tsk) 40 tsk = current; 41 42 if (!on_accessible_stack(tsk, fp, NULL)) 43 return -EINVAL; 44 45 frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp)); 46 frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8)); 47 48 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 49 if (tsk->ret_stack && 50 (frame->pc == (unsigned long)return_to_handler)) { 51 struct ftrace_ret_stack *ret_stack; 52 /* 53 * This is a case where function graph tracer has 54 * modified a return address (LR) in a stack frame 55 * to hook a function return. 56 * So replace it to an original value. 57 */ 58 ret_stack = ftrace_graph_get_ret_stack(tsk, frame->graph++); 59 if (WARN_ON_ONCE(!ret_stack)) 60 return -EINVAL; 61 frame->pc = ret_stack->ret; 62 } 63 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 64 65 /* 66 * Frames created upon entry from EL0 have NULL FP and PC values, so 67 * don't bother reporting these. Frames created by __noreturn functions 68 * might have a valid FP even if PC is bogus, so only terminate where 69 * both are NULL. 70 */ 71 if (!frame->fp && !frame->pc) 72 return -EINVAL; 73 74 return 0; 75 } 76 77 void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame, 78 int (*fn)(struct stackframe *, void *), void *data) 79 { 80 while (1) { 81 int ret; 82 83 if (fn(frame, data)) 84 break; 85 ret = unwind_frame(tsk, frame); 86 if (ret < 0) 87 break; 88 } 89 } 90 91 #ifdef CONFIG_STACKTRACE 92 struct stack_trace_data { 93 struct stack_trace *trace; 94 unsigned int no_sched_functions; 95 unsigned int skip; 96 }; 97 98 static int save_trace(struct stackframe *frame, void *d) 99 { 100 struct stack_trace_data *data = d; 101 struct stack_trace *trace = data->trace; 102 unsigned long addr = frame->pc; 103 104 if (data->no_sched_functions && in_sched_functions(addr)) 105 return 0; 106 if (data->skip) { 107 data->skip--; 108 return 0; 109 } 110 111 trace->entries[trace->nr_entries++] = addr; 112 113 return trace->nr_entries >= trace->max_entries; 114 } 115 116 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 117 { 118 struct stack_trace_data data; 119 struct stackframe frame; 120 121 data.trace = trace; 122 data.skip = trace->skip; 123 data.no_sched_functions = 0; 124 125 frame.fp = regs->regs[29]; 126 frame.pc = regs->pc; 127 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 128 frame.graph = 0; 129 #endif 130 131 walk_stackframe(current, &frame, save_trace, &data); 132 } 133 EXPORT_SYMBOL_GPL(save_stack_trace_regs); 134 135 static noinline void __save_stack_trace(struct task_struct *tsk, 136 struct stack_trace *trace, unsigned int nosched) 137 { 138 struct stack_trace_data data; 139 struct stackframe frame; 140 141 if (!try_get_task_stack(tsk)) 142 return; 143 144 data.trace = trace; 145 data.skip = trace->skip; 146 data.no_sched_functions = nosched; 147 148 if (tsk != current) { 149 frame.fp = thread_saved_fp(tsk); 150 frame.pc = thread_saved_pc(tsk); 151 } else { 152 /* We don't want this function nor the caller */ 153 data.skip += 2; 154 frame.fp = (unsigned long)__builtin_frame_address(0); 155 frame.pc = (unsigned long)__save_stack_trace; 156 } 157 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 158 frame.graph = 0; 159 #endif 160 161 walk_stackframe(tsk, &frame, save_trace, &data); 162 163 put_task_stack(tsk); 164 } 165 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 166 167 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 168 { 169 __save_stack_trace(tsk, trace, 1); 170 } 171 172 void save_stack_trace(struct stack_trace *trace) 173 { 174 __save_stack_trace(current, trace, 0); 175 } 176 177 EXPORT_SYMBOL_GPL(save_stack_trace); 178 #endif 179