When an UE or memory error exception is encountered the MCE handler tries to find the pfn using addr_to_pfn() which takes effective address as an argument, later pfn is used to poison the page where memory error occurred, recent rework in this area made addr_to_pfn to run in realmode, which can be fatal as it may try to access memory outside RMO region.
To fix this move the use of addr_to_pfn to save_mce_event(), which runs in virtual mode. Signed-off-by: Ganesh Goudar <ganes...@linux.ibm.com> --- arch/powerpc/kernel/mce.c | 7 +++++ arch/powerpc/kernel/mce_power.c | 39 +++++++--------------------- arch/powerpc/platforms/pseries/ras.c | 12 ++------- 3 files changed, 18 insertions(+), 40 deletions(-) diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c index fd90c0eda229..c5581a742367 100644 --- a/arch/powerpc/kernel/mce.c +++ b/arch/powerpc/kernel/mce.c @@ -148,6 +148,13 @@ void save_mce_event(struct pt_regs *regs, long handled, } 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->sync_error && addr) { + unsigned long pfn; + + pfn = addr_to_pfn(regs, addr); + if (pfn != ULONG_MAX) + phys_addr = pfn << PAGE_SHIFT; + } if (phys_addr != ULONG_MAX) { mce->u.ue_error.physical_address_provided = true; mce->u.ue_error.physical_address = phys_addr; diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index c3b522bff9b4..1b2582818f2b 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -361,8 +361,7 @@ static const struct mce_derror_table mce_p9_derror_table[] = { MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, { 0, false, 0, 0, 0, 0, 0 } }; -static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr, - uint64_t *phys_addr) +static int mce_find_instr_ea(struct pt_regs *regs, uint64_t *addr) { /* * Carefully look at the NIP to determine @@ -380,9 +379,7 @@ static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr, instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK); instr = ppc_inst_read((struct ppc_inst *)instr_addr); if (!analyse_instr(&op, &tmp, instr)) { - pfn = addr_to_pfn(regs, op.ea); *addr = op.ea; - *phys_addr = (pfn << PAGE_SHIFT); return 0; } /* @@ -398,8 +395,7 @@ static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr, static int mce_handle_ierror(struct pt_regs *regs, const struct mce_ierror_table table[], - struct mce_error_info *mce_err, uint64_t *addr, - uint64_t *phys_addr) + struct mce_error_info *mce_err, uint64_t *addr) { uint64_t srr1 = regs->msr; int handled = 0; @@ -455,21 +451,8 @@ static int mce_handle_ierror(struct pt_regs *regs, mce_err->sync_error = table[i].sync_error; mce_err->severity = table[i].severity; mce_err->initiator = table[i].initiator; - if (table[i].nip_valid) { + if (table[i].nip_valid) *addr = regs->nip; - if (mce_err->sync_error && - table[i].error_type == MCE_ERROR_TYPE_UE) { - unsigned long pfn; - - if (get_paca()->in_mce < MAX_MCE_DEPTH) { - pfn = addr_to_pfn(regs, regs->nip); - if (pfn != ULONG_MAX) { - *phys_addr = - (pfn << PAGE_SHIFT); - } - } - } - } return handled; } @@ -484,8 +467,7 @@ static int mce_handle_ierror(struct pt_regs *regs, static int mce_handle_derror(struct pt_regs *regs, const struct mce_derror_table table[], - struct mce_error_info *mce_err, uint64_t *addr, - uint64_t *phys_addr) + struct mce_error_info *mce_err, uint64_t *addr) { uint64_t dsisr = regs->dsisr; int handled = 0; @@ -562,8 +544,7 @@ static int mce_handle_derror(struct pt_regs *regs, * kernel/exception-64s.h */ if (get_paca()->in_mce < MAX_MCE_DEPTH) - mce_find_instr_ea_and_phys(regs, addr, - phys_addr); + mce_find_instr_ea(regs, addr); } found = 1; } @@ -608,21 +589,19 @@ static long mce_handle_error(struct pt_regs *regs, const struct mce_ierror_table itable[]) { struct mce_error_info mce_err = { 0 }; - uint64_t addr, phys_addr = ULONG_MAX; + uint64_t addr; uint64_t srr1 = regs->msr; long handled; if (SRR1_MC_LOADSTORE(srr1)) - handled = mce_handle_derror(regs, dtable, &mce_err, &addr, - &phys_addr); + handled = mce_handle_derror(regs, dtable, &mce_err, &addr); else - handled = mce_handle_ierror(regs, itable, &mce_err, &addr, - &phys_addr); + handled = mce_handle_ierror(regs, itable, &mce_err, &addr); if (!handled && mce_err.error_type == MCE_ERROR_TYPE_UE) handled = mce_handle_ue_error(regs, &mce_err); - save_mce_event(regs, handled, &mce_err, regs->nip, addr, phys_addr); + save_mce_event(regs, handled, &mce_err, regs->nip, addr, ULONG_MAX); return handled; } diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index f3736fcd98fc..6d64a9fb6130 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -526,7 +526,7 @@ int pSeries_system_reset_exception(struct pt_regs *regs) static int mce_handle_error(struct pt_regs *regs, struct rtas_error_log *errp) { struct mce_error_info mce_err = { 0 }; - unsigned long eaddr = 0, paddr = 0; + unsigned long eaddr = 0, paddr = ULONG_MAX; struct pseries_errorlog *pseries_log; struct pseries_mc_errorlog *mce_log; int disposition = rtas_error_disposition(errp); @@ -610,16 +610,8 @@ static int mce_handle_error(struct pt_regs *regs, struct rtas_error_log *errp) if (mce_log->sub_err_type & UE_EFFECTIVE_ADDR_PROVIDED) eaddr = be64_to_cpu(mce_log->effective_address); - if (mce_log->sub_err_type & UE_LOGICAL_ADDR_PROVIDED) { + if (mce_log->sub_err_type & UE_LOGICAL_ADDR_PROVIDED) paddr = be64_to_cpu(mce_log->logical_address); - } else if (mce_log->sub_err_type & UE_EFFECTIVE_ADDR_PROVIDED) { - unsigned long pfn; - - pfn = addr_to_pfn(regs, eaddr); - if (pfn != ULONG_MAX) - paddr = pfn << PAGE_SHIFT; - } - break; case MC_ERROR_TYPE_SLB: mce_err.error_type = MCE_ERROR_TYPE_SLB; -- 2.17.2