[PATCH] powerpc: Fix OPAL support in 32-bit boot wrapper
From: Ben Hutchings <hidden>
Date: 2025-09-10 22:07:55
Subsystem:
linux for powerpc (32-bit and 64-bit), the rest · Maintainers:
Madhavan Srinivasan, Linus Torvalds
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 <redacted>
---
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@toc@ha; \ addi reg,reg,name@toc@l -#else -#define LOAD_REG_ADDR(reg,name) \ - lis reg,name@ha; \ - addi reg,reg,name@l #endif #endif /* _PPC64_PPC_ASM_H */
--
2.39.5