git: 68ecb918a531 - main - powerpc/booke: Reorganize FRAME_LEAVE

From: Justin Hibbits <jhibbits_at_FreeBSD.org>
Date: Sat, 05 Sep 2026 04:15:16 UTC
The branch main has been updated by jhibbits:

URL: https://cgit.FreeBSD.org/src/commit/?id=68ecb918a5313150be660322c6686ae6292b4aac

commit 68ecb918a5313150be660322c6686ae6292b4aac
Author:     Justin Hibbits <jhibbits@FreeBSD.org>
AuthorDate: 2026-09-04 03:40:12 +0000
Commit:     Justin Hibbits <jhibbits@FreeBSD.org>
CommitDate: 2026-09-05 04:09:45 +0000

    powerpc/booke: Reorganize FRAME_LEAVE
    
    There's a small window between when the SRR* registers are restore and
    the exception returns, in which a TLB miss exception may be triggered.
    Since there are not special SRR* registers for TLB miss exceptions, the
    registers from the frame will be ovwritten, and the FRAME_LEAVE block
    will effectively be re-entered on exit, leading to a very hard to
    diagnose panic or wedge.
    
    Minimize this chance by pushing the SRR* restore to the last possible
    moments, caching them in a PCPU save area instead until the end.  This
    matches what the AIM side already does.
---
 sys/powerpc/booke/trap_subr.S | 42 +++++++++++++++++++++++++++++++-----------
 1 file changed, 31 insertions(+), 11 deletions(-)

diff --git a/sys/powerpc/booke/trap_subr.S b/sys/powerpc/booke/trap_subr.S
index 10babefbb2f8..55e4d03639d4 100644
--- a/sys/powerpc/booke/trap_subr.S
+++ b/sys/powerpc/booke/trap_subr.S
@@ -314,13 +314,23 @@
 
 /*
  *
+ * sprg_sp - SPRG{1-3} reg free to use as a scratch once r1 is restored
+ * savearea - temp save area (pc_{tempsave, critsave, mchksave, dbsave})
  * isrr0-1 - save restore registers to restore CPU state to (may be
  *           SRR0-1, CSRR0-1, MCSRR0-1
  *
  * Notes:
  *  - potential TLB miss: YES. The deref'd kstack may be not covered
+ *
+ *  - xSRR0-1 are staged in the savearea rather than written to the SPRs up
+ *    front.  A DTLB miss on the kstack below is not maskable by wrteei, and
+ *    the hardware overwrites SRR0-1 on entry to the miss handler, which then
+ *    restores its own values -- so anything parked in SRR0-1 across the
+ *    register reload is lost, and the rfi returns into the middle of this
+ *    macro instead of to the interrupted context.  Everything after the last
+ *    kstack access touches only pcpu and SPRs, which cannot fault.
  */
-#define	FRAME_LEAVE(isrr0, isrr1)					\
+#define	FRAME_LEAVE(sprg_sp, savearea, isrr0, isrr1)			\
 	wrteei 0;							\
 	/* restore CTR, XER, LR, CR */					\
 	LOAD	%r4, FRAME_CTR+CALLSIZE(%r1);				\
@@ -334,16 +344,26 @@
 	/* restore DBCR0 */						\
 	LOAD	%r4, FRAME_BOOKE_DBCR0+CALLSIZE(%r1);			\
 	mtspr	SPR_DBCR0, %r4;						\
-	/* restore xSRR0-1 */						\
+	/* stage xSRR0-1 where a TLB miss cannot reach them */		\
 	LOAD	%r30, FRAME_SRR0+CALLSIZE(%r1);				\
 	LOAD	%r31, FRAME_SRR1+CALLSIZE(%r1);				\
-	mtspr	isrr0, %r30;						\
-	mtspr	isrr1, %r31;						\
+	GET_CPUINFO(%r4);						\
+	STORE	%r30, (savearea+CPUSAVE_SRR0)(%r4);			\
+	STORE	%r31, (savearea+CPUSAVE_SRR1)(%r4);			\
 	/* restore R2-31, SP */						\
 	LD_REGS(%r1);							\
 	LOAD	%r2, FRAME_2+CALLSIZE(%r1);				\
 	LOAD	%r0, FRAME_0+CALLSIZE(%r1);				\
 	LOAD	%r1, FRAME_1+CALLSIZE(%r1);				\
+	/* no kstack references past here; park r3 to free a scratch */	\
+	mtspr	sprg_sp, %r3;						\
+	GET_CPUINFO(%r3);						\
+	LOAD	%r3, (savearea+CPUSAVE_SRR0)(%r3);			\
+	mtspr	isrr0, %r3;						\
+	GET_CPUINFO(%r3);						\
+	LOAD	%r3, (savearea+CPUSAVE_SRR1)(%r3);			\
+	mtspr	isrr1, %r3;						\
+	mfspr	%r3, sprg_sp;						\
 	isync
 
 /*
@@ -541,7 +561,7 @@ INTERRUPT(int_critical_input)
 	bl	CNAME(powerpc_interrupt)
 	TOC_RESTORE
 	CRIT_SRR_RESTORE
-	FRAME_LEAVE(SPR_CSRR0, SPR_CSRR1)
+	FRAME_LEAVE(SPR_SPRG2, PC_BOOKE_CRITSAVE, SPR_CSRR0, SPR_CSRR1)
 	rfci
 
 
@@ -555,7 +575,7 @@ INTERRUPT(int_machine_check)
 	addi	%r3, %r1, CALLSIZE
 	bl	CNAME(powerpc_interrupt)
 	TOC_RESTORE
-	FRAME_LEAVE(SPR_MCSRR0, SPR_MCSRR1)
+	FRAME_LEAVE(SPR_SPRG3, PC_BOOKE_MCHKSAVE, SPR_MCSRR0, SPR_MCSRR1)
 	rfmci
 
 
@@ -642,7 +662,7 @@ INTERRUPT(int_watchdog)
 	bl	CNAME(powerpc_interrupt)
 	TOC_RESTORE
 	CRIT_SRR_RESTORE
-	FRAME_LEAVE(SPR_CSRR0, SPR_CSRR1)
+	FRAME_LEAVE(SPR_SPRG2, PC_BOOKE_CRITSAVE, SPR_CSRR0, SPR_CSRR1)
 	rfci
 
 
@@ -964,14 +984,14 @@ INTERRUPT(int_debug)
 	STANDARD_CRIT_PROLOG(SPR_SPRG2, PC_BOOKE_CRITSAVE, SPR_CSRR0, SPR_CSRR1)
 	FRAME_SETUP(SPR_SPRG2, PC_BOOKE_CRITSAVE, EXC_DEBUG)
 	bl	int_debug_int
-	FRAME_LEAVE(SPR_CSRR0, SPR_CSRR1)
+	FRAME_LEAVE(SPR_SPRG2, PC_BOOKE_CRITSAVE, SPR_CSRR0, SPR_CSRR1)
 	rfci
 
 INTERRUPT(int_debug_ed)
 	STANDARD_CRIT_PROLOG(SPR_SPRG2, PC_BOOKE_CRITSAVE, SPR_DSRR0, SPR_DSRR1)
 	FRAME_SETUP(SPR_SPRG2, PC_BOOKE_CRITSAVE, EXC_DEBUG)
 	bl	int_debug_int
-	FRAME_LEAVE(SPR_DSRR0, SPR_DSRR1)
+	FRAME_LEAVE(SPR_SPRG2, PC_BOOKE_CRITSAVE, SPR_DSRR0, SPR_DSRR1)
 	rfdi
 	/* .long 0x4c00004e */
 
@@ -1043,7 +1063,7 @@ CNAME(trapexit):
 CNAME(asttrapexit):
 	b	trapexit		/* test ast ret value ? */
 1:
-	FRAME_LEAVE(SPR_SRR0, SPR_SRR1)
+	FRAME_LEAVE(SPR_SPRG1, PC_TEMPSAVE, SPR_SRR0, SPR_SRR1)
 	rfi
 
 
@@ -1097,7 +1117,7 @@ dbtrap:
 	b	trap_common
 
 dbleave:
-	FRAME_LEAVE(SPR_SRR0, SPR_SRR1)
+	FRAME_LEAVE(SPR_SPRG1, PC_DBSAVE, SPR_SRR0, SPR_SRR1)
 	rfi
 ASEND(breakpoint)
 #endif /* KDB */