233 lines
5.5 KiB
C
233 lines
5.5 KiB
C
// 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);
|
|
|