[PATCH] powerpc/mce: Avoid using addr_to_pfn in realmode

Subsystems: linux for powerpc (32-bit and 64-bit), the rest

STALE2235d

2 messages, 2 authors, 2020-06-22 · open the first message on its own page

[PATCH] powerpc/mce: Avoid using addr_to_pfn in realmode

From: Ganesh Goudar <hidden>
Date: 2020-06-20 12:48:51

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 <redacted>
---
 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

Re: [PATCH] powerpc/mce: Avoid using addr_to_pfn in realmode

From: Nicholas Piggin <npiggin@gmail.com>
Date: 2020-06-22 02:18:04

Excerpts from Ganesh Goudar's message of June 20, 2020 10:44 pm:
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.
I'd rather this be done in the pseries mce handler rather than
change bare metal, and leaving save_mce_event as purely recording the 
event, decoding remains in its caller.

Radix guests also don't have an RMO, we could probably restrict that
pseries hack to hash. I want to get rid of it but it will take longer
to fix things properly.

Thanks,
Nick
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help