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
195d5fc33cSAnton Blanchard extern asmlinkage void handle_exception(void);
2051356ce6SClément Léger extern unsigned long ret_from_exception_end;
217ecdadf7SGuo Ren
fp_is_valid(unsigned long fp,unsigned long sp)22a2a4d4a6SMatthew Bystrin static inline int fp_is_valid(unsigned long fp, unsigned long sp)
23a2a4d4a6SMatthew Bystrin {
24a2a4d4a6SMatthew Bystrin unsigned long low, high;
25a2a4d4a6SMatthew Bystrin
26a2a4d4a6SMatthew Bystrin low = sp + sizeof(struct stackframe);
27a2a4d4a6SMatthew Bystrin high = ALIGN(sp, THREAD_SIZE);
28a2a4d4a6SMatthew Bystrin
29a2a4d4a6SMatthew Bystrin return !(fp < low || fp > high || fp & 0x07);
30a2a4d4a6SMatthew Bystrin }
31a2a4d4a6SMatthew Bystrin
walk_stackframe(struct task_struct * task,struct pt_regs * regs,bool (* fn)(void *,unsigned long),void * arg)32dbeb90b0SMao Han void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
335cb0080fSKefeng Wang bool (*fn)(void *, unsigned long), void *arg)
345d8544e2SPalmer Dabbelt {
355d8544e2SPalmer Dabbelt unsigned long fp, sp, pc;
36393da6cbSPuranjay Mohan int graph_idx = 0;
376a00ef44SChangbin Du int level = 0;
385d8544e2SPalmer Dabbelt
395d8544e2SPalmer Dabbelt if (regs) {
406ab77af4SChristoph Hellwig fp = frame_pointer(regs);
416ab77af4SChristoph Hellwig sp = user_stack_pointer(regs);
426ab77af4SChristoph Hellwig pc = instruction_pointer(regs);
4378d9d800SJisheng Zhang } else if (task == NULL || task == current) {
446a00ef44SChangbin Du fp = (unsigned long)__builtin_frame_address(0);
45fdecfea0SKees Cook sp = current_stack_pointer;
466a00ef44SChangbin Du pc = (unsigned long)walk_stackframe;
47cb80242cSLiu Shixin level = -1;
485d8544e2SPalmer Dabbelt } else {
495d8544e2SPalmer Dabbelt /* task blocked in __switch_to */
505d8544e2SPalmer Dabbelt fp = task->thread.s[0];
515d8544e2SPalmer Dabbelt sp = task->thread.sp;
525d8544e2SPalmer Dabbelt pc = task->thread.ra;
535d8544e2SPalmer Dabbelt }
545d8544e2SPalmer Dabbelt
555d8544e2SPalmer Dabbelt for (;;) {
565d8544e2SPalmer Dabbelt struct stackframe *frame;
575d8544e2SPalmer Dabbelt
58cb80242cSLiu Shixin if (unlikely(!__kernel_text_address(pc) || (level++ >= 0 && !fn(arg, pc))))
595d8544e2SPalmer Dabbelt break;
605d8544e2SPalmer Dabbelt
61a2a4d4a6SMatthew Bystrin if (unlikely(!fp_is_valid(fp, sp)))
625d8544e2SPalmer Dabbelt break;
63a2a4d4a6SMatthew Bystrin
645d8544e2SPalmer Dabbelt /* Unwind stack frame */
655d8544e2SPalmer Dabbelt frame = (struct stackframe *)fp - 1;
665d8544e2SPalmer Dabbelt sp = fp;
67a2a4d4a6SMatthew Bystrin if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp)) {
68a2a4d4a6SMatthew Bystrin /* We hit function where ra is not saved on the stack */
69f766f77aSChen Huang fp = frame->ra;
70f766f77aSChen Huang pc = regs->ra;
71f766f77aSChen Huang } else {
725d8544e2SPalmer Dabbelt fp = frame->fp;
73393da6cbSPuranjay Mohan pc = ftrace_graph_ret_addr(current, &graph_idx, frame->ra,
745c3022e4SGuo Ren &frame->ra);
7551356ce6SClément Léger if (pc >= (unsigned long)handle_exception &&
7651356ce6SClément Léger pc < (unsigned long)&ret_from_exception_end) {
77*d3817d09SClément Léger if (unlikely(!fn(arg, pc)))
787ecdadf7SGuo Ren break;
797ecdadf7SGuo Ren
807ecdadf7SGuo Ren pc = ((struct pt_regs *)sp)->epc;
817ecdadf7SGuo Ren fp = ((struct pt_regs *)sp)->s0;
827ecdadf7SGuo Ren }
835d8544e2SPalmer Dabbelt }
84f766f77aSChen Huang
85f766f77aSChen Huang }
865d8544e2SPalmer Dabbelt }
875d8544e2SPalmer Dabbelt
885d8544e2SPalmer Dabbelt #else /* !CONFIG_FRAME_POINTER */
895d8544e2SPalmer Dabbelt
walk_stackframe(struct task_struct * task,struct pt_regs * regs,bool (* fn)(void *,unsigned long),void * arg)900502bee3SKefeng Wang void notrace walk_stackframe(struct task_struct *task,
919dd97064SKefeng Wang struct pt_regs *regs, bool (*fn)(void *, unsigned long), void *arg)
925d8544e2SPalmer Dabbelt {
935d8544e2SPalmer Dabbelt unsigned long sp, pc;
945d8544e2SPalmer Dabbelt unsigned long *ksp;
955d8544e2SPalmer Dabbelt
965d8544e2SPalmer Dabbelt if (regs) {
976ab77af4SChristoph Hellwig sp = user_stack_pointer(regs);
986ab77af4SChristoph Hellwig pc = instruction_pointer(regs);
995d8544e2SPalmer Dabbelt } else if (task == NULL || task == current) {
100fdecfea0SKees Cook sp = current_stack_pointer;
1015d8544e2SPalmer Dabbelt pc = (unsigned long)walk_stackframe;
1025d8544e2SPalmer Dabbelt } else {
1035d8544e2SPalmer Dabbelt /* task blocked in __switch_to */
1045d8544e2SPalmer Dabbelt sp = task->thread.sp;
1055d8544e2SPalmer Dabbelt pc = task->thread.ra;
1065d8544e2SPalmer Dabbelt }
1075d8544e2SPalmer Dabbelt
1085d8544e2SPalmer Dabbelt if (unlikely(sp & 0x7))
1095d8544e2SPalmer Dabbelt return;
1105d8544e2SPalmer Dabbelt
1115d8544e2SPalmer Dabbelt ksp = (unsigned long *)sp;
1125d8544e2SPalmer Dabbelt while (!kstack_end(ksp)) {
1135cb0080fSKefeng Wang if (__kernel_text_address(pc) && unlikely(!fn(arg, pc)))
1145d8544e2SPalmer Dabbelt break;
11576950340SAlexandre Ghiti pc = READ_ONCE_NOCHECK(*ksp++) - 0x4;
1165d8544e2SPalmer Dabbelt }
1175d8544e2SPalmer Dabbelt }
1185d8544e2SPalmer Dabbelt
1195d8544e2SPalmer Dabbelt #endif /* CONFIG_FRAME_POINTER */
1205d8544e2SPalmer Dabbelt
print_trace_address(void * arg,unsigned long pc)1215cb0080fSKefeng Wang static bool print_trace_address(void *arg, unsigned long pc)
1225d8544e2SPalmer Dabbelt {
1230b3d4365SDmitry Safonov const char *loglvl = arg;
1240b3d4365SDmitry Safonov
1250b3d4365SDmitry Safonov print_ip_sym(loglvl, pc);
1265cb0080fSKefeng Wang return true;
1275d8544e2SPalmer Dabbelt }
1285d8544e2SPalmer Dabbelt
dump_backtrace(struct pt_regs * regs,struct task_struct * task,const char * loglvl)129eac2f305SChen Huang noinline void dump_backtrace(struct pt_regs *regs, struct task_struct *task,
130091b9450SKefeng Wang const char *loglvl)
131091b9450SKefeng Wang {
132091b9450SKefeng Wang walk_stackframe(task, regs, print_trace_address, (void *)loglvl);
133091b9450SKefeng Wang }
134091b9450SKefeng Wang
show_stack(struct task_struct * task,unsigned long * sp,const char * loglvl)1359cb8f069SDmitry Safonov void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
1360b3d4365SDmitry Safonov {
137eac2f305SChen Huang pr_cont("%sCall Trace:\n", loglvl);
138091b9450SKefeng Wang dump_backtrace(NULL, task, loglvl);
1390b3d4365SDmitry Safonov }
1400b3d4365SDmitry Safonov
save_wchan(void * arg,unsigned long pc)1419dd97064SKefeng Wang static bool save_wchan(void *arg, unsigned long pc)
1425d8544e2SPalmer Dabbelt {
1435d8544e2SPalmer Dabbelt if (!in_sched_functions(pc)) {
1445d8544e2SPalmer Dabbelt unsigned long *p = arg;
1455d8544e2SPalmer Dabbelt *p = pc;
1465d8544e2SPalmer Dabbelt return false;
1475d8544e2SPalmer Dabbelt }
1485cb0080fSKefeng Wang return true;
1495cb0080fSKefeng Wang }
1505d8544e2SPalmer Dabbelt
__get_wchan(struct task_struct * task)15142a20f86SKees Cook unsigned long __get_wchan(struct task_struct *task)
1525d8544e2SPalmer Dabbelt {
1535d8544e2SPalmer Dabbelt unsigned long pc = 0;
1545d8544e2SPalmer Dabbelt
15576f5dfacSJisheng Zhang if (!try_get_task_stack(task))
15676f5dfacSJisheng Zhang return 0;
1575d8544e2SPalmer Dabbelt walk_stackframe(task, NULL, save_wchan, &pc);
15876f5dfacSJisheng Zhang put_task_stack(task);
1595d8544e2SPalmer Dabbelt return pc;
1605d8544e2SPalmer Dabbelt }
1615d8544e2SPalmer Dabbelt
arch_stack_walk(stack_trace_consume_fn consume_entry,void * cookie,struct task_struct * task,struct pt_regs * regs)16223b21889SAndy Chiu noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
1635cb0080fSKefeng Wang struct task_struct *task, struct pt_regs *regs)
1645d8544e2SPalmer Dabbelt {
1655cb0080fSKefeng Wang walk_stackframe(task, regs, consume_entry, cookie);
1665d8544e2SPalmer Dabbelt }
1671a748331SJinjie Ruan
1681a748331SJinjie Ruan /*
1691a748331SJinjie Ruan * Get the return address for a single stackframe and return a pointer to the
1701a748331SJinjie Ruan * next frame tail.
1711a748331SJinjie Ruan */
unwind_user_frame(stack_trace_consume_fn consume_entry,void * cookie,unsigned long fp,unsigned long reg_ra)1721a748331SJinjie Ruan static unsigned long unwind_user_frame(stack_trace_consume_fn consume_entry,
1731a748331SJinjie Ruan void *cookie, unsigned long fp,
1741a748331SJinjie Ruan unsigned long reg_ra)
1751a748331SJinjie Ruan {
1761a748331SJinjie Ruan struct stackframe buftail;
1771a748331SJinjie Ruan unsigned long ra = 0;
1781a748331SJinjie Ruan unsigned long __user *user_frame_tail =
1791a748331SJinjie Ruan (unsigned long __user *)(fp - sizeof(struct stackframe));
1801a748331SJinjie Ruan
1811a748331SJinjie Ruan /* Check accessibility of one struct frame_tail beyond */
1821a748331SJinjie Ruan if (!access_ok(user_frame_tail, sizeof(buftail)))
1831a748331SJinjie Ruan return 0;
1841a748331SJinjie Ruan if (__copy_from_user_inatomic(&buftail, user_frame_tail,
1851a748331SJinjie Ruan sizeof(buftail)))
1861a748331SJinjie Ruan return 0;
1871a748331SJinjie Ruan
1881a748331SJinjie Ruan ra = reg_ra ? : buftail.ra;
1891a748331SJinjie Ruan
1901a748331SJinjie Ruan fp = buftail.fp;
1911a748331SJinjie Ruan if (!ra || !consume_entry(cookie, ra))
1921a748331SJinjie Ruan return 0;
1931a748331SJinjie Ruan
1941a748331SJinjie Ruan return fp;
1951a748331SJinjie Ruan }
1961a748331SJinjie Ruan
arch_stack_walk_user(stack_trace_consume_fn consume_entry,void * cookie,const struct pt_regs * regs)1971a748331SJinjie Ruan void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
1981a748331SJinjie Ruan const struct pt_regs *regs)
1991a748331SJinjie Ruan {
2001a748331SJinjie Ruan unsigned long fp = 0;
2011a748331SJinjie Ruan
2021a748331SJinjie Ruan fp = regs->s0;
2031a748331SJinjie Ruan if (!consume_entry(cookie, regs->epc))
2041a748331SJinjie Ruan return;
2051a748331SJinjie Ruan
2061a748331SJinjie Ruan fp = unwind_user_frame(consume_entry, cookie, fp, regs->ra);
2071a748331SJinjie Ruan while (fp && !(fp & 0x7))
2081a748331SJinjie Ruan fp = unwind_user_frame(consume_entry, cookie, fp, 0);
2091a748331SJinjie Ruan }
210