1eecac38bSStafford Horne /*
2eecac38bSStafford Horne  * Stack trace utility for OpenRISC
3eecac38bSStafford Horne  *
4eecac38bSStafford Horne  * Copyright (C) 2017 Stafford Horne <[email protected]>
5eecac38bSStafford Horne  *
6eecac38bSStafford Horne  * This file is licensed under the terms of the GNU General Public License
7eecac38bSStafford Horne  * version 2.  This program is licensed "as is" without any warranty of any
8eecac38bSStafford Horne  * kind, whether express or implied.
9eecac38bSStafford Horne  *
10eecac38bSStafford Horne  * Losely based on work from sh and powerpc.
11eecac38bSStafford Horne  */
12eecac38bSStafford Horne 
13eecac38bSStafford Horne #include <linux/export.h>
14eecac38bSStafford Horne #include <linux/sched.h>
15eecac38bSStafford Horne #include <linux/sched/debug.h>
16*57b8e277SStafford Horne #include <linux/sched/task_stack.h>
17eecac38bSStafford Horne #include <linux/stacktrace.h>
18eecac38bSStafford Horne 
19eecac38bSStafford Horne #include <asm/processor.h>
20eecac38bSStafford Horne #include <asm/unwinder.h>
21eecac38bSStafford Horne 
22eecac38bSStafford Horne /*
23eecac38bSStafford Horne  * Save stack-backtrace addresses into a stack_trace buffer.
24eecac38bSStafford Horne  */
25eecac38bSStafford Horne static void
save_stack_address(void * data,unsigned long addr,int reliable)26eecac38bSStafford Horne save_stack_address(void *data, unsigned long addr, int reliable)
27eecac38bSStafford Horne {
28eecac38bSStafford Horne 	struct stack_trace *trace = data;
29eecac38bSStafford Horne 
30eecac38bSStafford Horne 	if (!reliable)
31eecac38bSStafford Horne 		return;
32eecac38bSStafford Horne 
33eecac38bSStafford Horne 	if (trace->skip > 0) {
34eecac38bSStafford Horne 		trace->skip--;
35eecac38bSStafford Horne 		return;
36eecac38bSStafford Horne 	}
37eecac38bSStafford Horne 
38eecac38bSStafford Horne 	if (trace->nr_entries < trace->max_entries)
39eecac38bSStafford Horne 		trace->entries[trace->nr_entries++] = addr;
40eecac38bSStafford Horne }
41eecac38bSStafford Horne 
save_stack_trace(struct stack_trace * trace)42eecac38bSStafford Horne void save_stack_trace(struct stack_trace *trace)
43eecac38bSStafford Horne {
44eecac38bSStafford Horne 	unwind_stack(trace, (unsigned long *) &trace, save_stack_address);
45eecac38bSStafford Horne }
46eecac38bSStafford Horne EXPORT_SYMBOL_GPL(save_stack_trace);
47eecac38bSStafford Horne 
48eecac38bSStafford Horne static void
save_stack_address_nosched(void * data,unsigned long addr,int reliable)49eecac38bSStafford Horne save_stack_address_nosched(void *data, unsigned long addr, int reliable)
50eecac38bSStafford Horne {
51eecac38bSStafford Horne 	struct stack_trace *trace = (struct stack_trace *)data;
52eecac38bSStafford Horne 
53eecac38bSStafford Horne 	if (!reliable)
54eecac38bSStafford Horne 		return;
55eecac38bSStafford Horne 
56eecac38bSStafford Horne 	if (in_sched_functions(addr))
57eecac38bSStafford Horne 		return;
58eecac38bSStafford Horne 
59eecac38bSStafford Horne 	if (trace->skip > 0) {
60eecac38bSStafford Horne 		trace->skip--;
61eecac38bSStafford Horne 		return;
62eecac38bSStafford Horne 	}
63eecac38bSStafford Horne 
64eecac38bSStafford Horne 	if (trace->nr_entries < trace->max_entries)
65eecac38bSStafford Horne 		trace->entries[trace->nr_entries++] = addr;
66eecac38bSStafford Horne }
67eecac38bSStafford Horne 
save_stack_trace_tsk(struct task_struct * tsk,struct stack_trace * trace)68eecac38bSStafford Horne void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
69eecac38bSStafford Horne {
70eecac38bSStafford Horne 	unsigned long *sp = NULL;
71eecac38bSStafford Horne 
72*57b8e277SStafford Horne 	if (!try_get_task_stack(tsk))
73*57b8e277SStafford Horne 		return;
74*57b8e277SStafford Horne 
75eecac38bSStafford Horne 	if (tsk == current)
76eecac38bSStafford Horne 		sp = (unsigned long *) &sp;
77*57b8e277SStafford Horne 	else {
78*57b8e277SStafford Horne 		unsigned long ksp;
79*57b8e277SStafford Horne 
80*57b8e277SStafford Horne 		/* Locate stack from kernel context */
81*57b8e277SStafford Horne 		ksp = task_thread_info(tsk)->ksp;
82*57b8e277SStafford Horne 		ksp += STACK_FRAME_OVERHEAD;	/* redzone */
83*57b8e277SStafford Horne 		ksp += sizeof(struct pt_regs);
84*57b8e277SStafford Horne 
85*57b8e277SStafford Horne 		sp = (unsigned long *) ksp;
86*57b8e277SStafford Horne 	}
87eecac38bSStafford Horne 
88eecac38bSStafford Horne 	unwind_stack(trace, sp, save_stack_address_nosched);
89*57b8e277SStafford Horne 
90*57b8e277SStafford Horne 	put_task_stack(tsk);
91eecac38bSStafford Horne }
92eecac38bSStafford Horne EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
93eecac38bSStafford Horne 
94eecac38bSStafford Horne void
save_stack_trace_regs(struct pt_regs * regs,struct stack_trace * trace)95eecac38bSStafford Horne save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
96eecac38bSStafford Horne {
97eecac38bSStafford Horne 	unwind_stack(trace, (unsigned long *) regs->sp,
98eecac38bSStafford Horne 		     save_stack_address_nosched);
99eecac38bSStafford Horne }
100eecac38bSStafford Horne EXPORT_SYMBOL_GPL(save_stack_trace_regs);
101