[PATCH] powerpc/mce: Avoid using addr_to_pfn in realmode
Ganesh Goudar
ganeshgr at linux.ibm.com
Sat Jun 20 22:44:35 AEST 2020
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 <ganeshgr at 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
More information about the Linuxppc-dev
mailing list