From: Reza Arbab <ar...@linux.ibm.com> If the instruction causing a UE has an exception table entry with fixup address, save it in the machine_check_event struct.
If a machine check notifier callback returns NOTIFY_STOP to indicate it has handled the error, set nip to continue execution from the fixup address. Signed-off-by: Reza Arbab <ar...@linux.ibm.com> --- arch/powerpc/include/asm/mce.h | 5 +++-- arch/powerpc/kernel/mce.c | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h index 240dd1fdfe35..9d9661747adf 100644 --- a/arch/powerpc/include/asm/mce.h +++ b/arch/powerpc/include/asm/mce.h @@ -122,11 +122,12 @@ struct machine_check_event { enum MCE_UeErrorType ue_error_type:8; u8 effective_address_provided; u8 physical_address_provided; + u8 fixup_address_provided; u8 process_event; - u8 reserved_1[4]; + u8 reserved_1[3]; u64 effective_address; u64 physical_address; - u8 reserved_2[8]; + u64 fixup_address; } ue_error; struct { diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c index 2616f1f71734..8afda1ab7358 100644 --- a/arch/powerpc/kernel/mce.c +++ b/arch/powerpc/kernel/mce.c @@ -15,10 +15,12 @@ #include <linux/percpu.h> #include <linux/export.h> #include <linux/irq_work.h> +#include <linux/extable.h> #include <asm/machdep.h> #include <asm/mce.h> #include <asm/nmi.h> +#include <asm/extable.h> static DEFINE_PER_CPU(int, mce_nest_count); static DEFINE_PER_CPU(struct machine_check_event[MAX_MC_EVT], mce_event); @@ -151,6 +153,8 @@ void save_mce_event(struct pt_regs *regs, long handled, mce->u.link_error.effective_address_provided = true; mce->u.link_error.effective_address = addr; } else if (mce->error_type == MCE_ERROR_TYPE_UE) { + const struct exception_table_entry *entry; + mce->u.ue_error.effective_address_provided = true; mce->u.ue_error.effective_address = addr; if (phys_addr != ULONG_MAX) { @@ -158,6 +162,12 @@ void save_mce_event(struct pt_regs *regs, long handled, mce->u.ue_error.physical_address = phys_addr; } + entry = search_exception_tables(regs->nip); + if (entry) { + mce->u.ue_error.fixup_address_provided = true; + mce->u.ue_error.fixup_address = extable_fixup(entry); + } + mce->u.ue_error.process_event = true; } return; @@ -666,8 +676,12 @@ long machine_check_notify(struct pt_regs *regs) rc = blocking_notifier_call_chain(&mce_notifier_list, 0, evt); if (rc & NOTIFY_STOP_MASK) { - if (evt->error_type == MCE_ERROR_TYPE_UE) + if (evt->error_type == MCE_ERROR_TYPE_UE) { + if (evt->u.ue_error.fixup_address_provided) + regs->nip = evt->u.ue_error.fixup_address; + evt->u.ue_error.process_event = false; + } evt->disposition = MCE_DISPOSITION_RECOVERED; regs->msr |= MSR_RI; -- 2.20.1