feat(runtime/core): add RMM register crash dump

Adds RMM logging of register crash dump
for R-EL2 synchronous exception.

Change-Id: Ifea7c22c0a985edd0d99708c58b358011ad67f9d
Signed-off-by: AlexeiFedorov <Alexei.Fedorov@arm.com>
diff --git a/runtime/core/handler.c b/runtime/core/handler.c
index a574d20..6b85990 100644
--- a/runtime/core/handler.c
+++ b/runtime/core/handler.c
@@ -7,6 +7,7 @@
 #include <arch_helpers.h>
 #include <assert.h>
 #include <buffer.h>
+#include <cpuid.h>
 #include <debug.h>
 #include <run.h>
 #include <simd.h>
@@ -15,6 +16,7 @@
 #include <smc.h>
 #include <status.h>
 #include <utils_def.h>
+#include <xlat_high_va.h>
 
 /* Maximum number of supported arguments */
 #define MAX_NUM_ARGS		5U
@@ -342,17 +344,6 @@
 	assert(check_cpu_slots_empty());
 }
 
-static void report_unexpected(void)
-{
-	ERROR("----\n");
-	ERROR("Unexpected exception:\n");
-	ERROR("SPSR_EL2: 0x%016lx\n", read_spsr_el2());
-	ERROR("ESR_EL2:  0x%016lx\n", read_esr_el2());
-	ERROR("ELR_EL2:  0x%016lx\n", read_elr_el2());
-	ERROR("FAR_EL2:  0x%016lx\n", read_far_el2());
-	ERROR("----\n");
-}
-
 /*
  * Identifies an abort that the RMM may recover from.
  */
@@ -388,13 +379,52 @@
 };
 #define RMM_TRAP_LIST_SIZE (sizeof(rmm_trap_list)/sizeof(struct rmm_trap_element))
 
-__dead2 static void fatal_abort(void)
-{
-	report_unexpected();
+/* Crash dump registers X0 - X29 */
+#define DUMP_REGS	30U
 
-	while (true) {
-		wfe();
+typedef struct {
+	uint64_t x[DUMP_REGS];
+	uint64_t lr;
+} dump_regs_t;
+
+__dead2 static void fatal_abort(dump_regs_t *regs)
+{
+	__unused uint64_t sp_el2;
+	__unused uint64_t sp_el0 = (uint64_t)regs + sizeof(dump_regs_t);
+	__unused uint64_t lr = regs->lr;
+
+	ERROR("Unexpected exception on CPU #%u:\n", my_cpuid());
+
+	for (unsigned int i = 0U; i < DUMP_REGS; i += 2U) {
+		INFO("X%u:\t\t0x%016lx   X%u:\t\t0x%016lx\n",
+			i, regs->x[i], i + 1U, regs->x[i + 1U]);
 	}
+
+	/* Demangle return address */
+	xpaci(lr);
+
+	/* Switch SPSEL to read SP_EL2 register */
+	write_spsel(MODE_SP_ELX);
+	read_sp(sp_el2);
+	write_spsel(MODE_SP_EL0);
+
+	INFO("LR:\t\t0x%016lx\n", lr);
+	INFO("SP_EL0:\t\t0x%016lx\n", sp_el0);
+	INFO("SP_EL2:\t\t0x%016lx%c (0x%016lx-0x%016lx)\n", sp_el2,
+		((sp_el2 < CPU_STACK_VIRT) ||
+		 (sp_el2 >= RMM_CPU_STACK_END_VA)) ? '*' : ' ',
+		CPU_STACK_VIRT, (RMM_CPU_STACK_END_VA - 1UL));
+	INFO("ESR_EL2:\t0x%016lx\n", read_esr_el2());
+	INFO("ELR_EL2:\t0x%016lx\n", read_elr_el2());
+	INFO("FAR_EL2:\t0x%016lx\n", read_far_el2());
+	INFO("SPSR_EL2:\t0x%016lx\n", read_spsr_el2());
+	INFO("HCR_EL2:\t0x%016lx   E2H = %c\n", read_hcr_el2(),
+		((read_hcr_el2() & HCR_E2H) == 0UL) ? '0' : '1');
+	INFO("CPTR_EL2:\t0x%016lx\n", read_cptr_el2());
+	INFO("MDCR_EL2:\t0x%016lx\n", read_mdcr_el2());
+	INFO("SCTLR_EL2:\t0x%016lx\n", read_sctlr_el2());
+
+	panic();
 }
 
 static bool is_el2_data_abort_gpf(unsigned long esr)
@@ -414,7 +444,7 @@
  * If no match is found, it aborts the RMM.
  */
 /* coverity[misra_c_2012_rule_8_4_violation:SUPPRESS] */
-unsigned long handle_rmm_trap(void)
+unsigned long handle_rmm_trap(dump_regs_t *regs)
 {
 	unsigned long esr = read_esr_el2();
 	unsigned long elr = read_elr_el2();
@@ -423,7 +453,7 @@
 	 * Only the GPF data aborts are recoverable.
 	 */
 	if (!is_el2_data_abort_gpf(esr)) {
-		fatal_abort();
+		fatal_abort(regs);
 	}
 
 	for (unsigned int i = 0U; i < RMM_TRAP_LIST_SIZE; i++) {
@@ -432,6 +462,6 @@
 		}
 	}
 
-	fatal_abort();
+	fatal_abort(regs);
 	return 0UL;
 }