@@ -22,8 +22,10 @@
#ifdef __KERNEL__
extern void show_regs(struct pt_regs *);
+void unwind_stack(void);
+
#define predicate(x) (x & 0xf0000000)
#define PREDICATE_ALWAYS 0xe0000000
#endif
@@ -3,15 +3,17 @@
* (C) Copyright 2013
* David Feng <fenghua@phytium.com.cn>
*/
+#include <asm-generic/sections.h>
#include <asm/esr.h>
#include <asm/global_data.h>
#include <asm/ptrace.h>
#include <irq_func.h>
#include <linux/compiler.h>
#include <efi_loader.h>
#include <semihosting.h>
+#include <symbols.h>
DECLARE_GLOBAL_DATA_PTR;
int interrupt_init(void)
@@ -80,8 +82,76 @@ static void dump_instr(struct pt_regs *regs)
printf(i == 0 ? "(%08x) " : "%08x ", addr[i]);
printf("\n");
}
+#if IS_ENABLED(CONFIG_SYMBOL_LOOKUP)
+
+static void print_sym(unsigned long symaddr, const char *symname, unsigned long offset)
+{
+ if (symaddr > (unsigned long)__bss_end)
+ printf("\t<%#016lx> ???\n", symaddr);
+ else
+ printf("\t<%#016lx> %s+%#lx\n", symaddr, symname, offset);
+}
+
+/* Stack frames automatically generated by compiler emitted code */
+struct stack_frame {
+ struct stack_frame *prev_ptr; /* Alays a bigger address on ARM64 */
+ unsigned long lr;
+ char data[];
+};
+
+static void __unwind_stack(unsigned long lr, unsigned long x29)
+{
+ char symname[KSYM_NAME_LEN+1] = { 0 };
+ unsigned long addr, offset;
+ struct stack_frame *fp;
+
+ /*
+ * Either the place where unwind_stack() was called, or the
+ * instruction that caused an exception
+ */
+ symbols_lookup(lr, &addr, &offset, symname);
+ print_sym(lr, symname, offset);
+
+ fp = (struct stack_frame *)x29;
+ while (fp && fp->prev_ptr > fp) {
+ symbols_lookup(fp->lr, &addr, &offset, symname);
+ /*
+ * _start is used over gd->relocaddr because it will be correct
+ * if U-Boot was built as relocatable and loaded at an arbitrary
+ * address (both pre and post-relocation).
+ */
+ print_sym(fp->lr, symname, offset);
+ fp = (struct stack_frame *)(u64)fp->prev_ptr;
+ }
+}
+
+/**
+ * unwind_stack() - Unwind the callstack and print a backtrace.
+ *
+ * This function can be called for debugging purposes to walk backwards through
+ * stack frames, printing the function name and offset of the branch instruction.
+ *
+ * It reads out the link register (x30) which contains the return address of the
+ * caller, and x29 which contains the frame pointer of the caller.
+ */
+void unwind_stack(void)
+{
+ unsigned long x29, x30;
+
+ asm("mov %0, x29" : "=r" (x29));
+ asm("mov %0, x30" : "=r" (x30));
+
+ __unwind_stack(x30, x29);
+}
+
+#else
+
+#define __unwind_stack(lr, x29) do {} while (0)
+
+#endif
+
void show_regs(struct pt_regs *regs)
{
int i;
@@ -95,8 +165,13 @@ void show_regs(struct pt_regs *regs)
printf("x%-2d: %016lx x%-2d: %016lx\n",
i, regs->regs[i], i+1, regs->regs[i+1]);
printf("\n");
dump_instr(regs);
+
+ if (IS_ENABLED(CONFIG_SYMBOL_LOOKUP)) {
+ printf("\nBacktrace:\n");
+ __unwind_stack(regs->elr, regs->regs[29]);
+ }
}
/*
* Try to "emulate" a semihosting call in the event that we don't have a
We already build arm64 images with frame pointers. Let's finally make use of them in tandem with the new symbol lookup support by unwinding the stack when an exception occurs, producing a backtrace similar to those emitted by Linux. In addition, introduce a dedicated unwind_stack() function which can be called from anywhere to print a backtrace. Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org> --- arch/arm/include/asm/ptrace.h | 2 ++ arch/arm/lib/interrupts_64.c | 75 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+)