diff options
Diffstat (limited to 'arch/riscv/lib/stacktrace.c')
-rw-r--r-- | arch/riscv/lib/stacktrace.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/arch/riscv/lib/stacktrace.c b/arch/riscv/lib/stacktrace.c new file mode 100644 index 0000000000..663938019e --- /dev/null +++ b/arch/riscv/lib/stacktrace.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2008 ARM Limited + * Copyright (C) 2014 Regents of the University of California + * Copyright (C) 2021 Ahmad Fatoum, Pengutronix + * + * Framepointer assisted stack unwinder + */ + +#include <linux/kernel.h> +#include <printk.h> +#include <asm/unwind.h> +#include <asm/ptrace.h> +#include <asm-generic/memory_layout.h> +#include <asm/sections.h> + +struct stackframe { + unsigned long fp; + unsigned long ra; +}; + +static void dump_backtrace_entry(unsigned long where, unsigned long from) +{ +#ifdef CONFIG_KALLSYMS + printf("[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from); +#else + printf("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); +#endif +} + +static int unwind_frame(struct stackframe *frame, unsigned long *sp) +{ + unsigned long low, high; + unsigned long fp = frame->fp; + + low = *sp; + high = ALIGN(low, STACK_SIZE); + + if (fp < low || fp > high - sizeof(struct stackframe) || fp & 0x7) + return -1; + + *frame = *((struct stackframe *)fp - 1); + *sp = fp; + + return 0; +} + +void unwind_backtrace(const struct pt_regs *regs) +{ + struct stackframe frame = {}; + register unsigned long current_sp asm ("sp"); + unsigned long sp = 0; + + if (regs) { + frame.fp = frame_pointer(regs); + frame.ra = regs->ra; + } else { + sp = current_sp; + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.ra = (unsigned long)unwind_backtrace; + } + + printf("Call trace:\n"); + for (;;) { + unsigned long where = frame.ra; + int ret; + + ret = unwind_frame(&frame, &sp); + if (ret < 0) + break; + + dump_backtrace_entry(where, frame.ra); + } +} + +void dump_stack(void) +{ + unwind_backtrace(NULL); +} |