162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hibernation support for x86-64 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2007 Rafael J. Wysocki <rjw@sisk.pl> 662306a36Sopenharmony_ci * Copyright 2005 Andi Kleen <ak@suse.de> 762306a36Sopenharmony_ci * Copyright 2004 Pavel Machek <pavel@suse.cz> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * swsusp_arch_resume must not use any stack or any nonlocal variables while 1062306a36Sopenharmony_ci * copying pages: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Its rewriting one kernel image with another. What is stack in "old" 1362306a36Sopenharmony_ci * image could very well be data page in "new" image, and overwriting 1462306a36Sopenharmony_ci * your own stack under you is bad idea. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci .text 1862306a36Sopenharmony_ci#include <linux/linkage.h> 1962306a36Sopenharmony_ci#include <asm/segment.h> 2062306a36Sopenharmony_ci#include <asm/page_types.h> 2162306a36Sopenharmony_ci#include <asm/asm-offsets.h> 2262306a36Sopenharmony_ci#include <asm/processor-flags.h> 2362306a36Sopenharmony_ci#include <asm/frame.h> 2462306a36Sopenharmony_ci#include <asm/nospec-branch.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* code below belongs to the image kernel */ 2762306a36Sopenharmony_ci .align PAGE_SIZE 2862306a36Sopenharmony_ciSYM_FUNC_START(restore_registers) 2962306a36Sopenharmony_ci /* go back to the original page tables */ 3062306a36Sopenharmony_ci movq %r9, %cr3 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* Flush TLB, including "global" things (vmalloc) */ 3362306a36Sopenharmony_ci movq mmu_cr4_features(%rip), %rax 3462306a36Sopenharmony_ci movq %rax, %rdx 3562306a36Sopenharmony_ci andq $~(X86_CR4_PGE), %rdx 3662306a36Sopenharmony_ci movq %rdx, %cr4; # turn off PGE 3762306a36Sopenharmony_ci movq %cr3, %rcx; # flush TLB 3862306a36Sopenharmony_ci movq %rcx, %cr3 3962306a36Sopenharmony_ci movq %rax, %cr4; # turn PGE back on 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* We don't restore %rax, it must be 0 anyway */ 4262306a36Sopenharmony_ci movq $saved_context, %rax 4362306a36Sopenharmony_ci movq pt_regs_sp(%rax), %rsp 4462306a36Sopenharmony_ci movq pt_regs_bp(%rax), %rbp 4562306a36Sopenharmony_ci movq pt_regs_si(%rax), %rsi 4662306a36Sopenharmony_ci movq pt_regs_di(%rax), %rdi 4762306a36Sopenharmony_ci movq pt_regs_bx(%rax), %rbx 4862306a36Sopenharmony_ci movq pt_regs_cx(%rax), %rcx 4962306a36Sopenharmony_ci movq pt_regs_dx(%rax), %rdx 5062306a36Sopenharmony_ci movq pt_regs_r8(%rax), %r8 5162306a36Sopenharmony_ci movq pt_regs_r9(%rax), %r9 5262306a36Sopenharmony_ci movq pt_regs_r10(%rax), %r10 5362306a36Sopenharmony_ci movq pt_regs_r11(%rax), %r11 5462306a36Sopenharmony_ci movq pt_regs_r12(%rax), %r12 5562306a36Sopenharmony_ci movq pt_regs_r13(%rax), %r13 5662306a36Sopenharmony_ci movq pt_regs_r14(%rax), %r14 5762306a36Sopenharmony_ci movq pt_regs_r15(%rax), %r15 5862306a36Sopenharmony_ci pushq pt_regs_flags(%rax) 5962306a36Sopenharmony_ci popfq 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Saved in save_processor_state. */ 6262306a36Sopenharmony_ci lgdt saved_context_gdt_desc(%rax) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci xorl %eax, %eax 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* tell the hibernation core that we've just restored the memory */ 6762306a36Sopenharmony_ci movq %rax, in_suspend(%rip) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci RET 7062306a36Sopenharmony_ciSYM_FUNC_END(restore_registers) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciSYM_FUNC_START(swsusp_arch_suspend) 7362306a36Sopenharmony_ci movq $saved_context, %rax 7462306a36Sopenharmony_ci movq %rsp, pt_regs_sp(%rax) 7562306a36Sopenharmony_ci movq %rbp, pt_regs_bp(%rax) 7662306a36Sopenharmony_ci movq %rsi, pt_regs_si(%rax) 7762306a36Sopenharmony_ci movq %rdi, pt_regs_di(%rax) 7862306a36Sopenharmony_ci movq %rbx, pt_regs_bx(%rax) 7962306a36Sopenharmony_ci movq %rcx, pt_regs_cx(%rax) 8062306a36Sopenharmony_ci movq %rdx, pt_regs_dx(%rax) 8162306a36Sopenharmony_ci movq %r8, pt_regs_r8(%rax) 8262306a36Sopenharmony_ci movq %r9, pt_regs_r9(%rax) 8362306a36Sopenharmony_ci movq %r10, pt_regs_r10(%rax) 8462306a36Sopenharmony_ci movq %r11, pt_regs_r11(%rax) 8562306a36Sopenharmony_ci movq %r12, pt_regs_r12(%rax) 8662306a36Sopenharmony_ci movq %r13, pt_regs_r13(%rax) 8762306a36Sopenharmony_ci movq %r14, pt_regs_r14(%rax) 8862306a36Sopenharmony_ci movq %r15, pt_regs_r15(%rax) 8962306a36Sopenharmony_ci pushfq 9062306a36Sopenharmony_ci popq pt_regs_flags(%rax) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* save cr3 */ 9362306a36Sopenharmony_ci movq %cr3, %rax 9462306a36Sopenharmony_ci movq %rax, restore_cr3(%rip) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci FRAME_BEGIN 9762306a36Sopenharmony_ci call swsusp_save 9862306a36Sopenharmony_ci FRAME_END 9962306a36Sopenharmony_ci RET 10062306a36Sopenharmony_ciSYM_FUNC_END(swsusp_arch_suspend) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciSYM_FUNC_START(restore_image) 10362306a36Sopenharmony_ci /* prepare to jump to the image kernel */ 10462306a36Sopenharmony_ci movq restore_jump_address(%rip), %r8 10562306a36Sopenharmony_ci movq restore_cr3(%rip), %r9 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* prepare to switch to temporary page tables */ 10862306a36Sopenharmony_ci movq temp_pgt(%rip), %rax 10962306a36Sopenharmony_ci movq mmu_cr4_features(%rip), %rbx 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* prepare to copy image data to their original locations */ 11262306a36Sopenharmony_ci movq restore_pblist(%rip), %rdx 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* jump to relocated restore code */ 11562306a36Sopenharmony_ci movq relocated_restore_code(%rip), %rcx 11662306a36Sopenharmony_ci ANNOTATE_RETPOLINE_SAFE 11762306a36Sopenharmony_ci jmpq *%rcx 11862306a36Sopenharmony_ciSYM_FUNC_END(restore_image) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* code below has been relocated to a safe page */ 12162306a36Sopenharmony_ciSYM_FUNC_START(core_restore_code) 12262306a36Sopenharmony_ci /* switch to temporary page tables */ 12362306a36Sopenharmony_ci movq %rax, %cr3 12462306a36Sopenharmony_ci /* flush TLB */ 12562306a36Sopenharmony_ci movq %rbx, %rcx 12662306a36Sopenharmony_ci andq $~(X86_CR4_PGE), %rcx 12762306a36Sopenharmony_ci movq %rcx, %cr4; # turn off PGE 12862306a36Sopenharmony_ci movq %cr3, %rcx; # flush TLB 12962306a36Sopenharmony_ci movq %rcx, %cr3; 13062306a36Sopenharmony_ci movq %rbx, %cr4; # turn PGE back on 13162306a36Sopenharmony_ci.Lloop: 13262306a36Sopenharmony_ci testq %rdx, %rdx 13362306a36Sopenharmony_ci jz .Ldone 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* get addresses from the pbe and copy the page */ 13662306a36Sopenharmony_ci movq pbe_address(%rdx), %rsi 13762306a36Sopenharmony_ci movq pbe_orig_address(%rdx), %rdi 13862306a36Sopenharmony_ci movq $(PAGE_SIZE >> 3), %rcx 13962306a36Sopenharmony_ci rep 14062306a36Sopenharmony_ci movsq 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* progress to the next pbe */ 14362306a36Sopenharmony_ci movq pbe_next(%rdx), %rdx 14462306a36Sopenharmony_ci jmp .Lloop 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci.Ldone: 14762306a36Sopenharmony_ci /* jump to the restore_registers address from the image header */ 14862306a36Sopenharmony_ci ANNOTATE_RETPOLINE_SAFE 14962306a36Sopenharmony_ci jmpq *%r8 15062306a36Sopenharmony_ciSYM_FUNC_END(core_restore_code) 151