Dump some registers when hitting an unexpected exception
At the moment, no information is printed on the UART whenever we hit
an unexpected exception, not even an error message. This is not great
from the user's perspective, who has got no idea of what is going on.
Now we print an error message, as well as the state of some of the
registers. This includes general-purpose registers, as well as some
system registers.
This is implemented for TFTF running:
- in AArch64 state, at EL2;
- in AArch64 state, at NS-EL1;
- in AArch32 state.
We might want to dump more registers in the future but this patch
at least provides a basis we can build upon.
Also, the SP_EL0 has been removed from the list of registers saved in
the CPU context because TFTF always uses SP_ELx and does not touch
SP_EL0 at all.
Change-Id: I56e4afa917b53b5ccccff1d5d09ac8ccfaa6ae49
Signed-off-by: Sandrine Bailleux <sandrine.bailleux@arm.com>
diff --git a/tftf/framework/aarch32/exception_report.c b/tftf/framework/aarch32/exception_report.c
new file mode 100644
index 0000000..46665e3
--- /dev/null
+++ b/tftf/framework/aarch32/exception_report.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <platform.h>
+#include <utils_def.h>
+
+/* We save r0-r12. */
+#define GPREGS_CNT 13
+
+/* Set of registers saved by the crash_dump() assembly function. */
+struct cpu_context {
+ u_register_t regs[GPREGS_CNT];
+ u_register_t lr;
+ u_register_t sp;
+};
+
+void __dead2 print_exception(const struct cpu_context *ctx)
+{
+ u_register_t mpid = read_mpidr();
+
+ /*
+ * The instruction barrier ensures we don't read stale values of system
+ * registers.
+ */
+ isb();
+
+ printf("Unhandled exception on CPU%u.\n", platform_get_core_pos(mpid));
+
+ /* Dump some interesting system registers. */
+ printf("System registers:\n");
+ printf(" MPIDR=0x%lx\n", mpid);
+ printf(" HSR=0x%lx ELR=0x%lx SPSR=0x%lx\n", read_hsr(),
+ read_elr_hyp(), read_spsr());
+
+ /* Dump general-purpose registers. */
+ printf("General-purpose registers:\n");
+ for (int i = 0; i < GPREGS_CNT; ++i) {
+ printf(" r%u=0x%lx\n", i, ctx->regs[i]);
+ }
+ printf(" LR=0x%lx\n", ctx->lr);
+ printf(" SP=0x%lx\n", ctx->sp);
+
+ while (1)
+ wfi();
+}
diff --git a/tftf/framework/aarch32/exceptions.S b/tftf/framework/aarch32/exceptions.S
index 1e6c574..aaeb93c 100644
--- a/tftf/framework/aarch32/exceptions.S
+++ b/tftf/framework/aarch32/exceptions.S
@@ -12,13 +12,23 @@
vector_base tftf_vector
b tftf_entrypoint
- b . /* Undef */
- b . /* Syscall */
- b . /* Prefetch abort */
- b . /* Data abort */
- b . /* Hyp trap */
- b tftf_intr_handle/* IRQ */
- b . /* FIQ */
+ b crash_dump /* Undef */
+ b crash_dump /* Syscall */
+ b crash_dump /* Prefetch abort */
+ b crash_dump /* Data abort */
+ b crash_dump /* Hyp trap */
+ b tftf_intr_handle /* IRQ */
+ b crash_dump /* FIQ */
+
+func crash_dump
+ /* Save SP and general-purpose registers on the stack. */
+ push {sp}
+ push {r0-r12, lr}
+
+ /* Print the saved CPU context on the UART. */
+ mov r0, sp
+ b print_exception
+endfunc crash_dump
/* ----------------------------------------------------------------------------
* The IRQ exception handler
diff --git a/tftf/framework/aarch64/exception_report.c b/tftf/framework/aarch64/exception_report.c
new file mode 100644
index 0000000..0add276
--- /dev/null
+++ b/tftf/framework/aarch64/exception_report.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <platform.h>
+#include <utils_def.h>
+
+/* We save x0-x30. */
+#define GPREGS_CNT 31
+
+/* Set of registers saved by the crash_dump() assembly function. */
+struct cpu_context {
+ u_register_t regs[GPREGS_CNT];
+ u_register_t sp;
+};
+
+/*
+ * Read the EL1 or EL2 version of a register, depending on the current exception
+ * level.
+ */
+#define read_sysreg(_name) \
+ (IS_IN_EL2() ? read_##_name##_el2() : read_##_name##_el1())
+
+void __dead2 print_exception(const struct cpu_context *ctx)
+{
+ u_register_t mpid = read_mpidr_el1();
+
+ /*
+ * The instruction barrier ensures we don't read stale values of system
+ * registers.
+ */
+ isb();
+
+ printf("Unhandled exception on CPU%u.\n", platform_get_core_pos(mpid));
+
+ /* Dump some interesting system registers. */
+ printf("System registers:\n");
+ printf(" MPIDR=0x%lx\n", mpid);
+ printf(" ESR=0x%lx ELR=0x%lx FAR=0x%lx\n", read_sysreg(esr),
+ read_sysreg(elr), read_sysreg(far));
+ printf(" SCTLR=0x%lx SPSR=0x%lx DAIF=0x%lx\n",
+ read_sysreg(sctlr), read_sysreg(spsr), read_daif());
+
+ /* Dump general-purpose registers. */
+ printf("General-purpose registers:\n");
+ for (int i = 0; i < GPREGS_CNT; ++i) {
+ printf(" x%u=0x%lx\n", i, ctx->regs[i]);
+ }
+ printf(" SP=0x%lx\n", ctx->sp);
+
+ while (1)
+ wfi();
+}
diff --git a/tftf/framework/aarch64/exceptions.S b/tftf/framework/aarch64/exceptions.S
index 37c5218..677b30f 100644
--- a/tftf/framework/aarch64/exceptions.S
+++ b/tftf/framework/aarch64/exceptions.S
@@ -8,10 +8,13 @@
.globl tftf_vector
-/* Exception vector code for unhandled exceptions. Loops forever. */
+/*
+ * Exception vector code for unhandled exceptions.
+ * Print a crash dump on the UART and loops forever.
+ */
.macro unhandled_exception name
vector_entry \name
- b \name
+ b crash_dump
end_vector_entry \name
.endm
@@ -53,10 +56,6 @@
unhandled_exception FiqA32
unhandled_exception SErrorA32
-/*
- * Exceptions will always be from the same exception level so no need to save
- * and restore SPSR.
- */
.macro save_gp_regs
stp x0, x1, [sp, #0x0]
stp x2, x3, [sp, #0x10]
@@ -73,13 +72,12 @@
stp x24, x25, [sp, #0xc0]
stp x26, x27, [sp, #0xd0]
stp x28, x29, [sp, #0xe0]
- mrs x0, sp_el0
- stp x30, x0, [sp, #0xf0]
+ /* We push xzr simply to keep the stack 16-byte aligned. */
+ stp x30, xzr, [sp, #0xf0]
.endm
.macro restore_gp_regs
- ldp x30, x0, [sp, #0xf0]
- msr sp_el0, x0
+ ldp x30, xzr, [sp, #0xf0]
ldp x28, x29, [sp, #0xe0]
ldp x26, x27, [sp, #0xd0]
ldp x24, x25, [sp, #0xc0]
@@ -105,3 +103,17 @@
add sp, sp, #0x100
eret
endfunc irq_vector_entry
+
+func crash_dump
+ /* Save general-purpose registers on the stack. */
+ sub sp, sp, #0x100
+ save_gp_regs
+
+ /* Save original stack pointer value on the stack. */
+ add x1, sp, #0x100
+ str x1, [sp, #0xf8]
+
+ /* Print the saved CPU context on the UART. */
+ mov x0, sp
+ b print_exception
+endfunc crash_dump
diff --git a/tftf/framework/framework.mk b/tftf/framework/framework.mk
index 81061d4..3bcb48a 100644
--- a/tftf/framework/framework.mk
+++ b/tftf/framework/framework.mk
@@ -53,6 +53,7 @@
framework/${ARCH}/asm_debug.S \
framework/${ARCH}/entrypoint.S \
framework/${ARCH}/exceptions.S \
+ framework/${ARCH}/exception_report.c \
framework/debug.c \
framework/main.c \
framework/nvm_results_helpers.c \