[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