diff mbox

[RFC,2/7] powerpc/book3s: mce: Call opal mce handler to extract MCE error reason.

Message ID 148764192219.19289.16692912616165807064.stgit@jupiter.in.ibm.com (mailing list archive)
State RFC
Headers show

Commit Message

Mahesh J Salgaonkar Feb. 21, 2017, 1:52 a.m. UTC
From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

This is a RFC implementation that proposes an approach where linux would
still take an MCE interrupt but it would make opal call to handle all
chip specific processing to extract the error reason.

Opal will support OPAL_HANDLE_MACHINE_CHECK token if it implements
machine check handler currently installed FW. If this token is supported
then linux will populate ppc_md.machine_check_early() function pointer.

But if opal mce handler does not support processing/extracting MCE reason
for current chip, then it will return OPAL_UNSUPPORTED. e.g. new OPAL FW
installed on system with Power8 or below. In this case (i.e on Power8 system)
it will fallback to in-kernel MCE handler as it does today.

This patch uses OPAL_CALL_REAL() wrapper to be able to go back into OPAL on
machine check interrupt. The reason is, OPAL_CALL() wrapper makes it
difficult since it uses r13 to save PACASAVEDMSR which overwrites previous
MSR if we already in opal when MCE hit us.

We also need to make OPAL to stick to caller's stack to make opal call
re-entrant. Will send out a separate patch to skiboot mailing list.

The other approach could be let opal patch up 0x200 vector so that opal
takes up the interrupt and does the processing. comments welcome.

Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/machdep.h             |    2 ++
 arch/powerpc/include/asm/opal-api.h            |    3 ++-
 arch/powerpc/include/asm/opal.h                |    3 +++
 arch/powerpc/kernel/traps.c                    |   15 ++++++++++++++-
 arch/powerpc/platforms/powernv/opal-wrappers.S |    1 +
 arch/powerpc/platforms/powernv/opal.c          |   16 ++++++++++++++++
 arch/powerpc/platforms/powernv/setup.c         |    4 ++++
 7 files changed, 42 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 5011b69..037ea19 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -108,6 +108,8 @@  struct machdep_calls {
 
 	/* Early exception handlers called in realmode */
 	int		(*hmi_exception_early)(struct pt_regs *regs);
+	int		(*machine_check_early)(struct pt_regs *regs,
+							long *handled);
 
 	/* Called during machine check exception to retrive fixup address. */
 	bool		(*mce_check_early_recovery)(struct pt_regs *regs);
diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
index 6851be6..8485e67 100644
--- a/arch/powerpc/include/asm/opal-api.h
+++ b/arch/powerpc/include/asm/opal-api.h
@@ -167,7 +167,8 @@ 
 #define OPAL_INT_EOI				124
 #define OPAL_INT_SET_MFRR			125
 #define OPAL_PCI_TCE_KILL			126
-#define OPAL_LAST				126
+#define OPAL_HANDLE_MACHINE_CHECK		145
+#define OPAL_LAST				145
 
 /* Device tree flags */
 
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 5c7db0f..4834e0d 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -189,6 +189,8 @@  int64_t opal_set_param(uint64_t token, uint32_t param_id, uint64_t buffer,
 		uint64_t length);
 int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data);
 int64_t opal_handle_hmi(void);
+int64_t opal_rm_handle_machine_check(uint64_t srr0, uint64_t srr1, uint64_t dar,
+					uint64_t dsisr, uint64_t *mce);
 int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
 int64_t opal_unregister_dump_region(uint32_t id);
 int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
@@ -279,6 +281,7 @@  extern int opal_hmi_handler_init(void);
 extern int opal_event_init(void);
 
 extern int opal_machine_check(struct pt_regs *regs);
+extern int opal_machine_check_early(struct pt_regs *regs, long *handled);
 extern bool opal_mce_check_early_recovery(struct pt_regs *regs);
 extern int opal_hmi_exception_early(struct pt_regs *regs);
 extern int opal_handle_hmi_exception(struct pt_regs *regs);
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index e6cc56b..62b587f 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -302,12 +302,25 @@  void system_reset_exception(struct pt_regs *regs)
 long machine_check_early(struct pt_regs *regs)
 {
 	long handled = 0;
+	int rc = 0;
 
 	__this_cpu_inc(irq_stat.mce_exceptions);
 
 	add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
 
-	if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
+	/*
+	 * See if platform is capable of handling machine check. (e.g. PowerNV
+	 * platform may take help from OPAL firmware to handle machine check.)
+	 * Otherwise fallthrough and allow CPU to handle this machine check.
+	 *
+	 * If rc == -1 then it means firmware does not provide support for
+	 * machine check handling for this CPU chip. Fallback to in-kernel
+	 * machine check handler.
+	 */
+	if (ppc_md.machine_check_early)
+		rc = ppc_md.machine_check_early(regs, &handled);
+
+	if (!rc && cur_cpu_spec && cur_cpu_spec->machine_check_early)
 		handled = cur_cpu_spec->machine_check_early(regs);
 	return handled;
 }
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 3aa40f1..3120d2e 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -312,3 +312,4 @@  OPAL_CALL(opal_int_set_mfrr,			OPAL_INT_SET_MFRR);
 OPAL_CALL_REAL(opal_rm_int_set_mfrr,		OPAL_INT_SET_MFRR);
 OPAL_CALL(opal_pci_tce_kill,			OPAL_PCI_TCE_KILL);
 OPAL_CALL_REAL(opal_rm_pci_tce_kill,		OPAL_PCI_TCE_KILL);
+OPAL_CALL_REAL(opal_rm_handle_machine_check,		OPAL_HANDLE_MACHINE_CHECK);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 3eb0e94..263c57e 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -488,6 +488,22 @@  int opal_machine_check(struct pt_regs *regs)
 	return 0;
 }
 
+/* Early mce handler called in real mode. */
+int opal_machine_check_early(struct pt_regs *regs, long *handled)
+{
+	int rc;
+	struct OpalMachineCheckEvent evt = { 0 };
+
+	*handled = 0;
+
+	rc = opal_rm_handle_machine_check(regs->nip, regs->msr, regs->dar,
+					regs->dsisr, (uint64_t *)&evt);
+	if (rc != OPAL_SUCCESS)
+		return -1;
+
+	return 0;
+}
+
 /* Early hmi handler called in real mode. */
 int opal_hmi_exception_early(struct pt_regs *regs)
 {
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index d50c7d9..781478a 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -264,6 +264,10 @@  static void __init pnv_setup_machdep_opal(void)
 	ppc_md.mce_check_early_recovery = opal_mce_check_early_recovery;
 	ppc_md.hmi_exception_early = opal_hmi_exception_early;
 	ppc_md.handle_hmi_exception = opal_handle_hmi_exception;
+
+	/* Check if OPAL is capable of handling machine check. */
+	if (opal_check_token(OPAL_HANDLE_MACHINE_CHECK))
+		ppc_md.machine_check_early = opal_machine_check_early;
 }
 
 static int __init pnv_probe(void)