summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv/Kconfig20
-rw-r--r--arch/riscv/Makefile4
-rw-r--r--arch/riscv/cpu/start.S1
-rw-r--r--arch/riscv/lib/interrupts.c35
4 files changed, 60 insertions, 0 deletions
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 6c26f91f166..1819db1e256 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -119,6 +119,26 @@ config ARCH_RV64I
endchoice
+config FRAMEPOINTER
+ bool "Build with frame pointer for stack unwinding"
+ help
+ Choose this option to use the frame pointer so the stack can be
+ unwound if needed. This is useful for tracing where faults came
+ from as the source may be several functions back
+
+ If you say Y here, then the code size will be increased due to
+ having to store the fp.
+
+config SPL_FRAMEPOINTER
+ bool "Build SPL with frame pointer for stack unwinding"
+ help
+ Choose this option to use the frame pointer so the stack can be
+ unwound if needed. This is useful for tracing where faults came
+ from as the source may be several functions back
+
+ If you say Y here, then the code size will be increased due to
+ having to store the fp.
+
choice
prompt "Code Model"
default CMODEL_MEDLOW
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index b3ef87078b5..c36a8533e0f 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -48,6 +48,10 @@ endif
ARCH_FLAGS = -march=$(RISCV_MARCH) -mabi=$(ABI) \
-mcmodel=$(CMODEL)
+ifeq ($(CONFIG_$(SPL_)FRAMEPOINTER),y)
+ ARCH_FLAGS += -fno-omit-frame-pointer
+endif
+
PLATFORM_CPPFLAGS += $(ARCH_FLAGS)
CFLAGS_EFI += $(ARCH_FLAGS)
diff --git a/arch/riscv/cpu/start.S b/arch/riscv/cpu/start.S
index 6cecadfac56..a9e19356928 100644
--- a/arch/riscv/cpu/start.S
+++ b/arch/riscv/cpu/start.S
@@ -418,6 +418,7 @@ call_board_init_r:
*/
mv a0, s3 /* gd_t */
mv a1, s4 /* dest_addr */
+ mv s0, zero /* fp == NULL */
/*
* jump to it ...
diff --git a/arch/riscv/lib/interrupts.c b/arch/riscv/lib/interrupts.c
index a26ccc721fd..7350e2ced85 100644
--- a/arch/riscv/lib/interrupts.c
+++ b/arch/riscv/lib/interrupts.c
@@ -60,6 +60,40 @@ static void show_regs(struct pt_regs *regs)
#endif
}
+#if defined(CONFIG_FRAMEPOINTER) || defined(CONFIG_SPL_FRAMEPOINTER)
+static void show_backtrace(struct pt_regs *regs)
+{
+ uintptr_t *fp = (uintptr_t *)regs->s0;
+ unsigned count = 0;
+ ulong ra;
+
+ printf("backtrace:\n");
+
+ /* there are a few entry points where the s0 register is
+ * set to gd, so to avoid changing those, just abort if
+ * the value is the same */
+ while (fp != NULL && fp != (uintptr_t *)gd) {
+ ra = fp[-1];
+ printf("backtrace %2d: FP: " REG_FMT " RA: " REG_FMT,
+ count, (ulong)fp, ra);
+
+ if (gd && gd->flags & GD_FLG_RELOC)
+ printf(" - RA: " REG_FMT " reloc adjusted\n",
+ ra - gd->reloc_off);
+ else
+ printf("\n");
+
+ fp = (uintptr_t *)fp[-2];
+ count++;
+ }
+}
+#else
+static void show_backtrace(struct pt_regs *regs)
+{
+ printf("No backtrace support enabled\n");
+}
+#endif
+
/**
* instr_len() - get instruction length
*
@@ -131,6 +165,7 @@ static void _exit_trap(ulong code, ulong epc, ulong tval, struct pt_regs *regs)
epc - gd->reloc_off, regs->ra - gd->reloc_off);
show_regs(regs);
+ show_backtrace(regs);
show_code(epc);
show_efi_loaded_images(epc);
panic("\n");