root/arch/powerpc/kernel/swsusp_asm64.S
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * PowerPC 64-bit swsusp implementation
 *
 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
 */

#include <linux/threads.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/cputable.h>
#include <asm/thread_info.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#include <asm/feature-fixups.h>

/*
 * Structure for storing CPU registers on the save area.
 */
#define SL_r1           0x00    /* stack pointer */
#define SL_PC           0x08
#define SL_MSR          0x10
#define SL_SDR1         0x18
#define SL_XER          0x20
#define SL_TB           0x40
#define SL_r2           0x48
#define SL_CR           0x50
#define SL_LR           0x58
#define SL_r12          0x60
#define SL_r13          0x68
#define SL_r14          0x70
#define SL_r15          0x78
#define SL_r16          0x80
#define SL_r17          0x88
#define SL_r18          0x90
#define SL_r19          0x98
#define SL_r20          0xa0
#define SL_r21          0xa8
#define SL_r22          0xb0
#define SL_r23          0xb8
#define SL_r24          0xc0
#define SL_r25          0xc8
#define SL_r26          0xd0
#define SL_r27          0xd8
#define SL_r28          0xe0
#define SL_r29          0xe8
#define SL_r30          0xf0
#define SL_r31          0xf8
#define SL_SPRG1        0x100
#define SL_TCR          0x108
#define SL_SIZE         SL_TCR+8

/* these macros rely on the save area being
 * pointed to by r11 */

#define SAVE_SPR(register)              \
        mfspr   r0, SPRN_##register     ;\
        std     r0, SL_##register(r11)
#define RESTORE_SPR(register)           \
        ld      r0, SL_##register(r11)  ;\
        mtspr   SPRN_##register, r0
#define SAVE_SPECIAL(special)           \
        mf##special     r0              ;\
        std     r0, SL_##special(r11)
#define RESTORE_SPECIAL(special)        \
        ld      r0, SL_##special(r11)   ;\
        mt##special     r0
#define SAVE_REGISTER(reg)              \
        std     reg, SL_##reg(r11)
#define RESTORE_REGISTER(reg)           \
        ld      reg, SL_##reg(r11)

/* space for storing cpu state */
        .section .data
        .align  5
swsusp_save_area:
        .space SL_SIZE

        .section .text
        .align  5
_GLOBAL(swsusp_arch_suspend)
        LOAD_REG_ADDR(r11, swsusp_save_area)
        SAVE_SPECIAL(LR)
        SAVE_REGISTER(r1)
        SAVE_SPECIAL(CR)
        SAVE_SPECIAL(TB)
        SAVE_REGISTER(r2)
        SAVE_REGISTER(r12)
        SAVE_REGISTER(r13)
        SAVE_REGISTER(r14)
        SAVE_REGISTER(r15)
        SAVE_REGISTER(r16)
        SAVE_REGISTER(r17)
        SAVE_REGISTER(r18)
        SAVE_REGISTER(r19)
        SAVE_REGISTER(r20)
        SAVE_REGISTER(r21)
        SAVE_REGISTER(r22)
        SAVE_REGISTER(r23)
        SAVE_REGISTER(r24)
        SAVE_REGISTER(r25)
        SAVE_REGISTER(r26)
        SAVE_REGISTER(r27)
        SAVE_REGISTER(r28)
        SAVE_REGISTER(r29)
        SAVE_REGISTER(r30)
        SAVE_REGISTER(r31)
        SAVE_SPECIAL(MSR)
        SAVE_SPECIAL(XER)
#ifdef CONFIG_PPC_BOOK3S_64
BEGIN_FW_FTR_SECTION
        SAVE_SPECIAL(SDR1)
END_FW_FTR_SECTION_IFCLR(FW_FEATURE_LPAR)
#else
        SAVE_SPR(TCR)

        /* Save SPRG1, SPRG1 be used save paca */
        SAVE_SPR(SPRG1)
#endif

        /* we push the stack up 128 bytes but don't store the
         * stack pointer on the stack like a real stackframe */
        addi    r1,r1,-128

        bl swsusp_save

        /* restore LR */
        LOAD_REG_ADDR(r11, swsusp_save_area)
        RESTORE_SPECIAL(LR)
        addi    r1,r1,128

        blr

/* Resume code */
_GLOBAL(swsusp_arch_resume)
        /* Stop pending alitvec streams and memory accesses */
BEGIN_FTR_SECTION
        PPC_DSSALL
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
        sync

        LOAD_REG_ADDR(r11, restore_pblist)
        ld      r12,0(r12)

        cmpdi   r12,0
        beq-    nothing_to_copy
        li      r15,PAGE_SIZE>>3
copyloop:
        ld      r13,pbe_address(r12)
        ld      r14,pbe_orig_address(r12)

        mtctr   r15
        li      r10,0
copy_page_loop:
        ldx     r0,r10,r13
        stdx    r0,r10,r14
        addi    r10,r10,8
        bdnz copy_page_loop

        ld      r12,pbe_next(r12)
        cmpdi   r12,0
        bne+    copyloop
nothing_to_copy:

#ifdef CONFIG_PPC_BOOK3S_64
        /* flush caches */
        lis     r3, 0x10
        mtctr   r3
        li      r3, 0
        ori     r3, r3, CONFIG_KERNEL_START>>48
        li      r0, 48
        sld     r3, r3, r0
        li      r0, 0
1:
        dcbf    0,r3
        addi    r3,r3,0x20
        bdnz    1b

        sync

        tlbia
#endif

        LOAD_REG_ADDR(r11, swsusp_save_area)

        RESTORE_SPECIAL(CR)

        /* restore timebase */
        /* load saved tb */
        ld      r1, SL_TB(r11)
        /* get upper 32 bits of it */
        srdi    r2, r1, 32
        /* clear tb lower to avoid wrap */
        li      r0, 0
        mttbl   r0
        /* set tb upper */
        mttbu   r2
        /* set tb lower */
        mttbl   r1

        /* restore registers */
        RESTORE_REGISTER(r1)
        RESTORE_REGISTER(r2)
        RESTORE_REGISTER(r12)
        RESTORE_REGISTER(r13)
        RESTORE_REGISTER(r14)
        RESTORE_REGISTER(r15)
        RESTORE_REGISTER(r16)
        RESTORE_REGISTER(r17)
        RESTORE_REGISTER(r18)
        RESTORE_REGISTER(r19)
        RESTORE_REGISTER(r20)
        RESTORE_REGISTER(r21)
        RESTORE_REGISTER(r22)
        RESTORE_REGISTER(r23)
        RESTORE_REGISTER(r24)
        RESTORE_REGISTER(r25)
        RESTORE_REGISTER(r26)
        RESTORE_REGISTER(r27)
        RESTORE_REGISTER(r28)
        RESTORE_REGISTER(r29)
        RESTORE_REGISTER(r30)
        RESTORE_REGISTER(r31)

#ifdef CONFIG_PPC_BOOK3S_64
        /* can't use RESTORE_SPECIAL(MSR) */
        ld      r0, SL_MSR(r11)
        mtmsrd  r0, 0
BEGIN_FW_FTR_SECTION
        RESTORE_SPECIAL(SDR1)
END_FW_FTR_SECTION_IFCLR(FW_FEATURE_LPAR)
#else
        /* Restore SPRG1, be used to save paca */
        ld      r0, SL_SPRG1(r11)
        mtsprg  1, r0

        RESTORE_SPECIAL(MSR)

        /* Restore TCR and clear any pending bits in TSR. */
        RESTORE_SPR(TCR)
        lis     r0, (TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS)@h
        mtspr   SPRN_TSR, r0

        /* Kick decrementer */
        li      r0, 1
        mtdec   r0

        /* Invalidate all tlbs */
        bl      _tlbil_all
#endif
        RESTORE_SPECIAL(XER)

        sync

        addi    r1,r1,-128
#ifdef CONFIG_PPC_BOOK3S_64
        bl      slb_flush_and_restore_bolted
#endif
        bl      do_after_copyback
        addi    r1,r1,128

        LOAD_REG_ADDR(r11, swsusp_save_area)
        RESTORE_SPECIAL(LR)

        li      r3, 0
        blr