@@ -7,7 +7,7 @@ CORE_OBJS = relocate.o console.o stack.o init.o chip.o mem_region.o vm.o
CORE_OBJS += malloc.o lock.o cpu.o utils.o fdt.o opal.o interrupts.o timebase.o
CORE_OBJS += opal-msg.o pci.o pci-virt.o pci-slot.o pcie-slot.o
CORE_OBJS += pci-opal.o fast-reboot.o device.o exceptions.o trace.o affinity.o
-CORE_OBJS += vpd.o platform.o nvram.o nvram-format.o hmi.o
+CORE_OBJS += vpd.o platform.o nvram.o nvram-format.o hmi.o mce.o
CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o
CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o powercap.o psr.o
@@ -32,6 +32,7 @@ static void dump_regs(struct stack_frame *stack)
#define EXCEPTION_MAX_STR 320
+#if 0
static void print_recoverable_mce_vm(struct stack_frame *stack, uint64_t nip, uint64_t msr)
{
char buf[EXCEPTION_MAX_STR];
@@ -46,6 +47,7 @@ static void print_recoverable_mce_vm(struct stack_frame *stack, uint64_t nip, ui
dump_regs(stack);
prerror("Continuing with VM off\n");
}
+#endif
void exception_entry(struct stack_frame *stack)
{
@@ -103,7 +105,11 @@ void exception_entry(struct stack_frame *stack)
}
break;
- case 0x200:
+ case 0x200: {
+ uint64_t mce_flags, mce_addr;
+ const char *mce_err;
+
+#if 0
if (this_cpu()->vm_local_map_inuse)
fatal = true; /* local map is non-linear */
@@ -114,12 +120,29 @@ void exception_entry(struct stack_frame *stack)
stack->srr1 &= ~(MSR_IR|MSR_DR);
goto out;
}
+#endif
fatal = true;
prerror("***********************************************\n");
l += snprintf(buf + l, EXCEPTION_MAX_STR - l,
"Fatal MCE at "REG" ", 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);
+
+ decode_mce(stack->srr0, stack->srr1, stack->dsisr, stack->dar,
+ &mce_flags, &mce_err, &mce_addr);
+ 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 += snprintf(buf + l, EXCEPTION_MAX_STR - l,
+ "Effective address: 0x%016llx", mce_addr);
+ prerror("%s\n", buf);
+ }
+ goto no_symbol;
+ }
case 0x300:
if (vm_dsi(nip, stack->dar, !!(stack->dsisr & DSISR_ISSTORE)))
@@ -195,6 +218,7 @@ void exception_entry(struct stack_frame *stack)
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) {
new file mode 100644
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Deal with Machine Check Exceptions
+ *
+ * Copyright 2019 IBM Corp.
+ */
+
+#define pr_fmt(fmt) "MCE: " fmt
+
+#include <skiboot.h>
+#include <opal.h>
+#include <processor.h>
+#include <cpu.h>
+#include <cpu.h>
+
+static void flush_erat(void)
+{
+ asm volatile("slbia 7");
+}
+
+static void flush_tlb(void)
+{
+ /* XXX */
+}
+
+static void flush_all_tlb(void)
+{
+ /* XXX */
+}
+
+#define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42))
+
+#define CORRECTION_NONE 0
+#define CORRECTION_ERAT 1
+#define CORRECTION_TLB 2
+#define CORRECTION_TLBIE 3
+
+struct mce_ierror_table {
+ unsigned long srr1_mask;
+ unsigned long srr1_value;
+ uint64_t type;
+ const char *error_str;
+ int correction_type;
+};
+
+static const struct mce_ierror_table mce_p9_ierror_table[] = {
+{ 0x00000000081c0000, 0x0000000000040000,
+ MCE_INSNFETCH | MCE_MEMORY_ERROR | MCE_INVOLVED_EA,
+ "instruction fetch memory uncorrectable error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x0000000000080000,
+ MCE_INSNFETCH | MCE_SLB_ERROR | MCE_INVOLVED_EA,
+ "instruction fetch SLB parity error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x00000000000c0000,
+ MCE_INSNFETCH | MCE_SLB_ERROR | MCE_INVOLVED_EA,
+ "instruction fetch SLB multi-hit error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x0000000000100000,
+ MCE_INSNFETCH | MCE_INVOLVED_EA,
+ "instruction fetch ERAT multi-hit error",
+ CORRECTION_ERAT },
+{ 0x00000000081c0000, 0x0000000000140000,
+ MCE_INSNFETCH | MCE_INVOLVED_EA,
+ "instruction fetch TLB multi-hit error",
+ CORRECTION_TLB },
+{ 0x00000000081c0000, 0x0000000000180000,
+ MCE_INSNFETCH | MCE_MEMORY_ERROR | MCE_TABLE_WALK | MCE_INVOLVED_EA,
+ "instruction fetch page table access memory uncorrectable error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x00000000001c0000,
+ MCE_INSNFETCH | MCE_INVOLVED_EA,
+ "instruction fetch to foreign address",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x0000000008000000,
+ MCE_INSNFETCH | MCE_INVOLVED_EA,
+ "instruction fetch foreign link time-out",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x0000000008040000,
+ MCE_INSNFETCH | MCE_TABLE_WALK | MCE_INVOLVED_EA,
+ "instruction fetch page table access foreign link time-out",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x00000000080c0000,
+ MCE_INSNFETCH | MCE_INVOLVED_EA,
+ "instruction fetch real address error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x0000000008100000,
+ MCE_INSNFETCH | MCE_TABLE_WALK | MCE_INVOLVED_EA,
+ "instruction fetch page table access real address error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x0000000008140000,
+ MCE_LOADSTORE | MCE_IMPRECISE,
+ "store real address asynchronous error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x0000000008180000,
+ MCE_LOADSTORE | MCE_IMPRECISE,
+ "store foreign link time-out asynchronous error",
+ CORRECTION_NONE },
+{ 0x00000000081c0000, 0x00000000081c0000,
+ MCE_INSNFETCH | MCE_TABLE_WALK | MCE_INVOLVED_EA,
+ "instruction fetch page table access to foreign address",
+ CORRECTION_NONE },
+{ 0 } };
+
+struct mce_derror_table {
+ unsigned long dsisr_value;
+ uint64_t type;
+ const char *error_str;
+ int correction_type;
+};
+
+static const struct mce_derror_table mce_p9_derror_table[] = {
+{ 0x00008000,
+ MCE_LOADSTORE | MCE_MEMORY_ERROR,
+ "load/store memory uncorrectable error",
+ CORRECTION_NONE },
+{ 0x00004000,
+ MCE_LOADSTORE | MCE_MEMORY_ERROR | MCE_TABLE_WALK | MCE_INVOLVED_EA,
+ "load/store page table access memory uncorrectable error",
+ CORRECTION_NONE },
+{ 0x00002000,
+ MCE_LOADSTORE | MCE_INVOLVED_EA,
+ "load/store foreign link time-out",
+ CORRECTION_NONE },
+{ 0x00001000,
+ MCE_LOADSTORE | MCE_TABLE_WALK | MCE_INVOLVED_EA,
+ "load/store page table access foreign link time-out",
+ CORRECTION_NONE },
+{ 0x00000800,
+ MCE_LOADSTORE | MCE_INVOLVED_EA,
+ "load/store ERAT multi-hit error",
+ CORRECTION_ERAT },
+{ 0x00000400,
+ MCE_LOADSTORE | MCE_INVOLVED_EA,
+ "load/store TLB multi-hit error",
+ CORRECTION_TLB },
+{ 0x00000200,
+ MCE_LOADSTORE,
+ "TLBIE or TLBIEL instruction programming error",
+ CORRECTION_TLBIE },
+{ 0x00000100,
+ MCE_LOADSTORE | MCE_INVOLVED_EA | MCE_SLB_ERROR,
+ "load/store SLB parity error",
+ CORRECTION_NONE },
+{ 0x00000080,
+ MCE_LOADSTORE | MCE_INVOLVED_EA | MCE_SLB_ERROR,
+ "load/store SLB multi-hit error",
+ CORRECTION_NONE },
+{ 0x00000040,
+ MCE_LOADSTORE | MCE_INVOLVED_EA,
+ "load real address error",
+ CORRECTION_NONE },
+{ 0x00000020,
+ MCE_LOADSTORE | MCE_TABLE_WALK,
+ "load/store page table access real address error",
+ CORRECTION_NONE },
+{ 0x00000010,
+ MCE_LOADSTORE,
+ "load/store to foreign address",
+ CORRECTION_NONE },
+{ 0x00000008,
+ MCE_LOADSTORE | MCE_TABLE_WALK,
+ "load/store page table access to foreign address",
+ CORRECTION_NONE },
+{ 0 } };
+
+static void decode_ierror(const struct mce_ierror_table table[],
+ uint64_t srr1,
+ uint64_t *type,
+ const char **error_str,
+ int *correction_type)
+{
+ int i;
+
+ for (i = 0; table[i].srr1_mask; i++) {
+ if ((srr1 & table[i].srr1_mask) != table[i].srr1_value)
+ continue;
+
+ *type = table[i].type;
+ *error_str = table[i].error_str;
+ *correction_type = table[i].correction_type;
+ }
+}
+
+static void decode_derror(const struct mce_derror_table table[],
+ uint32_t dsisr,
+ uint64_t *type,
+ const char **error_str,
+ int *correction_type)
+{
+ int i;
+
+ for (i = 0; table[i].dsisr_value; i++) {
+ if (!(dsisr & table[i].dsisr_value))
+ continue;
+
+ *type = table[i].type;
+ *error_str = table[i].error_str;
+ *correction_type = table[i].correction_type;
+ }
+}
+
+static void __decode_mce(uint64_t srr0, uint64_t srr1,
+ uint32_t dsisr, uint64_t dar,
+ uint64_t *type, const char **error_str,
+ uint64_t *address, int *correction_type)
+{
+ /*
+ * On POWER9 DD2.1 and below, it's possible to get a machine check
+ * caused by a paste instruction where only DSISR bit 25 is set. This
+ * will result in the MCE handler seeing an unknown event and the kernel
+ * crashing. An MCE that occurs like this is spurious, so we don't need
+ * to do anything in terms of servicing it. If there is something that
+ * needs to be serviced, the CPU will raise the MCE again with the
+ * correct DSISR so that it can be serviced properly. So detect this
+ * case and mark it as handled.
+ */
+ if (SRR1_MC_LOADSTORE(srr1) && dsisr == 0x02000000) {
+ *type = MCE_NO_ERROR;
+ return;
+ }
+
+ *type = MCE_UNKNOWN;
+ *error_str = "unknown error";
+ *address = 0;
+ *correction_type = CORRECTION_NONE;
+
+ if (SRR1_MC_LOADSTORE(srr1)) {
+ decode_derror(mce_p9_derror_table,
+ dsisr, type, error_str, correction_type);
+ if (*type & MCE_INVOLVED_EA)
+ *address = dar;
+ } else {
+ decode_ierror(mce_p9_ierror_table,
+ srr1, type, error_str, correction_type);
+ if (*type & MCE_INVOLVED_EA)
+ *address = srr0;
+ }
+}
+
+void decode_mce(uint64_t srr0, uint64_t srr1,
+ uint32_t dsisr, uint64_t dar,
+ uint64_t *type, const char **error_str,
+ uint64_t *address)
+{
+ int correction_type;
+ __decode_mce(srr0, srr1, dsisr, dar, type, error_str, address,
+ &correction_type);
+}
+
+static int64_t opal_handle_mce(struct opal_mce *opal_mce)
+{
+ uint64_t flags = be64_to_cpu(opal_mce->flags);
+ int correction_type;
+ uint64_t type;
+ uint64_t ea;
+ uint64_t len;
+ const char *error_str;
+ const char *handled_str = ""; /* silence gcc warning */
+ char *outbuf;
+
+ if (proc_gen != proc_gen_p9)
+ return OPAL_UNSUPPORTED;
+
+ __decode_mce( be64_to_cpu(opal_mce->srr0),
+ be64_to_cpu(opal_mce->srr1),
+ be64_to_cpu(opal_mce->dsisr),
+ be64_to_cpu(opal_mce->dar),
+ &type, &error_str, &ea, &correction_type);
+
+ if (flags & MCE_HANDLE_CORRECT) {
+ /* Attempt to correct */
+ // uint64_t insn = be64_to_cpu(opal_mce->insn);
+
+ if (correction_type == CORRECTION_ERAT) {
+ flush_erat();
+ flags |= MCE_HANDLE_CORRECTED;
+ handled_str = "ERAT flush";
+ } else if (correction_type == CORRECTION_TLB) {
+ flush_tlb();
+ flags |= MCE_HANDLE_CORRECTED;
+ handled_str = "TLB flush";
+ } else if (correction_type == CORRECTION_TLBIE) {
+ flush_all_tlb();
+ flags |= MCE_HANDLE_CORRECTED | MCE_HANDLE_EMULATED;
+ handled_str = "global TLB flush";
+ }
+ }
+
+ opal_mce->flags = cpu_to_be64(flags);
+ opal_mce->type = cpu_to_be64(type);
+ opal_mce->ea = cpu_to_be64(ea);
+
+ len = be64_to_cpu(opal_mce->errorlen);
+ outbuf = (char *)be64_to_cpu(opal_mce->errorbuf);
+ strncpy(outbuf, error_str, len);
+
+ if (flags & MCE_HANDLE_CORRECTED) {
+ len = be64_to_cpu(opal_mce->handledlen);
+ outbuf = (char *)be64_to_cpu(opal_mce->handledbuf);
+ strncpy(outbuf, handled_str, len);
+ }
+
+ return OPAL_SUCCESS;
+}
+opal_call(OPAL_HANDLE_MCE, opal_handle_mce, 1);
@@ -230,7 +230,8 @@
#define OPAL_GET_SYMBOL 181
#define OPAL_LOOKUP_SYMBOL 182
#define OPAL_REGISTER_OS_OPS 183
-#define OPAL_LAST 183
+#define OPAL_HANDLE_MCE 184
+#define OPAL_LAST 184
#define QUIESCE_HOLD 1 /* Spin all calls at entry */
#define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */
@@ -1264,6 +1265,44 @@ struct opal_os_ops {
__be64 os_printf; /* void printf(int32_t level, const char *str) */
};
+#define MCE_HANDLE_CORRECT 0x0001 /* Attempt to correct */
+#define MCE_HANDLE_CORRECTED 0x1000 /* Error attmped to be corrected */
+#define MCE_HANDLE_EMULATED 0x2000 /* Should advance NIA */
+#define MCE_HANDLE_NEED_INSN 0x4000 /* Try again with insn field */
+
+
+#define MCE_NO_ERROR 0x0001
+#define MCE_UNKNOWN 0x0002
+#define MCE_INSNFETCH 0x0004
+#define MCE_LOADSTORE 0x0008
+#define MCE_TABLE_WALK 0x0010
+#define MCE_IMPRECISE 0x0020
+#define MCE_MEMORY_ERROR 0x0040
+#define MCE_SLB_ERROR 0x0080
+#define MCE_INVOLVED_EA 0x0100
+#define MCE_INVOLVED_PA 0x0200
+
+struct opal_mce {
+ /* Input and output */
+ __be64 flags; /* How it should be handled / how it was handled */
+
+ /* Inputs */
+ __be64 srr0;
+ __be64 srr1;
+ __be32 dsisr;
+ __be32 reserved;
+ __be64 dar;
+ __be64 insn; /* Zero if instruction was not read */
+
+ /* Outputs */
+ __be64 type;
+ __be64 ea;
+ __be64 pa;
+ __be64 errorbuf; /* pointer to buffer for string */
+ __be64 errorlen;
+ __be64 handledbuf;
+ __be64 handledlen;
+};
#endif /* __ASSEMBLY__ */
#endif /* __OPAL_API_H */
@@ -181,6 +181,12 @@ extern char __sym_map_start[];
extern char __sym_map_end[];
extern size_t snprintf_symbol(char *buf, size_t len, uint64_t addr);
+/* RAS */
+void decode_mce(uint64_t srr0, uint64_t srr1,
+ uint32_t dsisr, uint64_t dar,
+ uint64_t *flags, const char **error_str,
+ uint64_t *address);
+
/* Direct controls */
extern void direct_controls_init(void);
extern int64_t opal_signal_system_reset(int cpu_nr);