[Skiboot] [PATCH 16/16] core/exceptions: implement support for MCE interrupts in powersave

Nicholas Piggin npiggin at gmail.com
Tue Jan 8 01:04:28 AEDT 2019


The ISA specifies that MCE interrupts in power saving modes will enter
at 0x200 with powersave bits in SRR1 set. This is not currently
supported properly, the MCE will just happen like a normal interrupt,
but GPRs could be lost, which would lead to crashes (e.g., r1, r2, r13
etc).

So check the power save bits similarly to the sreset vector, and
handle this properly.

Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
---
 asm/head.S          | 47 +++++++++++++++++++++++++++++++++++----------
 core/cpu.c          | 11 +++++++----
 core/exceptions.c   | 17 ++++++++++++++++
 include/processor.h |  1 +
 include/skiboot.h   |  5 +++--
 5 files changed, 65 insertions(+), 16 deletions(-)

diff --git a/asm/head.S b/asm/head.S
index 4a00532df..696d168d4 100644
--- a/asm/head.S
+++ b/asm/head.S
@@ -119,6 +119,31 @@ hdat_entry:
 	li	%r27,0
 	b	boot_entry
 
+	.= 0x200
+	mtsprg0	%r3
+	mtsprg1 %r4
+	mfspr	%r3,SPR_SRR1
+	mfcr	%r4
+	rldicl.	%r3,%r3,48,62
+	bne	1f		/* powersave wakeup (CFAR not required) */
+	mtcr	%r4
+	mfspr	%r3,SPR_CFAR
+	li	%r4,0x200
+	b	_exception
+1:
+	LOAD_IMM64(%r30, SKIBOOT_BASE)
+	cmpdi	%r3,0x1
+	bne	2f		/* state loss */
+	LOAD_IMM32(%r3, reset_resume - __head)
+	b	3f
+2:
+	LOAD_IMM32(%r3, reset_wakeup - __head)
+3:
+	add	%r3,%r30,%r3
+	mtctr	%r3
+	li	%r3,0x200
+	bctr
+
 #define EXCEPTION(nr)		\
 	.= nr			;\
 	mtsprg0	%r3		;\
@@ -128,7 +153,6 @@ hdat_entry:
 	b	_exception
 
 	/* More exception stubs */
-	EXCEPTION(0x200)
 	EXCEPTION(0x300)
 	EXCEPTION(0x380)
 	EXCEPTION(0x400)
@@ -667,7 +691,8 @@ enter_p9_pm_state:
 	b	.
 
 /* This is a little piece of code that is copied down to
- * 0x100 for handling power management wakeups
+ * 0x100 for handling sresets and power management wakeups.
+ * This matches the 0x200 handler closely.
  */
 .global reset_patch_start
 reset_patch_start:
@@ -692,10 +717,12 @@ reset_patch_start:
 3:
 	add	%r3,%r30,%r3
 	mtctr	%r3
+	li	%r3,0x100
 	bctr
 .global reset_patch_end
 reset_patch_end:
 
+/* Wakeup vector in r3 */
 reset_wakeup:
 	/* Get PIR */
 	mfspr	%r31,SPR_PIR
@@ -708,14 +735,14 @@ reset_wakeup:
 	ld	%r1,CPUTHREAD_SAVE_R1(%r13)
 
 	/* Restore more stuff */
-	lwz	%r3,STACK_CR(%r1)
-	lwz	%r4,STACK_XER(%r1)
-	ld	%r5,STACK_GPR0(%r1)
-	ld	%r6,STACK_GPR1(%r1)
-	mtcr	%r3
-	mtxer	%r4
-	mtspr	SPR_HSPRG0,%r5
-	mtspr	SPR_HSPRG1,%r6
+	lwz	%r4,STACK_CR(%r1)
+	lwz	%r5,STACK_XER(%r1)
+	ld	%r6,STACK_GPR0(%r1)
+	ld	%r7,STACK_GPR1(%r1)
+	mtcr	%r4
+	mtxer	%r5
+	mtspr	SPR_HSPRG0,%r6
+	mtspr	SPR_HSPRG1,%r7
 	REST_GPR(2,%r1)
 	REST_GPR(14,%r1)
 	REST_GPR(15,%r1)
diff --git a/core/cpu.c b/core/cpu.c
index c1a0a0258..8e0513d4d 100644
--- a/core/cpu.c
+++ b/core/cpu.c
@@ -426,8 +426,7 @@ static unsigned int cpu_idle_p8(enum cpu_wake_cause wake_on)
 	isync();
 
 	/* Enter nap */
-	enter_p8_pm_state(false);
-	vec = 0x100;
+	vec = enter_p8_pm_state(false);
 
 skip_sleep:
 	/* Restore */
@@ -486,8 +485,7 @@ static unsigned int cpu_idle_p9(enum cpu_wake_cause wake_on)
 		/* PSSCR SD=0 ESL=1 EC=1 PSSL=0 TR=3 MTL=0 RL=1 */
 		psscr = PPC_BIT(42) | PPC_BIT(43) |
 			PPC_BITMASK(54, 55) | PPC_BIT(63);
-		enter_p9_pm_state(psscr);
-		vec = 0x100;
+		vec = enter_p9_pm_state(psscr);
 	} else {
 		/* stop with EC=0 (resumes) which does not require sreset. */
 		/* PSSCR SD=0 ESL=0 EC=0 PSSL=0 TR=3 MTL=0 RL=1 */
@@ -535,6 +533,11 @@ static void cpu_idle_pm(enum cpu_wake_cause wake_on)
 			break;
 		}
 		mtmsrd(MSR_RI, 1);
+
+	} else if (vec == 0x200) {
+		exception_entry_pm_mce();
+		enable_machine_check();
+		mtmsrd(MSR_RI, 1);
 	}
 }
 
diff --git a/core/exceptions.c b/core/exceptions.c
index 779cd620a..e15848ad3 100644
--- a/core/exceptions.c
+++ b/core/exceptions.c
@@ -127,6 +127,23 @@ void exception_entry_pm_sreset(void)
 	backtrace();
 }
 
+void __noreturn exception_entry_pm_mce(void)
+{
+	const size_t max = 320;
+	char buf[max];
+	size_t l;
+
+	prerror("***********************************************\n");
+	l = 0;
+	l += snprintf(buf + l, max - l,
+		"Fatal MCE in sleep");
+	prerror("%s\n", buf);
+	prerror("SRR0 : "REG" SRR1 : "REG"\n",
+			(uint64_t)mfspr(SPR_SRR0), (uint64_t)mfspr(SPR_SRR1));
+	prerror("DSISR: "REG32"         DAR  : "REG"\n",
+			(uint32_t)mfspr(SPR_DSISR), (uint64_t)mfspr(SPR_DAR));
+	abort();
+}
 
 static int64_t opal_register_exc_handler(uint64_t opal_exception __unused,
 					 uint64_t handler_address __unused,
diff --git a/include/processor.h b/include/processor.h
index 70991dfb0..2b398b46b 100644
--- a/include/processor.h
+++ b/include/processor.h
@@ -92,6 +92,7 @@
 
 #define SPR_SRR1_PM_WAKE_MASK	0x3c0000	/* PM wake reason for P8/9 */
 #define SPR_SRR1_PM_WAKE_SRESET	0x100000
+#define SPR_SRR1_PM_WAKE_MCE	0x3c0000	/* Use reserved value for MCE */
 
 /* Bits in LPCR */
 
diff --git a/include/skiboot.h b/include/skiboot.h
index c06146d06..6da62233a 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -289,12 +289,13 @@ extern void fake_rtc_init(void);
 struct stack_frame;
 extern void exception_entry(struct stack_frame *stack);
 extern void exception_entry_pm_sreset(void);
+extern void exception_entry_pm_mce(void);
 
 /* Assembly in head.S */
 extern void disable_machine_check(void);
 extern void enable_machine_check(void);
-extern void enter_p8_pm_state(bool winkle);
-extern void enter_p9_pm_state(uint64_t psscr);
+extern unsigned int enter_p8_pm_state(bool winkle);
+extern unsigned int enter_p9_pm_state(uint64_t psscr);
 extern void enter_p9_pm_lite_state(uint64_t psscr);
 extern uint32_t reset_patch_start;
 extern uint32_t reset_patch_end;
-- 
2.18.0



More information about the Skiboot mailing list