diff options
Diffstat (limited to 'roms/skiboot/core/exceptions.c')
-rw-r--r-- | roms/skiboot/core/exceptions.c | 233 |
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); + |