blob: 389548d164580d37cbbdcb00bb29832504b474ea [file] [log] [blame]
// 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);