xref: /linux/arch/csky/kernel/stacktrace.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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 
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_sp __asm__ ("sp");
2718c07d23SGuo Ren 		const register unsigned long current_fp __asm__ ("r8");
2818c07d23SGuo Ren 		fp = current_fp;
2918c07d23SGuo Ren 		sp = current_sp;
3018c07d23SGuo Ren 		pc = (unsigned long)walk_stackframe;
3118c07d23SGuo Ren 	} else {
3218c07d23SGuo Ren 		/* task blocked in __switch_to */
3318c07d23SGuo Ren 		fp = thread_saved_fp(task);
3418c07d23SGuo Ren 		sp = thread_saved_sp(task);
3518c07d23SGuo Ren 		pc = thread_saved_lr(task);
3618c07d23SGuo Ren 	}
3718c07d23SGuo Ren 
3818c07d23SGuo Ren 	for (;;) {
3918c07d23SGuo Ren 		unsigned long low, high;
4018c07d23SGuo Ren 		struct stackframe *frame;
4118c07d23SGuo Ren 
4218c07d23SGuo Ren 		if (unlikely(!__kernel_text_address(pc) || fn(pc, arg)))
4318c07d23SGuo Ren 			break;
4418c07d23SGuo Ren 
4518c07d23SGuo Ren 		/* Validate frame pointer */
4618c07d23SGuo Ren 		low = sp;
4718c07d23SGuo Ren 		high = ALIGN(sp, THREAD_SIZE);
4818c07d23SGuo Ren 		if (unlikely(fp < low || fp > high || fp & 0x3))
4918c07d23SGuo Ren 			break;
5018c07d23SGuo Ren 		/* Unwind stack frame */
5118c07d23SGuo Ren 		frame = (struct stackframe *)fp;
5218c07d23SGuo Ren 		sp = fp;
5318c07d23SGuo Ren 		fp = frame->fp;
5418c07d23SGuo Ren 		pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
5518c07d23SGuo Ren 					   (unsigned long *)(fp - 8));
5618c07d23SGuo Ren 	}
5718c07d23SGuo Ren }
5818c07d23SGuo Ren 
5918c07d23SGuo Ren #else /* !CONFIG_FRAME_POINTER */
6018c07d23SGuo Ren 
6118c07d23SGuo Ren static void notrace walk_stackframe(struct task_struct *task,
6218c07d23SGuo Ren 	struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
6318c07d23SGuo Ren {
6418c07d23SGuo Ren 	unsigned long sp, pc;
6518c07d23SGuo Ren 	unsigned long *ksp;
6618c07d23SGuo Ren 
6718c07d23SGuo Ren 	if (regs) {
6818c07d23SGuo Ren 		sp = user_stack_pointer(regs);
6918c07d23SGuo Ren 		pc = instruction_pointer(regs);
7018c07d23SGuo Ren 	} else if (task == NULL || task == current) {
7118c07d23SGuo Ren 		const register unsigned long current_sp __asm__ ("sp");
7218c07d23SGuo Ren 		sp = current_sp;
7318c07d23SGuo Ren 		pc = (unsigned long)walk_stackframe;
7418c07d23SGuo Ren 	} else {
7518c07d23SGuo Ren 		/* task blocked in __switch_to */
7618c07d23SGuo Ren 		sp = thread_saved_sp(task);
7718c07d23SGuo Ren 		pc = thread_saved_lr(task);
7818c07d23SGuo Ren 	}
7918c07d23SGuo Ren 
8018c07d23SGuo Ren 	if (unlikely(sp & 0x3))
8118c07d23SGuo Ren 		return;
8218c07d23SGuo Ren 
8318c07d23SGuo Ren 	ksp = (unsigned long *)sp;
8418c07d23SGuo Ren 	while (!kstack_end(ksp)) {
8518c07d23SGuo Ren 		if (__kernel_text_address(pc) && unlikely(fn(pc, arg)))
8618c07d23SGuo Ren 			break;
8718c07d23SGuo Ren 		pc = (*ksp++) - 0x4;
8818c07d23SGuo Ren 	}
8918c07d23SGuo Ren }
9018c07d23SGuo Ren #endif /* CONFIG_FRAME_POINTER */
9118c07d23SGuo Ren 
9218c07d23SGuo Ren static bool print_trace_address(unsigned long pc, void *arg)
9318c07d23SGuo Ren {
94aeeb59d6SDmitry Safonov 	print_ip_sym((const char *)arg, pc);
9518c07d23SGuo Ren 	return false;
9618c07d23SGuo Ren }
9718c07d23SGuo Ren 
989cb8f069SDmitry Safonov void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
99aeeb59d6SDmitry Safonov {
100aeeb59d6SDmitry Safonov 	pr_cont("Call Trace:\n");
101aeeb59d6SDmitry Safonov 	walk_stackframe(task, NULL, print_trace_address, (void *)loglvl);
102aeeb59d6SDmitry Safonov }
103aeeb59d6SDmitry Safonov 
10418c07d23SGuo Ren static bool save_wchan(unsigned long pc, void *arg)
10518c07d23SGuo Ren {
10618c07d23SGuo Ren 	if (!in_sched_functions(pc)) {
10718c07d23SGuo Ren 		unsigned long *p = arg;
10818c07d23SGuo Ren 		*p = pc;
10918c07d23SGuo Ren 		return true;
11018c07d23SGuo Ren 	}
11118c07d23SGuo Ren 	return false;
11218c07d23SGuo Ren }
11318c07d23SGuo Ren 
114*42a20f86SKees Cook unsigned long __get_wchan(struct task_struct *task)
11518c07d23SGuo Ren {
11618c07d23SGuo Ren 	unsigned long pc = 0;
11718c07d23SGuo Ren 
11818c07d23SGuo Ren 	walk_stackframe(task, NULL, save_wchan, &pc);
11918c07d23SGuo Ren 	return pc;
12018c07d23SGuo Ren }
12118c07d23SGuo Ren 
12218c07d23SGuo Ren #ifdef CONFIG_STACKTRACE
12318c07d23SGuo Ren static bool __save_trace(unsigned long pc, void *arg, bool nosched)
12418c07d23SGuo Ren {
12518c07d23SGuo Ren 	struct stack_trace *trace = arg;
12618c07d23SGuo Ren 
12718c07d23SGuo Ren 	if (unlikely(nosched && in_sched_functions(pc)))
12818c07d23SGuo Ren 		return false;
12918c07d23SGuo Ren 	if (unlikely(trace->skip > 0)) {
13018c07d23SGuo Ren 		trace->skip--;
13118c07d23SGuo Ren 		return false;
13218c07d23SGuo Ren 	}
13318c07d23SGuo Ren 
13418c07d23SGuo Ren 	trace->entries[trace->nr_entries++] = pc;
13518c07d23SGuo Ren 	return (trace->nr_entries >= trace->max_entries);
13618c07d23SGuo Ren }
13718c07d23SGuo Ren 
13818c07d23SGuo Ren static bool save_trace(unsigned long pc, void *arg)
13918c07d23SGuo Ren {
14018c07d23SGuo Ren 	return __save_trace(pc, arg, false);
14118c07d23SGuo Ren }
14218c07d23SGuo Ren 
14318c07d23SGuo Ren /*
14418c07d23SGuo Ren  * Save stack-backtrace addresses into a stack_trace buffer.
14518c07d23SGuo Ren  */
14618c07d23SGuo Ren void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
14718c07d23SGuo Ren {
14818c07d23SGuo Ren 	walk_stackframe(tsk, NULL, save_trace, trace);
14918c07d23SGuo Ren }
15018c07d23SGuo Ren EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
1510ea2dc7cSGuo Ren 
1520ea2dc7cSGuo Ren void save_stack_trace(struct stack_trace *trace)
1530ea2dc7cSGuo Ren {
15418c07d23SGuo Ren 	save_stack_trace_tsk(NULL, trace);
1550ea2dc7cSGuo Ren }
1560ea2dc7cSGuo Ren EXPORT_SYMBOL_GPL(save_stack_trace);
1570ea2dc7cSGuo Ren 
15818c07d23SGuo Ren #endif /* CONFIG_STACKTRACE */
159