11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25d8544e2SPalmer Dabbelt /*
35d8544e2SPalmer Dabbelt * Copyright (C) 2008 ARM Limited
45d8544e2SPalmer Dabbelt * Copyright (C) 2014 Regents of the University of California
55d8544e2SPalmer Dabbelt */
65d8544e2SPalmer Dabbelt
75d8544e2SPalmer Dabbelt #include <linux/export.h>
85d8544e2SPalmer Dabbelt #include <linux/kallsyms.h>
95d8544e2SPalmer Dabbelt #include <linux/sched.h>
105d8544e2SPalmer Dabbelt #include <linux/sched/debug.h>
115d8544e2SPalmer Dabbelt #include <linux/sched/task_stack.h>
125d8544e2SPalmer Dabbelt #include <linux/stacktrace.h>
13b785ec12SAlan Kao #include <linux/ftrace.h>
145d8544e2SPalmer Dabbelt
1599c168fcSKefeng Wang #include <asm/stacktrace.h>
1699c168fcSKefeng Wang
175d8544e2SPalmer Dabbelt #ifdef CONFIG_FRAME_POINTER
185d8544e2SPalmer Dabbelt
197ecdadf7SGuo Ren extern asmlinkage void ret_from_exception(void);
207ecdadf7SGuo Ren
fp_is_valid(unsigned long fp,unsigned long sp)21c273cae0SMatthew Bystrin static inline int fp_is_valid(unsigned long fp, unsigned long sp)
22c273cae0SMatthew Bystrin {
23c273cae0SMatthew Bystrin unsigned long low, high;
24c273cae0SMatthew Bystrin
25c273cae0SMatthew Bystrin low = sp + sizeof(struct stackframe);
26c273cae0SMatthew Bystrin high = ALIGN(sp, THREAD_SIZE);
27c273cae0SMatthew Bystrin
28c273cae0SMatthew Bystrin return !(fp < low || fp > high || fp & 0x07);
29c273cae0SMatthew Bystrin }
30c273cae0SMatthew Bystrin
walk_stackframe(struct task_struct * task,struct pt_regs * regs,bool (* fn)(void *,unsigned long),void * arg)31dbeb90b0SMao Han void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
325cb0080fSKefeng Wang bool (*fn)(void *, unsigned long), void *arg)
335d8544e2SPalmer Dabbelt {
345d8544e2SPalmer Dabbelt unsigned long fp, sp, pc;
35*55f6da70SPuranjay Mohan int graph_idx = 0;
366a00ef44SChangbin Du int level = 0;
375d8544e2SPalmer Dabbelt
385d8544e2SPalmer Dabbelt if (regs) {
396ab77af4SChristoph Hellwig fp = frame_pointer(regs);
406ab77af4SChristoph Hellwig sp = user_stack_pointer(regs);
416ab77af4SChristoph Hellwig pc = instruction_pointer(regs);
4278d9d800SJisheng Zhang } else if (task == NULL || task == current) {
436a00ef44SChangbin Du fp = (unsigned long)__builtin_frame_address(0);
44fdecfea0SKees Cook sp = current_stack_pointer;
456a00ef44SChangbin Du pc = (unsigned long)walk_stackframe;
46cb80242cSLiu Shixin level = -1;
475d8544e2SPalmer Dabbelt } else {
485d8544e2SPalmer Dabbelt /* task blocked in __switch_to */
495d8544e2SPalmer Dabbelt fp = task->thread.s[0];
505d8544e2SPalmer Dabbelt sp = task->thread.sp;
515d8544e2SPalmer Dabbelt pc = task->thread.ra;
525d8544e2SPalmer Dabbelt }
535d8544e2SPalmer Dabbelt
545d8544e2SPalmer Dabbelt for (;;) {
555d8544e2SPalmer Dabbelt struct stackframe *frame;
565d8544e2SPalmer Dabbelt
57cb80242cSLiu Shixin if (unlikely(!__kernel_text_address(pc) || (level++ >= 0 && !fn(arg, pc))))
585d8544e2SPalmer Dabbelt break;
595d8544e2SPalmer Dabbelt
60c273cae0SMatthew Bystrin if (unlikely(!fp_is_valid(fp, sp)))
615d8544e2SPalmer Dabbelt break;
62c273cae0SMatthew Bystrin
635d8544e2SPalmer Dabbelt /* Unwind stack frame */
645d8544e2SPalmer Dabbelt frame = (struct stackframe *)fp - 1;
655d8544e2SPalmer Dabbelt sp = fp;
66c273cae0SMatthew Bystrin if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp)) {
67c273cae0SMatthew Bystrin /* We hit function where ra is not saved on the stack */
68f766f77aSChen Huang fp = frame->ra;
69f766f77aSChen Huang pc = regs->ra;
70f766f77aSChen Huang } else {
715d8544e2SPalmer Dabbelt fp = frame->fp;
72*55f6da70SPuranjay Mohan pc = ftrace_graph_ret_addr(current, &graph_idx, frame->ra,
735c3022e4SGuo Ren &frame->ra);
747ecdadf7SGuo Ren if (pc == (unsigned long)ret_from_exception) {
757ecdadf7SGuo Ren if (unlikely(!__kernel_text_address(pc) || !fn(arg, pc)))
767ecdadf7SGuo Ren break;
777ecdadf7SGuo Ren
787ecdadf7SGuo Ren pc = ((struct pt_regs *)sp)->epc;
797ecdadf7SGuo Ren fp = ((struct pt_regs *)sp)->s0;
807ecdadf7SGuo Ren }
815d8544e2SPalmer Dabbelt }
82f766f77aSChen Huang
83f766f77aSChen Huang }
845d8544e2SPalmer Dabbelt }
855d8544e2SPalmer Dabbelt
865d8544e2SPalmer Dabbelt #else /* !CONFIG_FRAME_POINTER */
875d8544e2SPalmer Dabbelt
walk_stackframe(struct task_struct * task,struct pt_regs * regs,bool (* fn)(void *,unsigned long),void * arg)880502bee3SKefeng Wang void notrace walk_stackframe(struct task_struct *task,
899dd97064SKefeng Wang struct pt_regs *regs, bool (*fn)(void *, unsigned long), void *arg)
905d8544e2SPalmer Dabbelt {
915d8544e2SPalmer Dabbelt unsigned long sp, pc;
925d8544e2SPalmer Dabbelt unsigned long *ksp;
935d8544e2SPalmer Dabbelt
945d8544e2SPalmer Dabbelt if (regs) {
956ab77af4SChristoph Hellwig sp = user_stack_pointer(regs);
966ab77af4SChristoph Hellwig pc = instruction_pointer(regs);
975d8544e2SPalmer Dabbelt } else if (task == NULL || task == current) {
98fdecfea0SKees Cook sp = current_stack_pointer;
995d8544e2SPalmer Dabbelt pc = (unsigned long)walk_stackframe;
1005d8544e2SPalmer Dabbelt } else {
1015d8544e2SPalmer Dabbelt /* task blocked in __switch_to */
1025d8544e2SPalmer Dabbelt sp = task->thread.sp;
1035d8544e2SPalmer Dabbelt pc = task->thread.ra;
1045d8544e2SPalmer Dabbelt }
1055d8544e2SPalmer Dabbelt
1065d8544e2SPalmer Dabbelt if (unlikely(sp & 0x7))
1075d8544e2SPalmer Dabbelt return;
1085d8544e2SPalmer Dabbelt
1095d8544e2SPalmer Dabbelt ksp = (unsigned long *)sp;
1105d8544e2SPalmer Dabbelt while (!kstack_end(ksp)) {
1115cb0080fSKefeng Wang if (__kernel_text_address(pc) && unlikely(!fn(arg, pc)))
1125d8544e2SPalmer Dabbelt break;
11376950340SAlexandre Ghiti pc = READ_ONCE_NOCHECK(*ksp++) - 0x4;
1145d8544e2SPalmer Dabbelt }
1155d8544e2SPalmer Dabbelt }
1165d8544e2SPalmer Dabbelt
1175d8544e2SPalmer Dabbelt #endif /* CONFIG_FRAME_POINTER */
1185d8544e2SPalmer Dabbelt
print_trace_address(void * arg,unsigned long pc)1195cb0080fSKefeng Wang static bool print_trace_address(void *arg, unsigned long pc)
1205d8544e2SPalmer Dabbelt {
1210b3d4365SDmitry Safonov const char *loglvl = arg;
1220b3d4365SDmitry Safonov
1230b3d4365SDmitry Safonov print_ip_sym(loglvl, pc);
1245cb0080fSKefeng Wang return true;
1255d8544e2SPalmer Dabbelt }
1265d8544e2SPalmer Dabbelt
dump_backtrace(struct pt_regs * regs,struct task_struct * task,const char * loglvl)127eac2f305SChen Huang noinline void dump_backtrace(struct pt_regs *regs, struct task_struct *task,
128091b9450SKefeng Wang const char *loglvl)
129091b9450SKefeng Wang {
130091b9450SKefeng Wang walk_stackframe(task, regs, print_trace_address, (void *)loglvl);
131091b9450SKefeng Wang }
132091b9450SKefeng Wang
show_stack(struct task_struct * task,unsigned long * sp,const char * loglvl)1339cb8f069SDmitry Safonov void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
1340b3d4365SDmitry Safonov {
135eac2f305SChen Huang pr_cont("%sCall Trace:\n", loglvl);
136091b9450SKefeng Wang dump_backtrace(NULL, task, loglvl);
1370b3d4365SDmitry Safonov }
1380b3d4365SDmitry Safonov
save_wchan(void * arg,unsigned long pc)1399dd97064SKefeng Wang static bool save_wchan(void *arg, unsigned long pc)
1405d8544e2SPalmer Dabbelt {
1415d8544e2SPalmer Dabbelt if (!in_sched_functions(pc)) {
1425d8544e2SPalmer Dabbelt unsigned long *p = arg;
1435d8544e2SPalmer Dabbelt *p = pc;
1445d8544e2SPalmer Dabbelt return false;
1455d8544e2SPalmer Dabbelt }
1465cb0080fSKefeng Wang return true;
1475cb0080fSKefeng Wang }
1485d8544e2SPalmer Dabbelt
__get_wchan(struct task_struct * task)14942a20f86SKees Cook unsigned long __get_wchan(struct task_struct *task)
1505d8544e2SPalmer Dabbelt {
1515d8544e2SPalmer Dabbelt unsigned long pc = 0;
1525d8544e2SPalmer Dabbelt
15376f5dfacSJisheng Zhang if (!try_get_task_stack(task))
15476f5dfacSJisheng Zhang return 0;
1555d8544e2SPalmer Dabbelt walk_stackframe(task, NULL, save_wchan, &pc);
15676f5dfacSJisheng Zhang put_task_stack(task);
1575d8544e2SPalmer Dabbelt return pc;
1585d8544e2SPalmer Dabbelt }
1595d8544e2SPalmer Dabbelt
arch_stack_walk(stack_trace_consume_fn consume_entry,void * cookie,struct task_struct * task,struct pt_regs * regs)16074eb70ceSAndy Chiu noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
1615cb0080fSKefeng Wang struct task_struct *task, struct pt_regs *regs)
1625d8544e2SPalmer Dabbelt {
1635cb0080fSKefeng Wang walk_stackframe(task, regs, consume_entry, cookie);
1645d8544e2SPalmer Dabbelt }
165