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