/* * arch/xtensa/kernel/stacktrace.c * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2001 - 2013 Tensilica Inc. */ #include <linux/export.h> #include <linux/sched.h> #include <linux/stacktrace.h> #include <asm/stacktrace.h> #include <asm/traps.h> void walk_stackframe(unsigned long *sp, int (*fn)(struct stackframe *frame, void *data), void *data) { unsigned long a0, a1; unsigned long sp_end; a1 = (unsigned long)sp; sp_end = ALIGN(a1, THREAD_SIZE); spill_registers(); while (a1 < sp_end) { struct stackframe frame; sp = (unsigned long *)a1; a0 = *(sp - 4); a1 = *(sp - 3); if (a1 <= (unsigned long)sp) break; frame.pc = MAKE_PC_FROM_RA(a0, a1); frame.sp = a1; if (fn(&frame, data)) return; } } #ifdef CONFIG_STACKTRACE struct stack_trace_data { struct stack_trace *trace; unsigned skip; }; static int stack_trace_cb(struct stackframe *frame, void *data) { struct stack_trace_data *trace_data = data; struct stack_trace *trace = trace_data->trace; if (trace_data->skip) { --trace_data->skip; return 0; } if (!kernel_text_address(frame->pc)) return 0; trace->entries[trace->nr_entries++] = frame->pc; return trace->nr_entries >= trace->max_entries; } void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace) { struct stack_trace_data trace_data = { .trace = trace, .skip = trace->skip, }; walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data); } EXPORT_SYMBOL_GPL(save_stack_trace_tsk); void save_stack_trace(struct stack_trace *trace) { save_stack_trace_tsk(current, trace); } EXPORT_SYMBOL_GPL(save_stack_trace); #endif #ifdef CONFIG_FRAME_POINTER struct return_addr_data { unsigned long addr; unsigned skip; }; static int return_address_cb(struct stackframe *frame, void *data) { struct return_addr_data *r = data; if (r->skip) { --r->skip; return 0; } if (!kernel_text_address(frame->pc)) return 0; r->addr = frame->pc; return 1; } unsigned long return_address(unsigned level) { struct return_addr_data r = { .skip = level + 1, }; walk_stackframe(stack_pointer(NULL), return_address_cb, &r); return r.addr; } EXPORT_SYMBOL(return_address); #endif