xref: /openbmc/linux/arch/csky/kernel/stacktrace.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
10ea2dc7cSGuo Ren // SPDX-License-Identifier: GPL-2.0
20ea2dc7cSGuo Ren 
30ea2dc7cSGuo Ren #include <linux/sched/debug.h>
40ea2dc7cSGuo Ren #include <linux/sched/task_stack.h>
50ea2dc7cSGuo Ren #include <linux/stacktrace.h>
60ea2dc7cSGuo Ren #include <linux/ftrace.h>
718c07d23SGuo Ren #include <linux/ptrace.h>
818c07d23SGuo Ren 
918c07d23SGuo Ren #ifdef CONFIG_FRAME_POINTER
1018c07d23SGuo Ren 
1118c07d23SGuo Ren struct stackframe {
1218c07d23SGuo Ren 	unsigned long fp;
1318c07d23SGuo Ren 	unsigned long ra;
1418c07d23SGuo Ren };
1518c07d23SGuo Ren 
walk_stackframe(struct task_struct * task,struct pt_regs * regs,bool (* fn)(unsigned long,void *),void * arg)1618c07d23SGuo Ren void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
1718c07d23SGuo Ren 			     bool (*fn)(unsigned long, void *), void *arg)
1818c07d23SGuo Ren {
1918c07d23SGuo Ren 	unsigned long fp, sp, pc;
2018c07d23SGuo Ren 
2118c07d23SGuo Ren 	if (regs) {
2218c07d23SGuo Ren 		fp = frame_pointer(regs);
2318c07d23SGuo Ren 		sp = user_stack_pointer(regs);
2418c07d23SGuo Ren 		pc = instruction_pointer(regs);
2518c07d23SGuo Ren 	} else if (task == NULL || task == current) {
2618c07d23SGuo Ren 		const register unsigned long current_fp __asm__ ("r8");
2718c07d23SGuo Ren 		fp = current_fp;
28*b203c67eSTong Tiangen 		sp = current_stack_pointer;
2918c07d23SGuo Ren 		pc = (unsigned long)walk_stackframe;
3018c07d23SGuo Ren 	} else {
3118c07d23SGuo Ren 		/* task blocked in __switch_to */
3218c07d23SGuo Ren 		fp = thread_saved_fp(task);
3318c07d23SGuo Ren 		sp = thread_saved_sp(task);
3418c07d23SGuo Ren 		pc = thread_saved_lr(task);
3518c07d23SGuo Ren 	}
3618c07d23SGuo Ren 
3718c07d23SGuo Ren 	for (;;) {
3818c07d23SGuo Ren 		unsigned long low, high;
3918c07d23SGuo Ren 		struct stackframe *frame;
4018c07d23SGuo Ren 
4118c07d23SGuo Ren 		if (unlikely(!__kernel_text_address(pc) || fn(pc, arg)))
4218c07d23SGuo Ren 			break;
4318c07d23SGuo Ren 
4418c07d23SGuo Ren 		/* Validate frame pointer */
4518c07d23SGuo Ren 		low = sp;
4618c07d23SGuo Ren 		high = ALIGN(sp, THREAD_SIZE);
4718c07d23SGuo Ren 		if (unlikely(fp < low || fp > high || fp & 0x3))
4818c07d23SGuo Ren 			break;
4918c07d23SGuo Ren 		/* Unwind stack frame */
5018c07d23SGuo Ren 		frame = (struct stackframe *)fp;
5118c07d23SGuo Ren 		sp = fp;
5218c07d23SGuo Ren 		fp = frame->fp;
5318c07d23SGuo Ren 		pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
5418c07d23SGuo Ren 					   (unsigned long *)(fp - 8));
5518c07d23SGuo Ren 	}
5618c07d23SGuo Ren }
5718c07d23SGuo Ren 
5818c07d23SGuo Ren #else /* !CONFIG_FRAME_POINTER */
5918c07d23SGuo Ren 
walk_stackframe(struct task_struct * task,struct pt_regs * regs,bool (* fn)(unsigned long,void *),void * arg)6018c07d23SGuo Ren static void notrace walk_stackframe(struct task_struct *task,
6118c07d23SGuo Ren 	struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
6218c07d23SGuo Ren {
6318c07d23SGuo Ren 	unsigned long sp, pc;
6418c07d23SGuo Ren 	unsigned long *ksp;
6518c07d23SGuo Ren 
6618c07d23SGuo Ren 	if (regs) {
6718c07d23SGuo Ren 		sp = user_stack_pointer(regs);
6818c07d23SGuo Ren 		pc = instruction_pointer(regs);
6918c07d23SGuo Ren 	} else if (task == NULL || task == current) {
70*b203c67eSTong Tiangen 		sp = current_stack_pointer;
7118c07d23SGuo Ren 		pc = (unsigned long)walk_stackframe;
7218c07d23SGuo Ren 	} else {
7318c07d23SGuo Ren 		/* task blocked in __switch_to */
7418c07d23SGuo Ren 		sp = thread_saved_sp(task);
7518c07d23SGuo Ren 		pc = thread_saved_lr(task);
7618c07d23SGuo Ren 	}
7718c07d23SGuo Ren 
7818c07d23SGuo Ren 	if (unlikely(sp & 0x3))
7918c07d23SGuo Ren 		return;
8018c07d23SGuo Ren 
8118c07d23SGuo Ren 	ksp = (unsigned long *)sp;
8218c07d23SGuo Ren 	while (!kstack_end(ksp)) {
8318c07d23SGuo Ren 		if (__kernel_text_address(pc) && unlikely(fn(pc, arg)))
8418c07d23SGuo Ren 			break;
8518c07d23SGuo Ren 		pc = (*ksp++) - 0x4;
8618c07d23SGuo Ren 	}
8718c07d23SGuo Ren }
8818c07d23SGuo Ren #endif /* CONFIG_FRAME_POINTER */
8918c07d23SGuo Ren 
print_trace_address(unsigned long pc,void * arg)9018c07d23SGuo Ren static bool print_trace_address(unsigned long pc, void *arg)
9118c07d23SGuo Ren {
92aeeb59d6SDmitry Safonov 	print_ip_sym((const char *)arg, pc);
9318c07d23SGuo Ren 	return false;
9418c07d23SGuo Ren }
9518c07d23SGuo Ren 
show_stack(struct task_struct * task,unsigned long * sp,const char * loglvl)969cb8f069SDmitry Safonov void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
97aeeb59d6SDmitry Safonov {
98aeeb59d6SDmitry Safonov 	pr_cont("Call Trace:\n");
99aeeb59d6SDmitry Safonov 	walk_stackframe(task, NULL, print_trace_address, (void *)loglvl);
100aeeb59d6SDmitry Safonov }
101aeeb59d6SDmitry Safonov 
save_wchan(unsigned long pc,void * arg)10218c07d23SGuo Ren static bool save_wchan(unsigned long pc, void *arg)
10318c07d23SGuo Ren {
10418c07d23SGuo Ren 	if (!in_sched_functions(pc)) {
10518c07d23SGuo Ren 		unsigned long *p = arg;
10618c07d23SGuo Ren 		*p = pc;
10718c07d23SGuo Ren 		return true;
10818c07d23SGuo Ren 	}
10918c07d23SGuo Ren 	return false;
11018c07d23SGuo Ren }
11118c07d23SGuo Ren 
__get_wchan(struct task_struct * task)11242a20f86SKees Cook unsigned long __get_wchan(struct task_struct *task)
11318c07d23SGuo Ren {
11418c07d23SGuo Ren 	unsigned long pc = 0;
11518c07d23SGuo Ren 
11618c07d23SGuo Ren 	walk_stackframe(task, NULL, save_wchan, &pc);
11718c07d23SGuo Ren 	return pc;
11818c07d23SGuo Ren }
11918c07d23SGuo Ren 
12018c07d23SGuo Ren #ifdef CONFIG_STACKTRACE
__save_trace(unsigned long pc,void * arg,bool nosched)12118c07d23SGuo Ren static bool __save_trace(unsigned long pc, void *arg, bool nosched)
12218c07d23SGuo Ren {
12318c07d23SGuo Ren 	struct stack_trace *trace = arg;
12418c07d23SGuo Ren 
12518c07d23SGuo Ren 	if (unlikely(nosched && in_sched_functions(pc)))
12618c07d23SGuo Ren 		return false;
12718c07d23SGuo Ren 	if (unlikely(trace->skip > 0)) {
12818c07d23SGuo Ren 		trace->skip--;
12918c07d23SGuo Ren 		return false;
13018c07d23SGuo Ren 	}
13118c07d23SGuo Ren 
13218c07d23SGuo Ren 	trace->entries[trace->nr_entries++] = pc;
13318c07d23SGuo Ren 	return (trace->nr_entries >= trace->max_entries);
13418c07d23SGuo Ren }
13518c07d23SGuo Ren 
save_trace(unsigned long pc,void * arg)13618c07d23SGuo Ren static bool save_trace(unsigned long pc, void *arg)
13718c07d23SGuo Ren {
13818c07d23SGuo Ren 	return __save_trace(pc, arg, false);
13918c07d23SGuo Ren }
14018c07d23SGuo Ren 
14118c07d23SGuo Ren /*
14218c07d23SGuo Ren  * Save stack-backtrace addresses into a stack_trace buffer.
14318c07d23SGuo Ren  */
save_stack_trace_tsk(struct task_struct * tsk,struct stack_trace * trace)14418c07d23SGuo Ren void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
14518c07d23SGuo Ren {
14618c07d23SGuo Ren 	walk_stackframe(tsk, NULL, save_trace, trace);
14718c07d23SGuo Ren }
14818c07d23SGuo Ren EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
1490ea2dc7cSGuo Ren 
save_stack_trace(struct stack_trace * trace)1500ea2dc7cSGuo Ren void save_stack_trace(struct stack_trace *trace)
1510ea2dc7cSGuo Ren {
15218c07d23SGuo Ren 	save_stack_trace_tsk(NULL, trace);
1530ea2dc7cSGuo Ren }
1540ea2dc7cSGuo Ren EXPORT_SYMBOL_GPL(save_stack_trace);
1550ea2dc7cSGuo Ren 
15618c07d23SGuo Ren #endif /* CONFIG_STACKTRACE */
157