root/arch/powerpc/kernel/rtas_entry.S
/* SPDX-License-Identifier: GPL-2.0-or-later */

#include <asm/asm-offsets.h>
#include <asm/bug.h>
#include <asm/page.h>
#include <asm/ppc_asm.h>

/*
 * RTAS is called with MSR IR, DR, EE disabled, and LR in the return address.
 *
 * Note: r3 is an input parameter to rtas, so don't trash it...
 */

#ifdef CONFIG_PPC32
_GLOBAL(enter_rtas)
        stwu    r1,-INT_FRAME_SIZE(r1)
        mflr    r0
        stw     r0,INT_FRAME_SIZE+4(r1)
        LOAD_REG_ADDR(r4, rtas)
        lis     r6,1f@ha        /* physical return address for rtas */
        addi    r6,r6,1f@l
        tophys(r6,r6)
        lwz     r8,RTASENTRY(r4)
        lwz     r4,RTASBASE(r4)
        mfmsr   r9
        stw     r9,8(r1)
        li      r9,MSR_KERNEL & ~(MSR_IR|MSR_DR)
        mtlr    r6
        stw     r1, THREAD + RTAS_SP(r2)
        mtspr   SPRN_SRR0,r8
        mtspr   SPRN_SRR1,r9
        rfi
1:
        lis     r8, 1f@h
        ori     r8, r8, 1f@l
        LOAD_REG_IMMEDIATE(r9,MSR_KERNEL)
        mtspr   SPRN_SRR0,r8
        mtspr   SPRN_SRR1,r9
        rfi                     /* Reactivate MMU translation */
1:
        lwz     r8,INT_FRAME_SIZE+4(r1) /* get return address */
        lwz     r9,8(r1)        /* original msr value */
        addi    r1,r1,INT_FRAME_SIZE
        li      r0,0
        stw     r0, THREAD + RTAS_SP(r2)
        mtlr    r8
        mtmsr   r9
        blr                     /* return to caller */
_ASM_NOKPROBE_SYMBOL(enter_rtas)

#else /* CONFIG_PPC32 */
#include <asm/exception-64s.h>

/*
 * 32-bit rtas on 64-bit machines has the additional problem that RTAS may
 * not preserve the upper parts of registers it uses.
 */
_GLOBAL(enter_rtas)
        mflr    r0
        std     r0,16(r1)
        stdu    r1,-SWITCH_FRAME_SIZE(r1) /* Save SP and create stack space. */

        /* Because RTAS is running in 32b mode, it clobbers the high order half
         * of all registers that it saves.  We therefore save those registers
         * RTAS might touch to the stack.  (r0, r3-r12 are caller saved)
         */
        SAVE_GPR(2, r1)                 /* Save the TOC */
        SAVE_NVGPRS(r1)                 /* Save the non-volatiles */

        mfcr    r4
        std     r4,_CCR(r1)
        mfctr   r5
        std     r5,_CTR(r1)
        mfspr   r6,SPRN_XER
        std     r6,_XER(r1)
        mfdar   r7
        std     r7,_DAR(r1)
        mfdsisr r8
        std     r8,_DSISR(r1)

        /* Temporary workaround to clear CR until RTAS can be modified to
         * ignore all bits.
         */
        li      r0,0
        mtcr    r0

        mfmsr   r6

        /* Unfortunately, the stack pointer and the MSR are also clobbered,
         * so they are saved in the PACA which allows us to restore
         * our original state after RTAS returns.
         */
        std     r1,PACAR1(r13)
        std     r6,PACASAVEDMSR(r13)

        /* Setup our real return addr */
        LOAD_REG_ADDR(r4,rtas_return_loc)
        clrldi  r4,r4,2                 /* convert to realmode address */
        mtlr    r4

__enter_rtas:
        LOAD_REG_ADDR(r4, rtas)
        ld      r5,RTASENTRY(r4)        /* get the rtas->entry value */
        ld      r4,RTASBASE(r4)         /* get the rtas->base value */

        /*
         * RTAS runs in 32-bit big endian real mode, but leave MSR[RI] on as we
         * may hit NMI (SRESET or MCE) while in RTAS. RTAS should disable RI in
         * its critical regions (as specified in PAPR+ section 7.2.1). MSR[S]
         * is not impacted by RFI_TO_KERNEL (only urfid can unset it). So if
         * MSR[S] is set, it will remain when entering RTAS.
         * If we're in HV mode, RTAS must also run in HV mode, so extract MSR_HV
         * from the saved MSR value and insert into the value RTAS will use.
         */
        extrdi  r0, r6, 1, 63 - MSR_HV_LG
        LOAD_REG_IMMEDIATE(r6, MSR_ME | MSR_RI)
        insrdi  r6, r0, 1, 63 - MSR_HV_LG

        li      r0,0
        mtmsrd  r0,1                    /* disable RI before using SRR0/1 */
        
        mtspr   SPRN_SRR0,r5
        mtspr   SPRN_SRR1,r6
        RFI_TO_KERNEL
        b       .       /* prevent speculative execution */
rtas_return_loc:
        FIXUP_ENDIAN

        /* Set SF before anything. */
        LOAD_REG_IMMEDIATE(r6, MSR_KERNEL & ~(MSR_IR|MSR_DR))
        mtmsrd  r6

        /* relocation is off at this point */
        GET_PACA(r13)

        bcl     20,31,$+4
0:      mflr    r3
        ld      r3,(1f-0b)(r3)          /* get &rtas_restore_regs */

        ld      r1,PACAR1(r13)          /* Restore our SP */
        ld      r4,PACASAVEDMSR(r13)    /* Restore our MSR */

        mtspr   SPRN_SRR0,r3
        mtspr   SPRN_SRR1,r4
        RFI_TO_KERNEL
        b       .       /* prevent speculative execution */
_ASM_NOKPROBE_SYMBOL(enter_rtas)
_ASM_NOKPROBE_SYMBOL(__enter_rtas)
_ASM_NOKPROBE_SYMBOL(rtas_return_loc)

        .align  3
1:      .8byte  rtas_restore_regs

rtas_restore_regs:
        /* relocation is on at this point */
        REST_GPR(2, r1)                 /* Restore the TOC */
        REST_NVGPRS(r1)                 /* Restore the non-volatiles */

        ld      r4,_CCR(r1)
        mtcr    r4
        ld      r5,_CTR(r1)
        mtctr   r5
        ld      r6,_XER(r1)
        mtspr   SPRN_XER,r6
        ld      r7,_DAR(r1)
        mtdar   r7
        ld      r8,_DSISR(r1)
        mtdsisr r8

        addi    r1,r1,SWITCH_FRAME_SIZE /* Unstack our frame */
        ld      r0,16(r1)               /* get return address */

        mtlr    r0
        blr                             /* return to caller */

#endif /* CONFIG_PPC32 */