1/* 2 * arch/xtensa/kernel/stacktrace.c 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file "COPYING" in the main directory of this archive 6 * for more details. 7 * 8 * Copyright (C) 2001 - 2013 Tensilica Inc. 9 */ 10#include <linux/export.h> 11#include <linux/sched.h> 12#include <linux/stacktrace.h> 13 14#include <asm/stacktrace.h> 15#include <asm/traps.h> 16 17void walk_stackframe(unsigned long *sp, 18 int (*fn)(struct stackframe *frame, void *data), 19 void *data) 20{ 21 unsigned long a0, a1; 22 unsigned long sp_end; 23 24 a1 = (unsigned long)sp; 25 sp_end = ALIGN(a1, THREAD_SIZE); 26 27 spill_registers(); 28 29 while (a1 < sp_end) { 30 struct stackframe frame; 31 32 sp = (unsigned long *)a1; 33 34 a0 = *(sp - 4); 35 a1 = *(sp - 3); 36 37 if (a1 <= (unsigned long)sp) 38 break; 39 40 frame.pc = MAKE_PC_FROM_RA(a0, a1); 41 frame.sp = a1; 42 43 if (fn(&frame, data)) 44 return; 45 } 46} 47 48#ifdef CONFIG_STACKTRACE 49 50struct stack_trace_data { 51 struct stack_trace *trace; 52 unsigned skip; 53}; 54 55static int stack_trace_cb(struct stackframe *frame, void *data) 56{ 57 struct stack_trace_data *trace_data = data; 58 struct stack_trace *trace = trace_data->trace; 59 60 if (trace_data->skip) { 61 --trace_data->skip; 62 return 0; 63 } 64 if (!kernel_text_address(frame->pc)) 65 return 0; 66 67 trace->entries[trace->nr_entries++] = frame->pc; 68 return trace->nr_entries >= trace->max_entries; 69} 70 71void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace) 72{ 73 struct stack_trace_data trace_data = { 74 .trace = trace, 75 .skip = trace->skip, 76 }; 77 walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data); 78} 79EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 80 81void save_stack_trace(struct stack_trace *trace) 82{ 83 save_stack_trace_tsk(current, trace); 84} 85EXPORT_SYMBOL_GPL(save_stack_trace); 86 87#endif 88 89#ifdef CONFIG_FRAME_POINTER 90 91struct return_addr_data { 92 unsigned long addr; 93 unsigned skip; 94}; 95 96static int return_address_cb(struct stackframe *frame, void *data) 97{ 98 struct return_addr_data *r = data; 99 100 if (r->skip) { 101 --r->skip; 102 return 0; 103 } 104 if (!kernel_text_address(frame->pc)) 105 return 0; 106 r->addr = frame->pc; 107 return 1; 108} 109 110unsigned long return_address(unsigned level) 111{ 112 struct return_addr_data r = { 113 .skip = level + 1, 114 }; 115 walk_stackframe(stack_pointer(NULL), return_address_cb, &r); 116 return r.addr; 117} 118EXPORT_SYMBOL(return_address); 119 120#endif 121