root/arch/powerpc/kvm/tm.S
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *
 * Derived from book3s_hv_rmhandlers.S, which is:
 *
 * Copyright 2011 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
 */

#include <linux/export.h>
#include <asm/reg.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#include <asm/tm.h>
#include <asm/cputable.h>

#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
#define VCPU_GPRS_TM(reg) (((reg) * ULONG_SIZE) + VCPU_GPR_TM)

/*
 * Save transactional state and TM-related registers.
 * Called with:
 * - r3 pointing to the vcpu struct
 * - r4 containing the MSR with current TS bits:
 *      (For HV KVM, it is VCPU_MSR ; For PR KVM, it is host MSR).
 * - r5 containing a flag indicating that non-volatile registers
 *      must be preserved.
 * If r5 == 0, this can modify all checkpointed registers, but
 * restores r1, r2 before exit.  If r5 != 0, this restores the
 * MSR TM/FP/VEC/VSX bits to their state on entry.
 */
_GLOBAL(__kvmppc_save_tm)
        mflr    r0
        std     r0, PPC_LR_STKOFF(r1)
        stdu    r1, -SWITCH_FRAME_SIZE(r1)

        mr      r9, r3
        cmpdi   cr7, r5, 0

        /* Turn on TM. */
        mfmsr   r8
        mr      r10, r8
        li      r0, 1
        rldimi  r8, r0, MSR_TM_LG, 63-MSR_TM_LG
        ori     r8, r8, MSR_FP
        oris    r8, r8, (MSR_VEC | MSR_VSX)@h
        mtmsrd  r8

        rldicl. r4, r4, 64 - MSR_TS_S_LG, 62
        beq     1f      /* TM not active in guest. */

        std     r1, HSTATE_SCRATCH2(r13)
        std     r3, HSTATE_SCRATCH1(r13)

        /* Save CR on the stack - even if r5 == 0 we need to get cr7 back. */
        mfcr    r6
        SAVE_GPR(6, r1)

        /* Save DSCR so we can restore it to avoid running with user value */
        mfspr   r7, SPRN_DSCR
        SAVE_GPR(7, r1)

        /*
         * We are going to do treclaim., which will modify all checkpointed
         * registers.  Save the non-volatile registers on the stack if
         * preservation of non-volatile state has been requested.
         */
        beq     cr7, 3f
        SAVE_NVGPRS(r1)

        /* MSR[TS] will be 0 (non-transactional) once we do treclaim. */
        li      r0, 0
        rldimi  r10, r0, MSR_TS_S_LG, 63 - MSR_TS_T_LG
        SAVE_GPR(10, r1)        /* final MSR value */
3:
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
BEGIN_FTR_SECTION
        /* Emulation of the treclaim instruction needs TEXASR before treclaim */
        mfspr   r6, SPRN_TEXASR
        std     r6, VCPU_ORIG_TEXASR(r3)
END_FTR_SECTION_IFSET(CPU_FTR_P9_TM_HV_ASSIST)
#endif

        /* Clear the MSR RI since r1, r13 are all going to be foobar. */
        li      r5, 0
        mtmsrd  r5, 1

        li      r3, TM_CAUSE_KVM_RESCHED

        /* All GPRs are volatile at this point. */
        TRECLAIM(R3)

        /* Temporarily store r13 and r9 so we have some regs to play with */
        SET_SCRATCH0(r13)
        GET_PACA(r13)
        std     r9, PACATMSCRATCH(r13)
        ld      r9, HSTATE_SCRATCH1(r13)

        /* Save away PPR soon so we don't run with user value. */
        std     r0, VCPU_GPRS_TM(0)(r9)
        mfspr   r0, SPRN_PPR
        HMT_MEDIUM

        /* Reload stack pointer. */
        std     r1, VCPU_GPRS_TM(1)(r9)
        ld      r1, HSTATE_SCRATCH2(r13)

        /* Set MSR RI now we have r1 and r13 back. */
        std     r2, VCPU_GPRS_TM(2)(r9)
        li      r2, MSR_RI
        mtmsrd  r2, 1

        /* Reload TOC pointer. */
        LOAD_PACA_TOC()

        /* Save all but r0-r2, r9 & r13 */
        reg = 3
        .rept   29
        .if (reg != 9) && (reg != 13)
        std     reg, VCPU_GPRS_TM(reg)(r9)
        .endif
        reg = reg + 1
        .endr
        /* ... now save r13 */
        GET_SCRATCH0(r4)
        std     r4, VCPU_GPRS_TM(13)(r9)
        /* ... and save r9 */
        ld      r4, PACATMSCRATCH(r13)
        std     r4, VCPU_GPRS_TM(9)(r9)

        /* Restore host DSCR and CR values, after saving guest values */
        mfcr    r6
        mfspr   r7, SPRN_DSCR
        stw     r6, VCPU_CR_TM(r9)
        std     r7, VCPU_DSCR_TM(r9)
        REST_GPR(6, r1)
        REST_GPR(7, r1)
        mtcr    r6
        mtspr   SPRN_DSCR, r7

        /* Save away checkpointed SPRs. */
        std     r0, VCPU_PPR_TM(r9)
        mflr    r5
        mfctr   r7
        mfspr   r8, SPRN_AMR
        mfspr   r10, SPRN_TAR
        mfxer   r11
        std     r5, VCPU_LR_TM(r9)
        std     r7, VCPU_CTR_TM(r9)
        std     r8, VCPU_AMR_TM(r9)
        std     r10, VCPU_TAR_TM(r9)
        std     r11, VCPU_XER_TM(r9)

        /* Save FP/VSX. */
        addi    r3, r9, VCPU_FPRS_TM
        bl      store_fp_state
        addi    r3, r9, VCPU_VRS_TM
        bl      store_vr_state
        mfspr   r6, SPRN_VRSAVE
        stw     r6, VCPU_VRSAVE_TM(r9)

        /* Restore non-volatile registers if requested to */
        beq     cr7, 1f
        REST_NVGPRS(r1)
        REST_GPR(10, r1)
1:
        /*
         * We need to save these SPRs after the treclaim so that the software
         * error code is recorded correctly in the TEXASR.  Also the user may
         * change these outside of a transaction, so they must always be
         * context switched.
         */
        mfspr   r7, SPRN_TEXASR
        std     r7, VCPU_TEXASR(r9)
        mfspr   r5, SPRN_TFHAR
        mfspr   r6, SPRN_TFIAR
        std     r5, VCPU_TFHAR(r9)
        std     r6, VCPU_TFIAR(r9)

        /* Restore MSR state if requested */
        beq     cr7, 2f
        mtmsrd  r10, 0
2:
        addi    r1, r1, SWITCH_FRAME_SIZE
        ld      r0, PPC_LR_STKOFF(r1)
        mtlr    r0
        blr

/*
 * _kvmppc_save_tm_pr() is a wrapper around __kvmppc_save_tm(), so that it can
 * be invoked from C function by PR KVM only.
 */
_GLOBAL(_kvmppc_save_tm_pr)
        mflr    r0
        std     r0, PPC_LR_STKOFF(r1)
        stdu    r1, -PPC_MIN_STKFRM(r1)

        mfspr   r8, SPRN_TAR
        std     r8, PPC_MIN_STKFRM-8(r1)

        li      r5, 1           /* preserve non-volatile registers */
        bl      __kvmppc_save_tm

        ld      r8, PPC_MIN_STKFRM-8(r1)
        mtspr   SPRN_TAR, r8

        addi    r1, r1, PPC_MIN_STKFRM
        ld      r0, PPC_LR_STKOFF(r1)
        mtlr    r0
        blr

EXPORT_SYMBOL_GPL(_kvmppc_save_tm_pr);

/*
 * Restore transactional state and TM-related registers.
 * Called with:
 *  - r3 pointing to the vcpu struct.
 *  - r4 is the guest MSR with desired TS bits:
 *      For HV KVM, it is VCPU_MSR
 *      For PR KVM, it is provided by caller
 * - r5 containing a flag indicating that non-volatile registers
 *      must be preserved.
 * If r5 == 0, this potentially modifies all checkpointed registers, but
 * restores r1, r2 from the PACA before exit.
 * If r5 != 0, this restores the MSR TM/FP/VEC/VSX bits to their state on entry.
 */
_GLOBAL(__kvmppc_restore_tm)
        mflr    r0
        std     r0, PPC_LR_STKOFF(r1)

        cmpdi   cr7, r5, 0

        /* Turn on TM/FP/VSX/VMX so we can restore them. */
        mfmsr   r5
        mr      r10, r5
        li      r6, MSR_TM >> 32
        sldi    r6, r6, 32
        or      r5, r5, r6
        ori     r5, r5, MSR_FP
        oris    r5, r5, (MSR_VEC | MSR_VSX)@h
        mtmsrd  r5

        /*
         * The user may change these outside of a transaction, so they must
         * always be context switched.
         */
        ld      r5, VCPU_TFHAR(r3)
        ld      r6, VCPU_TFIAR(r3)
        ld      r7, VCPU_TEXASR(r3)
        mtspr   SPRN_TFHAR, r5
        mtspr   SPRN_TFIAR, r6
        mtspr   SPRN_TEXASR, r7

        mr      r5, r4
        rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
        beq     9f              /* TM not active in guest */

        /* Make sure the failure summary is set, otherwise we'll program check
         * when we trechkpt.  It's possible that this might have been not set
         * on a kvmppc_set_one_reg() call but we shouldn't let this crash the
         * host.
         */
        oris    r7, r7, (TEXASR_FS)@h
        mtspr   SPRN_TEXASR, r7

        /*
         * Make a stack frame and save non-volatile registers if requested.
         */
        stdu    r1, -SWITCH_FRAME_SIZE(r1)
        std     r1, HSTATE_SCRATCH2(r13)

        mfcr    r6
        mfspr   r7, SPRN_DSCR
        SAVE_GPR(2, r1)
        SAVE_GPR(6, r1)
        SAVE_GPR(7, r1)

        beq     cr7, 4f
        SAVE_NVGPRS(r1)

        /* MSR[TS] will be 1 (suspended) once we do trechkpt */
        li      r0, 1
        rldimi  r10, r0, MSR_TS_S_LG, 63 - MSR_TS_T_LG
        SAVE_GPR(10, r1)        /* final MSR value */
4:
        /*
         * We need to load up the checkpointed state for the guest.
         * We need to do this early as it will blow away any GPRs, VSRs and
         * some SPRs.
         */

        mr      r31, r3
        addi    r3, r31, VCPU_FPRS_TM
        bl      load_fp_state
        addi    r3, r31, VCPU_VRS_TM
        bl      load_vr_state
        mr      r3, r31
        lwz     r7, VCPU_VRSAVE_TM(r3)
        mtspr   SPRN_VRSAVE, r7

        ld      r5, VCPU_LR_TM(r3)
        lwz     r6, VCPU_CR_TM(r3)
        ld      r7, VCPU_CTR_TM(r3)
        ld      r8, VCPU_AMR_TM(r3)
        ld      r9, VCPU_TAR_TM(r3)
        ld      r10, VCPU_XER_TM(r3)
        mtlr    r5
        mtcr    r6
        mtctr   r7
        mtspr   SPRN_AMR, r8
        mtspr   SPRN_TAR, r9
        mtxer   r10

        /*
         * Load up PPR and DSCR values but don't put them in the actual SPRs
         * till the last moment to avoid running with userspace PPR and DSCR for
         * too long.
         */
        ld      r29, VCPU_DSCR_TM(r3)
        ld      r30, VCPU_PPR_TM(r3)

        /* Clear the MSR RI since r1, r13 are all going to be foobar. */
        li      r5, 0
        mtmsrd  r5, 1

        /* Load GPRs r0-r28 */
        reg = 0
        .rept   29
        ld      reg, VCPU_GPRS_TM(reg)(r31)
        reg = reg + 1
        .endr

        mtspr   SPRN_DSCR, r29
        mtspr   SPRN_PPR, r30

        /* Load final GPRs */
        ld      29, VCPU_GPRS_TM(29)(r31)
        ld      30, VCPU_GPRS_TM(30)(r31)
        ld      31, VCPU_GPRS_TM(31)(r31)

        /* TM checkpointed state is now setup.  All GPRs are now volatile. */
        TRECHKPT

        /* Now let's get back the state we need. */
        HMT_MEDIUM
        GET_PACA(r13)
        ld      r1, HSTATE_SCRATCH2(r13)
        REST_GPR(7, r1)
        mtspr   SPRN_DSCR, r7

        /* Set the MSR RI since we have our registers back. */
        li      r5, MSR_RI
        mtmsrd  r5, 1

        /* Restore TOC pointer and CR */
        REST_GPR(2, r1)
        REST_GPR(6, r1)
        mtcr    r6

        /* Restore non-volatile registers if requested to. */
        beq     cr7, 5f
        REST_GPR(10, r1)
        REST_NVGPRS(r1)

5:      addi    r1, r1, SWITCH_FRAME_SIZE
        ld      r0, PPC_LR_STKOFF(r1)
        mtlr    r0

9:      /* Restore MSR bits if requested */
        beqlr   cr7
        mtmsrd  r10, 0
        blr

/*
 * _kvmppc_restore_tm_pr() is a wrapper around __kvmppc_restore_tm(), so that it
 * can be invoked from C function by PR KVM only.
 */
_GLOBAL(_kvmppc_restore_tm_pr)
        mflr    r0
        std     r0, PPC_LR_STKOFF(r1)
        stdu    r1, -PPC_MIN_STKFRM(r1)

        /* save TAR so that it can be recovered later */
        mfspr   r8, SPRN_TAR
        std     r8, PPC_MIN_STKFRM-8(r1)

        li      r5, 1
        bl      __kvmppc_restore_tm

        ld      r8, PPC_MIN_STKFRM-8(r1)
        mtspr   SPRN_TAR, r8

        addi    r1, r1, PPC_MIN_STKFRM
        ld      r0, PPC_LR_STKOFF(r1)
        mtlr    r0
        blr

EXPORT_SYMBOL_GPL(_kvmppc_restore_tm_pr);
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */