summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorAhmad Fatoum <a.fatoum@pengutronix.de>2022-01-08 18:15:23 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2022-01-12 11:22:27 +0100
commit66d4822838e6a50c748f54cc854186fd3fcfbfe5 (patch)
tree75e0d7e6659b2c2ad0f73fae7890cada904e63a8 /arch
parentadf6e47b69d8d8d5a18748afb830320704dee50b (diff)
downloadbarebox-66d4822838e6a50c748f54cc854186fd3fcfbfe5.tar.gz
barebox-66d4822838e6a50c748f54cc854186fd3fcfbfe5.tar.xz
RISC-V: add stacktrace support via frame pointer walking
Make debugging more convenient by implementing stack_dump() and changing exception handlers to print stack trace along with the register dump. Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> Link: https://lore.barebox.org/20220108171524.587144-8-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/riscv/Kconfig10
-rw-r--r--arch/riscv/cpu/interrupts.c3
-rw-r--r--arch/riscv/include/asm/barebox-riscv-head.h1
-rw-r--r--arch/riscv/include/asm/ptrace.h10
-rw-r--r--arch/riscv/include/asm/unwind.h8
-rw-r--r--arch/riscv/lib/Makefile1
-rw-r--r--arch/riscv/lib/stacktrace.c79
7 files changed, 106 insertions, 6 deletions
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 8c66697185..c5b373e3af 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -83,6 +83,16 @@ config RISCV_EXCEPTIONS
default y
select ARCH_HAS_DATA_ABORT_MASK
+config RISCV_UNWIND
+ bool "enable stack unwinding support"
+ select ARCH_HAS_STACK_DUMP
+ select ARCH_WANT_FRAME_POINTERS
+ help
+ This option enables stack unwinding support in barebox
+ using the information automatically generated by the
+ compiler. The resulting kernel image is slightly bigger but
+ the performance is not affected.
+
config HAS_NMON
bool
diff --git a/arch/riscv/cpu/interrupts.c b/arch/riscv/cpu/interrupts.c
index 1f2d7b8857..0bb56d441d 100644
--- a/arch/riscv/cpu/interrupts.c
+++ b/arch/riscv/cpu/interrupts.c
@@ -14,6 +14,7 @@
#include <asm/ptrace.h>
#include <asm/irq.h>
#include <asm/csr.h>
+#include <asm/unwind.h>
#include <abort.h>
#include <pbl.h>
@@ -81,6 +82,8 @@ static void report_trap(const struct pt_regs *regs)
regs->epc, regs->ra, regs->badaddr);
show_regs(regs);
+
+ unwind_backtrace(regs);
}
diff --git a/arch/riscv/include/asm/barebox-riscv-head.h b/arch/riscv/include/asm/barebox-riscv-head.h
index a4c33472cd..4d62c20d1b 100644
--- a/arch/riscv/include/asm/barebox-riscv-head.h
+++ b/arch/riscv/include/asm/barebox-riscv-head.h
@@ -23,6 +23,7 @@
".ascii \"" magic2 "\"\n" /* magic 2 */ \
".word 0\n" /* reserved (PE-COFF offset) */ \
"1:\n" \
+ "li fp, 0\n" \
)
#define __barebox_riscv_header(instr, load_offset, version, magic1, magic2) \
diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h
index b5e792f666..319c50f946 100644
--- a/arch/riscv/include/asm/ptrace.h
+++ b/arch/riscv/include/asm/ptrace.h
@@ -61,7 +61,7 @@ struct pt_regs {
#define MAX_REG_OFFSET offsetof(struct pt_regs, cause)
/* Helpers for working with the instruction pointer */
-static inline unsigned long instruction_pointer(struct pt_regs *regs)
+static inline unsigned long instruction_pointer(const struct pt_regs *regs)
{
return regs->epc;
}
@@ -74,7 +74,7 @@ static inline void instruction_pointer_set(struct pt_regs *regs,
#define profile_pc(regs) instruction_pointer(regs)
/* Helpers for working with the user stack pointer */
-static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+static inline unsigned long user_stack_pointer(const struct pt_regs *regs)
{
return regs->sp;
}
@@ -85,13 +85,13 @@ static inline void user_stack_pointer_set(struct pt_regs *regs,
}
/* Valid only for Kernel mode traps. */
-static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+static inline unsigned long kernel_stack_pointer(const struct pt_regs *regs)
{
return regs->sp;
}
/* Helpers for working with the frame pointer */
-static inline unsigned long frame_pointer(struct pt_regs *regs)
+static inline unsigned long frame_pointer(const struct pt_regs *regs)
{
return regs->s0;
}
@@ -101,7 +101,7 @@ static inline void frame_pointer_set(struct pt_regs *regs,
regs->s0 = val;
}
-static inline unsigned long regs_return_value(struct pt_regs *regs)
+static inline unsigned long regs_return_value(const struct pt_regs *regs)
{
return regs->a0;
}
diff --git a/arch/riscv/include/asm/unwind.h b/arch/riscv/include/asm/unwind.h
index 9e5c8b5420..00f7845147 100644
--- a/arch/riscv/include/asm/unwind.h
+++ b/arch/riscv/include/asm/unwind.h
@@ -4,6 +4,12 @@
struct pt_regs;
-void unwind_backtrace(struct pt_regs *regs);
+#if defined CONFIG_RISCV_UNWIND && !defined __PBL__
+void unwind_backtrace(const struct pt_regs *regs);
+#else
+static inline void unwind_backtrace(const struct pt_regs *regs)
+{
+}
+#endif
#endif
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index a399de7399..f3db5320f7 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS) += memcpy.o memset.o memmove.o
obj-$(CONFIG_RISCV_SBI) += sbi.o
obj-$(CONFIG_CMD_RISCV_CPUINFO) += cpuinfo.o
obj-$(CONFIG_BOOTM) += bootm.o
+obj-$(CONFIG_RISCV_UNWIND) += stacktrace.o
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);
+}