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/sched.h> 21 #include <linux/stacktrace.h> 22 23 #include <asm/stacktrace.h> 24 25 /* 26 * AArch64 PCS assigns the frame pointer to x29. 27 * 28 * A simple function prologue looks like this: 29 * sub sp, sp, #0x10 30 * stp x29, x30, [sp] 31 * mov x29, sp 32 * 33 * A simple function epilogue looks like this: 34 * mov sp, x29 35 * ldp x29, x30, [sp] 36 * add sp, sp, #0x10 37 */ 38 int notrace unwind_frame(struct stackframe *frame) 39 { 40 unsigned long high, low; 41 unsigned long fp = frame->fp; 42 43 low = frame->sp; 44 high = ALIGN(low, THREAD_SIZE); 45 46 if (fp < low || fp > high - 0x18 || fp & 0xf) 47 return -EINVAL; 48 49 frame->sp = fp + 0x10; 50 frame->fp = *(unsigned long *)(fp); 51 /* 52 * -4 here because we care about the PC at time of bl, 53 * not where the return will go. 54 */ 55 frame->pc = *(unsigned long *)(fp + 8) - 4; 56 57 return 0; 58 } 59 60 void notrace walk_stackframe(struct stackframe *frame, 61 int (*fn)(struct stackframe *, void *), void *data) 62 { 63 while (1) { 64 int ret; 65 66 if (fn(frame, data)) 67 break; 68 ret = unwind_frame(frame); 69 if (ret < 0) 70 break; 71 } 72 } 73 EXPORT_SYMBOL(walk_stackframe); 74 75 #ifdef CONFIG_STACKTRACE 76 struct stack_trace_data { 77 struct stack_trace *trace; 78 unsigned int no_sched_functions; 79 unsigned int skip; 80 }; 81 82 static int save_trace(struct stackframe *frame, void *d) 83 { 84 struct stack_trace_data *data = d; 85 struct stack_trace *trace = data->trace; 86 unsigned long addr = frame->pc; 87 88 if (data->no_sched_functions && in_sched_functions(addr)) 89 return 0; 90 if (data->skip) { 91 data->skip--; 92 return 0; 93 } 94 95 trace->entries[trace->nr_entries++] = addr; 96 97 return trace->nr_entries >= trace->max_entries; 98 } 99 100 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 101 { 102 struct stack_trace_data data; 103 struct stackframe frame; 104 105 data.trace = trace; 106 data.skip = trace->skip; 107 108 if (tsk != current) { 109 data.no_sched_functions = 1; 110 frame.fp = thread_saved_fp(tsk); 111 frame.sp = thread_saved_sp(tsk); 112 frame.pc = thread_saved_pc(tsk); 113 } else { 114 register unsigned long current_sp asm("sp"); 115 data.no_sched_functions = 0; 116 frame.fp = (unsigned long)__builtin_frame_address(0); 117 frame.sp = current_sp; 118 frame.pc = (unsigned long)save_stack_trace_tsk; 119 } 120 121 walk_stackframe(&frame, save_trace, &data); 122 if (trace->nr_entries < trace->max_entries) 123 trace->entries[trace->nr_entries++] = ULONG_MAX; 124 } 125 126 void save_stack_trace(struct stack_trace *trace) 127 { 128 save_stack_trace_tsk(current, trace); 129 } 130 EXPORT_SYMBOL_GPL(save_stack_trace); 131 #endif 132