|
@@ -523,6 +523,136 @@ int pSeries_system_reset_exception(struct pt_regs *regs)
|
|
|
return 0; /* need to perform reset */
|
|
|
}
|
|
|
|
|
|
+#define VAL_TO_STRING(ar, val) \
|
|
|
+ (((val) < ARRAY_SIZE(ar)) ? ar[(val)] : "Unknown")
|
|
|
+
|
|
|
+static void pseries_print_mce_info(struct pt_regs *regs,
|
|
|
+ struct rtas_error_log *errp)
|
|
|
+{
|
|
|
+ const char *level, *sevstr;
|
|
|
+ struct pseries_errorlog *pseries_log;
|
|
|
+ struct pseries_mc_errorlog *mce_log;
|
|
|
+ u8 error_type, err_sub_type;
|
|
|
+ u64 addr;
|
|
|
+ u8 initiator = rtas_error_initiator(errp);
|
|
|
+ int disposition = rtas_error_disposition(errp);
|
|
|
+
|
|
|
+ static const char * const initiators[] = {
|
|
|
+ "Unknown",
|
|
|
+ "CPU",
|
|
|
+ "PCI",
|
|
|
+ "ISA",
|
|
|
+ "Memory",
|
|
|
+ "Power Mgmt",
|
|
|
+ };
|
|
|
+ static const char * const mc_err_types[] = {
|
|
|
+ "UE",
|
|
|
+ "SLB",
|
|
|
+ "ERAT",
|
|
|
+ "TLB",
|
|
|
+ "D-Cache",
|
|
|
+ "Unknown",
|
|
|
+ "I-Cache",
|
|
|
+ };
|
|
|
+ static const char * const mc_ue_types[] = {
|
|
|
+ "Indeterminate",
|
|
|
+ "Instruction fetch",
|
|
|
+ "Page table walk ifetch",
|
|
|
+ "Load/Store",
|
|
|
+ "Page table walk Load/Store",
|
|
|
+ };
|
|
|
+
|
|
|
+ /* SLB sub errors valid values are 0x0, 0x1, 0x2 */
|
|
|
+ static const char * const mc_slb_types[] = {
|
|
|
+ "Parity",
|
|
|
+ "Multihit",
|
|
|
+ "Indeterminate",
|
|
|
+ };
|
|
|
+
|
|
|
+ /* TLB and ERAT sub errors valid values are 0x1, 0x2, 0x3 */
|
|
|
+ static const char * const mc_soft_types[] = {
|
|
|
+ "Unknown",
|
|
|
+ "Parity",
|
|
|
+ "Multihit",
|
|
|
+ "Indeterminate",
|
|
|
+ };
|
|
|
+
|
|
|
+ if (!rtas_error_extended(errp)) {
|
|
|
+ pr_err("Machine check interrupt: Missing extended error log\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ pseries_log = get_pseries_errorlog(errp, PSERIES_ELOG_SECT_ID_MCE);
|
|
|
+ if (pseries_log == NULL)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mce_log = (struct pseries_mc_errorlog *)pseries_log->data;
|
|
|
+
|
|
|
+ error_type = mce_log->error_type;
|
|
|
+ err_sub_type = rtas_mc_error_sub_type(mce_log);
|
|
|
+
|
|
|
+ switch (rtas_error_severity(errp)) {
|
|
|
+ case RTAS_SEVERITY_NO_ERROR:
|
|
|
+ level = KERN_INFO;
|
|
|
+ sevstr = "Harmless";
|
|
|
+ break;
|
|
|
+ case RTAS_SEVERITY_WARNING:
|
|
|
+ level = KERN_WARNING;
|
|
|
+ sevstr = "";
|
|
|
+ break;
|
|
|
+ case RTAS_SEVERITY_ERROR:
|
|
|
+ case RTAS_SEVERITY_ERROR_SYNC:
|
|
|
+ level = KERN_ERR;
|
|
|
+ sevstr = "Severe";
|
|
|
+ break;
|
|
|
+ case RTAS_SEVERITY_FATAL:
|
|
|
+ default:
|
|
|
+ level = KERN_ERR;
|
|
|
+ sevstr = "Fatal";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
|
|
|
+ disposition == RTAS_DISP_FULLY_RECOVERED ?
|
|
|
+ "Recovered" : "Not recovered");
|
|
|
+ if (user_mode(regs)) {
|
|
|
+ printk("%s NIP: [%016lx] PID: %d Comm: %s\n", level,
|
|
|
+ regs->nip, current->pid, current->comm);
|
|
|
+ } else {
|
|
|
+ printk("%s NIP [%016lx]: %pS\n", level, regs->nip,
|
|
|
+ (void *)regs->nip);
|
|
|
+ }
|
|
|
+ printk("%s Initiator: %s\n", level,
|
|
|
+ VAL_TO_STRING(initiators, initiator));
|
|
|
+
|
|
|
+ switch (error_type) {
|
|
|
+ case MC_ERROR_TYPE_UE:
|
|
|
+ printk("%s Error type: %s [%s]\n", level,
|
|
|
+ VAL_TO_STRING(mc_err_types, error_type),
|
|
|
+ VAL_TO_STRING(mc_ue_types, err_sub_type));
|
|
|
+ break;
|
|
|
+ case MC_ERROR_TYPE_SLB:
|
|
|
+ printk("%s Error type: %s [%s]\n", level,
|
|
|
+ VAL_TO_STRING(mc_err_types, error_type),
|
|
|
+ VAL_TO_STRING(mc_slb_types, err_sub_type));
|
|
|
+ break;
|
|
|
+ case MC_ERROR_TYPE_ERAT:
|
|
|
+ case MC_ERROR_TYPE_TLB:
|
|
|
+ printk("%s Error type: %s [%s]\n", level,
|
|
|
+ VAL_TO_STRING(mc_err_types, error_type),
|
|
|
+ VAL_TO_STRING(mc_soft_types, err_sub_type));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ printk("%s Error type: %s\n", level,
|
|
|
+ VAL_TO_STRING(mc_err_types, error_type));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ addr = rtas_mc_get_effective_addr(mce_log);
|
|
|
+ if (addr)
|
|
|
+ printk("%s Effective address: %016llx\n", level, addr);
|
|
|
+}
|
|
|
+
|
|
|
static int mce_handle_error(struct rtas_error_log *errp)
|
|
|
{
|
|
|
struct pseries_errorlog *pseries_log;
|
|
@@ -585,8 +715,11 @@ static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err)
|
|
|
int recovered = 0;
|
|
|
int disposition = rtas_error_disposition(err);
|
|
|
|
|
|
+ pseries_print_mce_info(regs, err);
|
|
|
+
|
|
|
if (!(regs->msr & MSR_RI)) {
|
|
|
/* If MSR_RI isn't set, we cannot recover */
|
|
|
+ pr_err("Machine check interrupt unrecoverable: MSR(RI=0)\n");
|
|
|
recovered = 0;
|
|
|
|
|
|
} else if (disposition == RTAS_DISP_FULLY_RECOVERED) {
|