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 <[email protected]>
---
 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


Reply via email to