// SPDX-License-Identifier: GPL-2.0-or-later/* * Machine check exception handling. * * Copyright 2013 IBM Corporation * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> */ #undef DEBUG#define pr_fmt(fmt) "mce: " fmt #include <linux/hardirq.h>#include <linux/types.h>#include <linux/ptrace.h>#include <linux/percpu.h>#include <linux/export.h>#include <linux/irq_work.h>#include <linux/extable.h>#include <linux/ftrace.h>#include <linux/memblock.h>#include <linux/of.h> #include <asm/interrupt.h>#include <asm/machdep.h>#include <asm/mce.h>#include <asm/nmi.h> #include "setup.h" static void machine_check_ue_event(struct machine_check_event *evt);static void machine_process_ue_event(struct work_struct *work); static DECLARE_WORK(mce_ue_event_work, machine_process_ue_event); static BLOCKING_NOTIFIER_HEAD(mce_notifier_list); int mce_register_notifier(struct notifier_block *nb){ return blocking_notifier_chain_register(&mce_notifier_list, nb);}EXPORT_SYMBOL_GPL(mce_register_notifier); int mce_unregister_notifier(struct notifier_block *nb){ return blocking_notifier_chain_unregister(&mce_notifier_list, nb);}EXPORT_SYMBOL_GPL(mce_unregister_notifier); static void mce_set_error_info(struct machine_check_event *mce, struct mce_error_info *mce_err){ mce->error_type = mce_err->error_type; switch (mce_err->error_type) { case MCE_ERROR_TYPE_UE: mce->u.ue_error.ue_error_type = mce_err->u.ue_error_type; break; case MCE_ERROR_TYPE_SLB: mce->u.slb_error.slb_error_type = mce_err->u.slb_error_type; break; case MCE_ERROR_TYPE_ERAT: mce->u.erat_error.erat_error_type = mce_err->u.erat_error_type; break; case MCE_ERROR_TYPE_TLB: mce->u.tlb_error.tlb_error_type = mce_err->u.tlb_error_type; break; case MCE_ERROR_TYPE_USER: mce->u.user_error.user_error_type = mce_err->u.user_error_type; break; case MCE_ERROR_TYPE_RA: mce->u.ra_error.ra_error_type = mce_err->u.ra_error_type; break; case MCE_ERROR_TYPE_LINK: mce->u.link_error.link_error_type = mce_err->u.link_error_type; break; case MCE_ERROR_TYPE_UNKNOWN: default: break; }} void mce_irq_work_queue(void){ /* Raise decrementer interrupt */ arch_irq_work_raise(); set_mce_pending_irq_work();} /* * Decode and save high level MCE information into per cpu buffer which * is an array of machine_check_event structure. */void save_mce_event(struct pt_regs *regs, long handled, struct mce_error_info *mce_err, uint64_t nip, uint64_t addr, uint64_t phys_addr){ int index = local_paca->mce_info->mce_nest_count++; struct machine_check_event *mce; mce = &local_paca->mce_info->mce_event[index]; /* * Return if we don't have enough space to log mce event. * mce_nest_count may go beyond MAX_MC_EVT but that's ok, * the check below will stop buffer overrun. */ if (index >= MAX_MC_EVT) return; /* Populate generic machine check info */ mce->version = MCE_V1; mce->srr0 = nip; mce->srr1 = regs->msr; mce->gpr3 = regs->gpr[3]; mce->in_use = 1; mce->cpu = get_paca()->paca_index; /* Mark it recovered if we have handled it and MSR(RI=1). */ if (handled && (regs->msr & MSR_RI)) mce->disposition = MCE_DISPOSITION_RECOVERED; else mce->disposition = MCE_DISPOSITION_NOT_RECOVERED; mce->initiator = mce_err->initiator; mce->severity = mce_err->severity; mce->sync_error = mce_err->sync_error; mce->error_class = mce_err->error_class; /* * Populate the mce error_type and type-specific error_type. */ mce_set_error_info(mce, mce_err); if (mce->error_type == MCE_ERROR_TYPE_UE) mce->u.ue_error.ignore_event = mce_err->ignore_event; /* * Raise irq work, So that we don't miss to log the error for * unrecoverable errors. */ if (mce->disposition == MCE_DISPOSITION_NOT_RECOVERED) mce_irq_work_queue(); if (!addr) return; if (mce->error_type == MCE_ERROR_TYPE_TLB) { mce->u.tlb_error.effective_address_provided = true; mce->u.tlb_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_SLB) { mce->u.slb_error.effective_address_provided = true; mce->u.slb_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_ERAT) { mce->u.erat_error.effective_address_provided = true; mce->u.erat_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_USER) { mce->u.user_error.effective_address_provided = true; mce->u.user_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_RA) { mce->u.ra_error.effective_address_provided = true; mce->u.ra_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_LINK) { mce->u.link_error.effective_address_provided = true; mce->u.link_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_UE) { mce->u.ue_error.effective_address_provided = true; mce->u.ue_error.effective_address = addr; if (phys_addr != ULONG_MAX) { mce->u.ue_error.physical_address_provided = true; mce->u.ue_error.physical_address = phys_addr; machine_check_ue_event(mce); } } return;} /* * get_mce_event: * mce Pointer to machine_check_event structure to be filled. * release Flag to indicate whether to free the event slot or not. * 0 <= do not release the mce event. Caller will invoke * release_mce_event() once event has been consumed. * 1 <= release the slot. * * return 1 = success * 0 = failure * * get_mce_event() will be called by platform specific machine check * handle routine and in KVM. * When we call get_mce_event(), we are still in interrupt context and * preemption will not be scheduled until ret_from_expect() routine * is called. */int get_mce_event(struct machine_check_event *mce, bool release){ int index = local_paca->mce_info->mce_nest_count - 1; struct machine_check_event *mc_evt; int ret = 0; /* Sanity check */ if (index < 0) return ret; /* Check if we have MCE info to process. */ if (index < MAX_MC_EVT) { mc_evt = &local_paca->mce_info->mce_event[index]; /* Copy the event structure and release the original */ if (mce) *mce = *mc_evt; if (release) mc_evt->in_use = 0; ret = 1; } /* Decrement the count to free the slot. */ if (release) local_paca->mce_info->mce_nest_count--; return ret;} void release_mce_event(void){ get_mce_event(NULL, true);} static void machine_check_ue_work(void){ schedule_work(&mce_ue_event_work);} /* * Queue up the MCE event which then can be handled later. */static void machine_check_ue_event(struct machine_check_event *evt){ int index; index = local_paca->mce_info->mce_ue_count++; /* If queue is full, just return for now. */ if (index >= MAX_MC_EVT) { local_paca->mce_info->mce_ue_count--; return; } memcpy(&local_paca->mce_info->mce_ue_event_queue[index], evt, sizeof(*evt));} /* * Queue up the MCE event which then can be handled later. */void machine_check_queue_event(void){ int index; struct machine_check_event evt; if (!get_mce_event(&evt, MCE_EVENT_RELEASE)) return; index = local_paca->mce_info->mce_queue_count++; /* If queue is full, just return for now. */ if (index >= MAX_MC_EVT) { local_paca->mce_info->mce_queue_count--; return; } memcpy(&local_paca->mce_info->mce_event_queue[index], &evt, sizeof(evt)); mce_irq_work_queue();} void mce_common_process_ue(struct pt_regs *regs, struct mce_error_info *mce_err){ const struct exception_table_entry *entry; entry = search_kernel_exception_table(regs->nip); if (entry) { mce_err->ignore_event = true; regs_set_return_ip(regs, extable_fixup(entry)); }} /* * process pending MCE event from the mce event queue. This function will be * called during syscall exit. */static void machine_process_ue_event(struct work_struct *work){ int index; struct machine_check_event *evt; while (local_paca->mce_info->mce_ue_count > 0) { index = local_paca->mce_info->mce_ue_count - 1; evt = &local_paca->mce_info->mce_ue_event_queue[index]; blocking_notifier_call_chain(&mce_notifier_list, 0, evt);#ifdef CONFIG_MEMORY_FAILURE /* * This should probably queued elsewhere, but * oh! well * * Don't report this machine check because the caller has a * asked us to ignore the event, it has a fixup handler which * will do the appropriate error handling and reporting. */ if (evt->error_type == MCE_ERROR_TYPE_UE) { if (evt->u.ue_error.ignore_event) { local_paca->mce_info->mce_ue_count--; continue; } if (evt->u.ue_error.physical_address_provided) { unsigned long pfn; pfn = evt->u.ue_error.physical_address >> PAGE_SHIFT; memory_failure(pfn, 0); } else pr_warn("Failed to identify bad address from " "where the uncorrectable error (UE) " "was generated\n"); }#endif local_paca->mce_info->mce_ue_count--; }}/* * process pending MCE event from the mce event queue. This function will be * called during syscall exit. */static void machine_check_process_queued_event(void){ int index; struct machine_check_event *evt; add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); /* * For now just print it to console. * TODO: log this error event to FSP or nvram. */ while (local_paca->mce_info->mce_queue_count > 0) { index = local_paca->mce_info->mce_queue_count - 1; evt = &local_paca->mce_info->mce_event_queue[index]; if (evt->error_type == MCE_ERROR_TYPE_UE && evt->u.ue_error.ignore_event) { local_paca->mce_info->mce_queue_count--; continue; } machine_check_print_event_info(evt, false, false); local_paca->mce_info->mce_queue_count--; }} void set_mce_pending_irq_work(void){ local_paca->mce_pending_irq_work = 1;} void clear_mce_pending_irq_work(void){ local_paca->mce_pending_irq_work = 0;} void mce_run_irq_context_handlers(void){ if (unlikely(local_paca->mce_pending_irq_work)) { if (ppc_md.machine_check_log_err) ppc_md.machine_check_log_err(); machine_check_process_queued_event(); machine_check_ue_work(); clear_mce_pending_irq_work(); }} void machine_check_print_event_info(struct machine_check_event *evt, bool user_mode, bool in_guest){ const char *level, *sevstr, *subtype, *err_type, *initiator; uint64_t ea = 0, pa = 0; int n = 0; char dar_str[50]; char pa_str[50]; static const char *mc_ue_types[] = { "Indeterminate", "Instruction fetch", "Page table walk ifetch", "Load/Store", "Page table walk Load/Store", }; static const char *mc_slb_types[] = { "Indeterminate", "Parity", "Multihit", }; static const char *mc_erat_types[] = { "Indeterminate", "Parity", "Multihit", }; static const char *mc_tlb_types[] = { "Indeterminate", "Parity", "Multihit", }; static const char *mc_user_types[] = { "Indeterminate", "tlbie(l) invalid", "scv invalid", }; static const char *mc_ra_types[] = { "Indeterminate", "Instruction fetch (bad)", "Instruction fetch (foreign/control memory)", "Page table walk ifetch (bad)", "Page table walk ifetch (foreign/control memory)", "Load (bad)", "Store (bad)", "Page table walk Load/Store (bad)", "Page table walk Load/Store (foreign/control memory)", "Load/Store (foreign/control memory)", }; static const char *mc_link_types[] = { "Indeterminate", "Instruction fetch (timeout)", "Page table walk ifetch (timeout)", "Load (timeout)", "Store (timeout)", "Page table walk Load/Store (timeout)", }; static const char *mc_error_class[] = { "Unknown", "Hardware error", "Probable Hardware error (some chance of software cause)", "Software error", "Probable Software error (some chance of hardware cause)", }; /* Print things out */ if (evt->version != MCE_V1) { pr_err("Machine Check Exception, Unknown event version %d !\n", evt->version); return; } switch (evt->severity) { case MCE_SEV_NO_ERROR: level = KERN_INFO; sevstr = "Harmless"; break; case MCE_SEV_WARNING: level = KERN_WARNING; sevstr = "Warning"; break; case MCE_SEV_SEVERE: level = KERN_ERR; sevstr = "Severe"; break; case MCE_SEV_FATAL: default: level = KERN_ERR; sevstr = "Fatal"; break; } switch(evt->initiator) { case MCE_INITIATOR_CPU: initiator = "CPU"; break; case MCE_INITIATOR_PCI: initiator = "PCI"; break; case MCE_INITIATOR_ISA: initiator = "ISA"; break; case MCE_INITIATOR_MEMORY: initiator = "Memory"; break; case MCE_INITIATOR_POWERMGM: initiator = "Power Management"; break; case MCE_INITIATOR_UNKNOWN: default: initiator = "Unknown"; break; } switch (evt->error_type) { case MCE_ERROR_TYPE_UE: err_type = "UE"; subtype = evt->u.ue_error.ue_error_type < ARRAY_SIZE(mc_ue_types) ? mc_ue_types[evt->u.ue_error.ue_error_type] : "Unknown"; if (evt->u.ue_error.effective_address_provided) ea = evt->u.ue_error.effective_address; if (evt->u.ue_error.physical_address_provided) pa = evt->u.ue_error.physical_address; break; case MCE_ERROR_TYPE_SLB: err_type = "SLB"; subtype = evt->u.slb_error.slb_error_type < ARRAY_SIZE(mc_slb_types) ? mc_slb_types[evt->u.slb_error.slb_error_type]Lines 1–500 of 771. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Machine check exception handling. * * Copyright 2013 IBM Corporation * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> */ #undef DEBUG #define pr_fmt(fmt) "mce: " fmt #include <linux/hardirq.h> #include <linux/types.h> #include <linux/ptrace.h> #include <linux/percpu.h> #include <linux/export.h> #include <linux/irq_work.h> #include <linux/extable.h> #include <linux/ftrace.h> #include <linux/memblock.h> #include <linux/of.h> #include <asm/interrupt.h> #include <asm/machdep.h> #include <asm/mce.h> #include <asm/nmi.h> #include "setup.h" static void machine_check_ue_event(struct machine_check_event *evt); static void machine_process_ue_event(struct work_struct *work); static DECLARE_WORK(mce_ue_event_work, machine_process_ue_event); static BLOCKING_NOTIFIER_HEAD(mce_notifier_list); int mce_register_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&mce_notifier_list, nb); } EXPORT_SYMBOL_GPL(mce_register_notifier); int mce_unregister_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&mce_notifier_list, nb); } EXPORT_SYMBOL_GPL(mce_unregister_notifier); static void mce_set_error_info(struct machine_check_event *mce, struct mce_error_info *mce_err) { mce->error_type = mce_err->error_type; switch (mce_err->error_type) { case MCE_ERROR_TYPE_UE: mce->u.ue_error.ue_error_type = mce_err->u.ue_error_type; break; case MCE_ERROR_TYPE_SLB: mce->u.slb_error.slb_error_type = mce_err->u.slb_error_type; break; case MCE_ERROR_TYPE_ERAT: mce->u.erat_error.erat_error_type = mce_err->u.erat_error_type; break; case MCE_ERROR_TYPE_TLB: mce->u.tlb_error.tlb_error_type = mce_err->u.tlb_error_type; break; case MCE_ERROR_TYPE_USER: mce->u.user_error.user_error_type = mce_err->u.user_error_type; break; case MCE_ERROR_TYPE_RA: mce->u.ra_error.ra_error_type = mce_err->u.ra_error_type; break; case MCE_ERROR_TYPE_LINK: mce->u.link_error.link_error_type = mce_err->u.link_error_type; break; case MCE_ERROR_TYPE_UNKNOWN: default: break; } } void mce_irq_work_queue(void) { /* Raise decrementer interrupt */ arch_irq_work_raise(); set_mce_pending_irq_work(); } /* * Decode and save high level MCE information into per cpu buffer which * is an array of machine_check_event structure. */ void save_mce_event(struct pt_regs *regs, long handled, struct mce_error_info *mce_err, uint64_t nip, uint64_t addr, uint64_t phys_addr) { int index = local_paca->mce_info->mce_nest_count++; struct machine_check_event *mce; mce = &local_paca->mce_info->mce_event[index]; /* * Return if we don't have enough space to log mce event. * mce_nest_count may go beyond MAX_MC_EVT but that's ok, * the check below will stop buffer overrun. */ if (index >= MAX_MC_EVT) return; /* Populate generic machine check info */ mce->version = MCE_V1; mce->srr0 = nip; mce->srr1 = regs->msr; mce->gpr3 = regs->gpr[3]; mce->in_use = 1; mce->cpu = get_paca()->paca_index; /* Mark it recovered if we have handled it and MSR(RI=1). */ if (handled && (regs->msr & MSR_RI)) mce->disposition = MCE_DISPOSITION_RECOVERED; else mce->disposition = MCE_DISPOSITION_NOT_RECOVERED; mce->initiator = mce_err->initiator; mce->severity = mce_err->severity; mce->sync_error = mce_err->sync_error; mce->error_class = mce_err->error_class; /* * Populate the mce error_type and type-specific error_type. */ mce_set_error_info(mce, mce_err); if (mce->error_type == MCE_ERROR_TYPE_UE) mce->u.ue_error.ignore_event = mce_err->ignore_event; /* * Raise irq work, So that we don't miss to log the error for * unrecoverable errors. */ if (mce->disposition == MCE_DISPOSITION_NOT_RECOVERED) mce_irq_work_queue(); if (!addr) return; if (mce->error_type == MCE_ERROR_TYPE_TLB) { mce->u.tlb_error.effective_address_provided = true; mce->u.tlb_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_SLB) { mce->u.slb_error.effective_address_provided = true; mce->u.slb_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_ERAT) { mce->u.erat_error.effective_address_provided = true; mce->u.erat_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_USER) { mce->u.user_error.effective_address_provided = true; mce->u.user_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_RA) { mce->u.ra_error.effective_address_provided = true; mce->u.ra_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_LINK) { mce->u.link_error.effective_address_provided = true; mce->u.link_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_UE) { mce->u.ue_error.effective_address_provided = true; mce->u.ue_error.effective_address = addr; if (phys_addr != ULONG_MAX) { mce->u.ue_error.physical_address_provided = true; mce->u.ue_error.physical_address = phys_addr; machine_check_ue_event(mce); } } return; } /* * get_mce_event: * mce Pointer to machine_check_event structure to be filled. * release Flag to indicate whether to free the event slot or not. * 0 <= do not release the mce event. Caller will invoke * release_mce_event() once event has been consumed. * 1 <= release the slot. * * return 1 = success * 0 = failure * * get_mce_event() will be called by platform specific machine check * handle routine and in KVM. * When we call get_mce_event(), we are still in interrupt context and * preemption will not be scheduled until ret_from_expect() routine * is called. */ int get_mce_event(struct machine_check_event *mce, bool release) { int index = local_paca->mce_info->mce_nest_count - 1; struct machine_check_event *mc_evt; int ret = 0; /* Sanity check */ if (index < 0) return ret; /* Check if we have MCE info to process. */ if (index < MAX_MC_EVT) { mc_evt = &local_paca->mce_info->mce_event[index]; /* Copy the event structure and release the original */ if (mce) *mce = *mc_evt; if (release) mc_evt->in_use = 0; ret = 1; } /* Decrement the count to free the slot. */ if (release) local_paca->mce_info->mce_nest_count--; return ret; } void release_mce_event(void) { get_mce_event(NULL, true); } static void machine_check_ue_work(void) { schedule_work(&mce_ue_event_work); } /* * Queue up the MCE event which then can be handled later. */ static void machine_check_ue_event(struct machine_check_event *evt) { int index; index = local_paca->mce_info->mce_ue_count++; /* If queue is full, just return for now. */ if (index >= MAX_MC_EVT) { local_paca->mce_info->mce_ue_count--; return; } memcpy(&local_paca->mce_info->mce_ue_event_queue[index], evt, sizeof(*evt)); } /* * Queue up the MCE event which then can be handled later. */ void machine_check_queue_event(void) { int index; struct machine_check_event evt; if (!get_mce_event(&evt, MCE_EVENT_RELEASE)) return; index = local_paca->mce_info->mce_queue_count++; /* If queue is full, just return for now. */ if (index >= MAX_MC_EVT) { local_paca->mce_info->mce_queue_count--; return; } memcpy(&local_paca->mce_info->mce_event_queue[index], &evt, sizeof(evt)); mce_irq_work_queue(); } void mce_common_process_ue(struct pt_regs *regs, struct mce_error_info *mce_err) { const struct exception_table_entry *entry; entry = search_kernel_exception_table(regs->nip); if (entry) { mce_err->ignore_event = true; regs_set_return_ip(regs, extable_fixup(entry)); } } /* * process pending MCE event from the mce event queue. This function will be * called during syscall exit. */ static void machine_process_ue_event(struct work_struct *work) { int index; struct machine_check_event *evt; while (local_paca->mce_info->mce_ue_count > 0) { index = local_paca->mce_info->mce_ue_count - 1; evt = &local_paca->mce_info->mce_ue_event_queue[index]; blocking_notifier_call_chain(&mce_notifier_list, 0, evt); #ifdef CONFIG_MEMORY_FAILURE /* * This should probably queued elsewhere, but * oh! well * * Don't report this machine check because the caller has a * asked us to ignore the event, it has a fixup handler which * will do the appropriate error handling and reporting. */ if (evt->error_type == MCE_ERROR_TYPE_UE) { if (evt->u.ue_error.ignore_event) { local_paca->mce_info->mce_ue_count--; continue; } if (evt->u.ue_error.physical_address_provided) { unsigned long pfn; pfn = evt->u.ue_error.physical_address >> PAGE_SHIFT; memory_failure(pfn, 0); } else pr_warn("Failed to identify bad address from " "where the uncorrectable error (UE) " "was generated\n"); } #endif local_paca->mce_info->mce_ue_count--; } } /* * process pending MCE event from the mce event queue. This function will be * called during syscall exit. */ static void machine_check_process_queued_event(void) { int index; struct machine_check_event *evt; add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); /* * For now just print it to console. * TODO: log this error event to FSP or nvram. */ while (local_paca->mce_info->mce_queue_count > 0) { index = local_paca->mce_info->mce_queue_count - 1; evt = &local_paca->mce_info->mce_event_queue[index]; if (evt->error_type == MCE_ERROR_TYPE_UE && evt->u.ue_error.ignore_event) { local_paca->mce_info->mce_queue_count--; continue; } machine_check_print_event_info(evt, false, false); local_paca->mce_info->mce_queue_count--; } } void set_mce_pending_irq_work(void) { local_paca->mce_pending_irq_work = 1; } void clear_mce_pending_irq_work(void) { local_paca->mce_pending_irq_work = 0; } void mce_run_irq_context_handlers(void) { if (unlikely(local_paca->mce_pending_irq_work)) { if (ppc_md.machine_check_log_err) ppc_md.machine_check_log_err(); machine_check_process_queued_event(); machine_check_ue_work(); clear_mce_pending_irq_work(); } } void machine_check_print_event_info(struct machine_check_event *evt, bool user_mode, bool in_guest) { const char *level, *sevstr, *subtype, *err_type, *initiator; uint64_t ea = 0, pa = 0; int n = 0; char dar_str[50]; char pa_str[50]; static const char *mc_ue_types[] = { "Indeterminate", "Instruction fetch", "Page table walk ifetch", "Load/Store", "Page table walk Load/Store", }; static const char *mc_slb_types[] = { "Indeterminate", "Parity", "Multihit", }; static const char *mc_erat_types[] = { "Indeterminate", "Parity", "Multihit", }; static const char *mc_tlb_types[] = { "Indeterminate", "Parity", "Multihit", }; static const char *mc_user_types[] = { "Indeterminate", "tlbie(l) invalid", "scv invalid", }; static const char *mc_ra_types[] = { "Indeterminate", "Instruction fetch (bad)", "Instruction fetch (foreign/control memory)", "Page table walk ifetch (bad)", "Page table walk ifetch (foreign/control memory)", "Load (bad)", "Store (bad)", "Page table walk Load/Store (bad)", "Page table walk Load/Store (foreign/control memory)", "Load/Store (foreign/control memory)", }; static const char *mc_link_types[] = { "Indeterminate", "Instruction fetch (timeout)", "Page table walk ifetch (timeout)", "Load (timeout)", "Store (timeout)", "Page table walk Load/Store (timeout)", }; static const char *mc_error_class[] = { "Unknown", "Hardware error", "Probable Hardware error (some chance of software cause)", "Software error", "Probable Software error (some chance of hardware cause)", }; /* Print things out */ if (evt->version != MCE_V1) { pr_err("Machine Check Exception, Unknown event version %d !\n", evt->version); return; } switch (evt->severity) { case MCE_SEV_NO_ERROR: level = KERN_INFO; sevstr = "Harmless"; break; case MCE_SEV_WARNING: level = KERN_WARNING; sevstr = "Warning"; break; case MCE_SEV_SEVERE: level = KERN_ERR; sevstr = "Severe"; break; case MCE_SEV_FATAL: default: level = KERN_ERR; sevstr = "Fatal"; break; } switch(evt->initiator) { case MCE_INITIATOR_CPU: initiator = "CPU"; break; case MCE_INITIATOR_PCI: initiator = "PCI"; break; case MCE_INITIATOR_ISA: initiator = "ISA"; break; case MCE_INITIATOR_MEMORY: initiator = "Memory"; break; case MCE_INITIATOR_POWERMGM: initiator = "Power Management"; break; case MCE_INITIATOR_UNKNOWN: default: initiator = "Unknown"; break; } switch (evt->error_type) { case MCE_ERROR_TYPE_UE: err_type = "UE"; subtype = evt->u.ue_error.ue_error_type < ARRAY_SIZE(mc_ue_types) ? mc_ue_types[evt->u.ue_error.ue_error_type] : "Unknown"; if (evt->u.ue_error.effective_address_provided) ea = evt->u.ue_error.effective_address; if (evt->u.ue_error.physical_address_provided) pa = evt->u.ue_error.physical_address; break; case MCE_ERROR_TYPE_SLB: err_type = "SLB"; subtype = evt->u.slb_error.slb_error_type < ARRAY_SIZE(mc_slb_types) ? mc_slb_types[evt->u.slb_error.slb_error_type]