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