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