[PATCH] powerpc: Fix OPAL support in 32-bit boot wrapper

Ben Hutchings ben.hutchings at mind.be
Wed Sep 10 22:16:33 AEST 2025


The OPAL support code in the boot wrapper can be built for a big-
endian kernel, in which case it runs in 32-bit mode while OPAL must
always run in 64-bit mode.  In this case it is currently completely
broken:

- It is missing the necessary mode switch when calling and returning
  from OPAL.

- The definition of LOAD_REG_ADDR introduces R_PPC_ADDR16_{HA,LO}
  relocations, but _zimage_start only handles R_PPC_RELATIVE
  relocations.

- opal_call assumes the ELF v2 ABI's stack frame format.

- opal_call assumes that its arguments and return value will be in the
  same registers as for the OPAL service, but this is not true when
  the caller is 32-bit code.

Since so many changes are needed in opal_call and its wrappers, write
completely separate definitions for the 32-bit case.

Delete the definition of LOAD_REG_ADDR for 32-bit code, as no generic
definition is possible.  We could use the same definition as
LOAD_REG_ADDR_PIC in the main kernel, but that clobbers lr which would
break opal_call.

Signed-off-by: Ben Hutchings <ben.hutchings at mind.be>
---
 arch/powerpc/boot/opal-calls.S | 110 +++++++++++++++++++++++++++++++++
 arch/powerpc/boot/ppc_asm.h    |   6 +-
 2 files changed, 112 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/boot/opal-calls.S b/arch/powerpc/boot/opal-calls.S
index 1f2f330a459e..034811619c0d 100644
--- a/arch/powerpc/boot/opal-calls.S
+++ b/arch/powerpc/boot/opal-calls.S
@@ -16,11 +16,22 @@ opal_kentry:
 	li	r5, 0
 	li	r6, 0
 	li	r7, 0
+#ifdef CONFIG_PPC64_BOOT_WRAPPER
 	LOAD_REG_ADDR(r11, opal)
+#else
+	/* R_PPC_ADDR16_{HA,LO} are not supported, so copy pc into r11 */
+	bcl	20,31,0f
+0:	mflr	r11
+
+	addis	r11,r11,(opal - 0b)@ha
+	addi	r11,r11,(opal - 0b)@l;
+#endif
 	ld	r8,0(r11)
 	ld	r9,8(r11)
 	bctr
 
+#ifdef CONFIG_PPC64_BOOT_WRAPPER
+
 #define OPAL_CALL(name, token)				\
 	.globl name;					\
 name:							\
@@ -65,3 +76,102 @@ OPAL_CALL(opal_console_read,			OPAL_CONSOLE_READ);
 OPAL_CALL(opal_console_write_buffer_space,	OPAL_CONSOLE_WRITE_BUFFER_SPACE);
 OPAL_CALL(opal_poll_events,			OPAL_POLL_EVENTS);
 OPAL_CALL(opal_console_flush,			OPAL_CONSOLE_FLUSH);
+
+#else /* !CONFIG_PPC64_BOOT_WRAPPER */
+
+opal_call:
+	mflr	r11
+	stw	r11,4(r1)
+	mfcr	r12
+	stw	r12,16(r1)
+	mr	r13,r2
+
+	/* R_PPC_ADDR16_{HA,LO} are not supported, so copy pc into r2 */
+	bcl	20,31,0f
+0:	mflr	r2
+
+	/* Set opal return address */
+	addis	r11,r2,(opal_return - 0b)@ha
+	addi	r11,r11,(opal_return - 0b)@l;
+	mtlr	r11
+
+	/* switch to 64-bit when we enter OPAL */
+	mfmsr	r12
+	li	r11,1
+	rldicl	r11,r11,MSR_SF_LG,0
+	or	r12,r12,r11
+	mtspr	SPRN_HSRR1,r12
+
+	/* load the opal call entry point and base */
+	addis	r11,r2,(opal - 0b)@ha
+	addi	r11,r11,(opal - 0b)@l;
+	ld	r12,8(r11)
+	ld	r2,0(r11)
+	mtspr	SPRN_HSRR0,r12
+
+	hrfid
+
+opal_return:
+	/* switch back to 32-bit */
+	mfmsr	r12
+	li	r11,1
+	rldicl	r11,r11,MSR_SF_LG,0
+	andc	r12,r12,r11
+	mtmsrd	r12
+	isync
+
+	/* split 64-bit return value into 2 registers */
+	rldicl	r4,r3,0,32
+	rldicl	r3,r3,32,32
+
+	mr	r2,r13
+	lwz	r11,16(r1)
+	lwz	r12,4(r1)
+	mtcr	r11;
+	mtlr	r12
+	blr
+
+	/*
+	 * Wrapper functions need to combine and shift arguments into the
+	 * 64-bit calling convention.
+	 */
+
+	.globl opal_console_write
+opal_console_write:
+	li	r0, OPAL_CONSOLE_WRITE
+	sldi	r3,r3,32
+	or	r3,r3,r4
+	mr	r4,r5
+	mr	r5,r6
+	b	opal_call
+
+	.globl opal_console_read
+opal_console_read:
+	li	r0, OPAL_CONSOLE_READ
+	sldi	r3,r3,32
+	or	r3,r3,r4
+	mr	r4,r5
+	mr	r5,r6
+	b	opal_call
+
+	.globl opal_console_write_buffer_space
+opal_console_write_buffer_space:
+	li	r0, OPAL_CONSOLE_WRITE_BUFFER_SPACE
+	sldi	r3,r3,32
+	or	r3,r3,r4
+	mr	r4,r5
+	b	opal_call
+
+	.globl opal_poll_events
+opal_poll_events:
+	li	r0, OPAL_POLL_EVENTS
+	b	opal_call
+
+	.globl opal_console_flush
+opal_console_flush:
+	li	r0, OPAL_CONSOLE_FLUSH
+	sldi	r3,r3,32
+	or	r3,r3,r4
+	b	opal_call
+
+#endif /* CONFIG_PPC64_BOOT_WRAPPER */
diff --git a/arch/powerpc/boot/ppc_asm.h b/arch/powerpc/boot/ppc_asm.h
index a66cfd76fa4d..754956ea4bec 100644
--- a/arch/powerpc/boot/ppc_asm.h
+++ b/arch/powerpc/boot/ppc_asm.h
@@ -60,6 +60,8 @@
 #define SPRN_HSRR0	0x13A	/* Hypervisor Save/Restore 0 */
 #define SPRN_HSRR1	0x13B	/* Hypervisor Save/Restore 1 */
 
+#define MSR_SF_LG	63              /* Enable 64 bit mode */
+
 #define MSR_LE		0x0000000000000001
 
 #define FIXUP_ENDIAN						   \
@@ -88,10 +90,6 @@
 #define LOAD_REG_ADDR(reg,name)			\
 	addis	reg,r2,name at toc@ha;		\
 	addi	reg,reg,name at toc@l
-#else
-#define LOAD_REG_ADDR(reg,name)			\
-	lis	reg,name at ha;			\
-	addi	reg,reg,name at l
 #endif
 
 #endif /* _PPC64_PPC_ASM_H */
-- 
2.39.5



More information about the Linuxppc-dev mailing list