|  | /* | 
|  | * 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 |