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, NULL)) 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 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 60 if (tsk->ret_stack && 61 (frame->pc == (unsigned long)return_to_handler)) { 62 struct ftrace_ret_stack *ret_stack; 63 /* 64 * This is a case where function graph tracer has 65 * modified a return address (LR) in a stack frame 66 * to hook a function return. 67 * So replace it to an original value. 68 */ 69 ret_stack = ftrace_graph_get_ret_stack(tsk, frame->graph++); 70 if (WARN_ON_ONCE(!ret_stack)) 71 return -EINVAL; 72 frame->pc = ret_stack->ret; 73 } 74 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 75 76 /* 77 * Frames created upon entry from EL0 have NULL FP and PC values, so 78 * don't bother reporting these. Frames created by __noreturn functions 79 * might have a valid FP even if PC is bogus, so only terminate where 80 * both are NULL. 81 */ 82 if (!frame->fp && !frame->pc) 83 return -EINVAL; 84 85 return 0; 86 } 87 88 void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame, 89 int (*fn)(struct stackframe *, void *), void *data) 90 { 91 while (1) { 92 int ret; 93 94 if (fn(frame, data)) 95 break; 96 ret = unwind_frame(tsk, frame); 97 if (ret < 0) 98 break; 99 } 100 } 101 102 #ifdef CONFIG_STACKTRACE 103 struct stack_trace_data { 104 struct stack_trace *trace; 105 unsigned int no_sched_functions; 106 unsigned int skip; 107 }; 108 109 static int save_trace(struct stackframe *frame, void *d) 110 { 111 struct stack_trace_data *data = d; 112 struct stack_trace *trace = data->trace; 113 unsigned long addr = frame->pc; 114 115 if (data->no_sched_functions && in_sched_functions(addr)) 116 return 0; 117 if (data->skip) { 118 data->skip--; 119 return 0; 120 } 121 122 trace->entries[trace->nr_entries++] = addr; 123 124 return trace->nr_entries >= trace->max_entries; 125 } 126 127 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 128 { 129 struct stack_trace_data data; 130 struct stackframe frame; 131 132 data.trace = trace; 133 data.skip = trace->skip; 134 data.no_sched_functions = 0; 135 136 frame.fp = regs->regs[29]; 137 frame.pc = regs->pc; 138 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 139 frame.graph = 0; 140 #endif 141 142 walk_stackframe(current, &frame, save_trace, &data); 143 if (trace->nr_entries < trace->max_entries) 144 trace->entries[trace->nr_entries++] = ULONG_MAX; 145 } 146 147 static noinline void __save_stack_trace(struct task_struct *tsk, 148 struct stack_trace *trace, unsigned int nosched) 149 { 150 struct stack_trace_data data; 151 struct stackframe frame; 152 153 if (!try_get_task_stack(tsk)) 154 return; 155 156 data.trace = trace; 157 data.skip = trace->skip; 158 data.no_sched_functions = nosched; 159 160 if (tsk != current) { 161 frame.fp = thread_saved_fp(tsk); 162 frame.pc = thread_saved_pc(tsk); 163 } else { 164 /* We don't want this function nor the caller */ 165 data.skip += 2; 166 frame.fp = (unsigned long)__builtin_frame_address(0); 167 frame.pc = (unsigned long)__save_stack_trace; 168 } 169 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 170 frame.graph = 0; 171 #endif 172 173 walk_stackframe(tsk, &frame, save_trace, &data); 174 if (trace->nr_entries < trace->max_entries) 175 trace->entries[trace->nr_entries++] = ULONG_MAX; 176 177 put_task_stack(tsk); 178 } 179 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 180 181 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 182 { 183 __save_stack_trace(tsk, trace, 1); 184 } 185 186 void save_stack_trace(struct stack_trace *trace) 187 { 188 __save_stack_trace(current, trace, 0); 189 } 190 191 EXPORT_SYMBOL_GPL(save_stack_trace); 192 #endif 193