xref: /linux-6.15/arch/riscv/kernel/stacktrace.c (revision d3817d09)
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