aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/exceptions.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/exceptions.c')
-rw-r--r--roms/skiboot/core/exceptions.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/roms/skiboot/core/exceptions.c b/roms/skiboot/core/exceptions.c
new file mode 100644
index 000000000..389548d16
--- /dev/null
+++ b/roms/skiboot/core/exceptions.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Deal with exceptions when in OPAL.
+ *
+ * Copyright 2013-2014 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <stack.h>
+#include <opal.h>
+#include <processor.h>
+#include <cpu.h>
+#include <ras.h>
+
+#define REG "%016llx"
+#define REG32 "%08x"
+#define REGS_PER_LINE 4
+
+static void dump_regs(struct stack_frame *stack)
+{
+ unsigned int i;
+
+ prerror("CFAR : "REG" MSR : "REG"\n", stack->cfar, stack->msr);
+ prerror("SRR0 : "REG" SRR1 : "REG"\n", stack->srr0, stack->srr1);
+ prerror("HSRR0: "REG" HSRR1: "REG"\n", stack->hsrr0, stack->hsrr1);
+ prerror("DSISR: "REG32" DAR : "REG"\n", stack->dsisr, stack->dar);
+ prerror("LR : "REG" CTR : "REG"\n", stack->lr, stack->ctr);
+ prerror("CR : "REG32" XER : "REG32"\n", stack->cr, stack->xer);
+ for (i = 0; i < 16; i++)
+ prerror("GPR%02d: "REG" GPR%02d: "REG"\n",
+ i, stack->gpr[i], i + 16, stack->gpr[i + 16]);
+}
+
+#define EXCEPTION_MAX_STR 320
+
+static void handle_mce(struct stack_frame *stack, uint64_t nip, uint64_t msr, bool *fatal)
+{
+ uint64_t mce_flags, mce_addr;
+ const char *mce_err;
+ const char *mce_fix = NULL;
+ char buf[EXCEPTION_MAX_STR];
+ size_t l;
+
+ decode_mce(stack->srr0, stack->srr1, stack->dsisr, stack->dar,
+ &mce_flags, &mce_err, &mce_addr);
+
+ /* Try to recover. */
+ if (mce_flags & MCE_ERAT_ERROR) {
+ /* Real-mode still uses ERAT, flush transient bitflips */
+ flush_erat();
+ mce_fix = "ERAT flush";
+
+ } else {
+ *fatal = true;
+ }
+
+ prerror("***********************************************\n");
+ l = 0;
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "%s MCE at "REG" ", *fatal ? "Fatal" : "Non-fatal", nip);
+ l += snprintf_symbol(buf + l, EXCEPTION_MAX_STR - l, nip);
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l, " MSR "REG, msr);
+ prerror("%s\n", buf);
+
+ l = 0;
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Cause: %s", mce_err);
+ prerror("%s\n", buf);
+ if (mce_flags & MCE_INVOLVED_EA) {
+ l = 0;
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Effective address: 0x%016llx", mce_addr);
+ prerror("%s\n", buf);
+ }
+
+ if (!*fatal) {
+ l = 0;
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Attempting recovery: %s", mce_fix);
+ prerror("%s\n", buf);
+ }
+}
+
+void exception_entry(struct stack_frame *stack)
+{
+ bool fatal = false;
+ bool hv;
+ uint64_t nip;
+ uint64_t msr;
+ char buf[EXCEPTION_MAX_STR];
+ size_t l;
+
+ switch (stack->type) {
+ case 0x500:
+ case 0x980:
+ case 0xe00:
+ case 0xe20:
+ case 0xe40:
+ case 0xe60:
+ case 0xe80:
+ case 0xea0:
+ case 0xf80:
+ hv = true;
+ break;
+ default:
+ hv = false;
+ break;
+ }
+
+ if (hv) {
+ nip = stack->hsrr0;
+ msr = stack->hsrr1;
+ } else {
+ nip = stack->srr0;
+ msr = stack->srr1;
+ }
+ stack->msr = msr;
+ stack->pc = nip;
+
+ if (!(msr & MSR_RI))
+ fatal = true;
+
+ l = 0;
+ switch (stack->type) {
+ case 0x100:
+ prerror("***********************************************\n");
+ if (fatal) {
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Fatal System Reset at "REG" ", nip);
+ } else {
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "System Reset at "REG" ", nip);
+ }
+ break;
+
+ case 0x200:
+ handle_mce(stack, nip, msr, &fatal);
+ goto no_symbol;
+
+ case 0x700: {
+ struct trap_table_entry *tte;
+
+ fatal = true;
+ prerror("***********************************************\n");
+ for (tte = __trap_table_start; tte < __trap_table_end; tte++) {
+ if (tte->address == nip) {
+ prerror("< %s >\n", tte->message);
+ prerror(" .\n");
+ prerror(" .\n");
+ prerror(" .\n");
+ prerror(" OO__)\n");
+ prerror(" <\"__/\n");
+ prerror(" ^ ^\n");
+ break;
+ }
+ }
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Fatal TRAP at "REG" ", nip);
+ l += snprintf_symbol(buf + l, EXCEPTION_MAX_STR - l, nip);
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l, " MSR "REG, msr);
+ prerror("%s\n", buf);
+ dump_regs(stack);
+ backtrace_r1((uint64_t)stack);
+ if (platform.terminate)
+ platform.terminate(buf);
+ for (;;) ;
+ break; }
+
+ default:
+ fatal = true;
+ prerror("***********************************************\n");
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Fatal Exception 0x%llx at "REG" ", stack->type, nip);
+ break;
+ }
+ l += snprintf_symbol(buf + l, EXCEPTION_MAX_STR - l, nip);
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l, " MSR "REG, msr);
+ prerror("%s\n", buf);
+no_symbol:
+ dump_regs(stack);
+ backtrace_r1((uint64_t)stack);
+ if (fatal) {
+ if (platform.terminate)
+ platform.terminate(buf);
+ for (;;) ;
+ }
+
+ if (hv) {
+ /* Set up for SRR return */
+ stack->srr0 = nip;
+ stack->srr1 = msr;
+ }
+}
+
+void exception_entry_pm_sreset(void)
+{
+ char buf[EXCEPTION_MAX_STR];
+ size_t l;
+
+ prerror("***********************************************\n");
+ l = 0;
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "System Reset in sleep");
+ prerror("%s\n", buf);
+ backtrace();
+}
+
+void __noreturn exception_entry_pm_mce(void)
+{
+ char buf[EXCEPTION_MAX_STR];
+ size_t l;
+
+ prerror("***********************************************\n");
+ l = 0;
+ l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Fatal MCE in sleep");
+ prerror("%s\n", buf);
+ prerror("SRR0 : "REG" SRR1 : "REG"\n",
+ (uint64_t)mfspr(SPR_SRR0), (uint64_t)mfspr(SPR_SRR1));
+ prerror("DSISR: "REG32" DAR : "REG"\n",
+ (uint32_t)mfspr(SPR_DSISR), (uint64_t)mfspr(SPR_DAR));
+ abort();
+}
+
+static int64_t opal_register_exc_handler(uint64_t opal_exception __unused,
+ uint64_t handler_address __unused,
+ uint64_t glue_cache_line __unused)
+{
+ /* This interface is deprecated */
+ return OPAL_UNSUPPORTED;
+}
+opal_call(OPAL_REGISTER_OPAL_EXCEPTION_HANDLER, opal_register_exc_handler, 3);
+