18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hibernation support for x86-64 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2007 Rafael J. Wysocki <rjw@sisk.pl> 68c2ecf20Sopenharmony_ci * Copyright 2005 Andi Kleen <ak@suse.de> 78c2ecf20Sopenharmony_ci * Copyright 2004 Pavel Machek <pavel@suse.cz> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * swsusp_arch_resume must not use any stack or any nonlocal variables while 108c2ecf20Sopenharmony_ci * copying pages: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Its rewriting one kernel image with another. What is stack in "old" 138c2ecf20Sopenharmony_ci * image could very well be data page in "new" image, and overwriting 148c2ecf20Sopenharmony_ci * your own stack under you is bad idea. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci .text 188c2ecf20Sopenharmony_ci#include <linux/linkage.h> 198c2ecf20Sopenharmony_ci#include <asm/segment.h> 208c2ecf20Sopenharmony_ci#include <asm/page_types.h> 218c2ecf20Sopenharmony_ci#include <asm/asm-offsets.h> 228c2ecf20Sopenharmony_ci#include <asm/processor-flags.h> 238c2ecf20Sopenharmony_ci#include <asm/frame.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciSYM_FUNC_START(swsusp_arch_suspend) 268c2ecf20Sopenharmony_ci movq $saved_context, %rax 278c2ecf20Sopenharmony_ci movq %rsp, pt_regs_sp(%rax) 288c2ecf20Sopenharmony_ci movq %rbp, pt_regs_bp(%rax) 298c2ecf20Sopenharmony_ci movq %rsi, pt_regs_si(%rax) 308c2ecf20Sopenharmony_ci movq %rdi, pt_regs_di(%rax) 318c2ecf20Sopenharmony_ci movq %rbx, pt_regs_bx(%rax) 328c2ecf20Sopenharmony_ci movq %rcx, pt_regs_cx(%rax) 338c2ecf20Sopenharmony_ci movq %rdx, pt_regs_dx(%rax) 348c2ecf20Sopenharmony_ci movq %r8, pt_regs_r8(%rax) 358c2ecf20Sopenharmony_ci movq %r9, pt_regs_r9(%rax) 368c2ecf20Sopenharmony_ci movq %r10, pt_regs_r10(%rax) 378c2ecf20Sopenharmony_ci movq %r11, pt_regs_r11(%rax) 388c2ecf20Sopenharmony_ci movq %r12, pt_regs_r12(%rax) 398c2ecf20Sopenharmony_ci movq %r13, pt_regs_r13(%rax) 408c2ecf20Sopenharmony_ci movq %r14, pt_regs_r14(%rax) 418c2ecf20Sopenharmony_ci movq %r15, pt_regs_r15(%rax) 428c2ecf20Sopenharmony_ci pushfq 438c2ecf20Sopenharmony_ci popq pt_regs_flags(%rax) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* save cr3 */ 468c2ecf20Sopenharmony_ci movq %cr3, %rax 478c2ecf20Sopenharmony_ci movq %rax, restore_cr3(%rip) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci FRAME_BEGIN 508c2ecf20Sopenharmony_ci call swsusp_save 518c2ecf20Sopenharmony_ci FRAME_END 528c2ecf20Sopenharmony_ci RET 538c2ecf20Sopenharmony_ciSYM_FUNC_END(swsusp_arch_suspend) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciSYM_CODE_START(restore_image) 568c2ecf20Sopenharmony_ci /* prepare to jump to the image kernel */ 578c2ecf20Sopenharmony_ci movq restore_jump_address(%rip), %r8 588c2ecf20Sopenharmony_ci movq restore_cr3(%rip), %r9 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* prepare to switch to temporary page tables */ 618c2ecf20Sopenharmony_ci movq temp_pgt(%rip), %rax 628c2ecf20Sopenharmony_ci movq mmu_cr4_features(%rip), %rbx 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* prepare to copy image data to their original locations */ 658c2ecf20Sopenharmony_ci movq restore_pblist(%rip), %rdx 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* jump to relocated restore code */ 688c2ecf20Sopenharmony_ci movq relocated_restore_code(%rip), %rcx 698c2ecf20Sopenharmony_ci jmpq *%rcx 708c2ecf20Sopenharmony_ciSYM_CODE_END(restore_image) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* code below has been relocated to a safe page */ 738c2ecf20Sopenharmony_ciSYM_CODE_START(core_restore_code) 748c2ecf20Sopenharmony_ci /* switch to temporary page tables */ 758c2ecf20Sopenharmony_ci movq %rax, %cr3 768c2ecf20Sopenharmony_ci /* flush TLB */ 778c2ecf20Sopenharmony_ci movq %rbx, %rcx 788c2ecf20Sopenharmony_ci andq $~(X86_CR4_PGE), %rcx 798c2ecf20Sopenharmony_ci movq %rcx, %cr4; # turn off PGE 808c2ecf20Sopenharmony_ci movq %cr3, %rcx; # flush TLB 818c2ecf20Sopenharmony_ci movq %rcx, %cr3; 828c2ecf20Sopenharmony_ci movq %rbx, %cr4; # turn PGE back on 838c2ecf20Sopenharmony_ci.Lloop: 848c2ecf20Sopenharmony_ci testq %rdx, %rdx 858c2ecf20Sopenharmony_ci jz .Ldone 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* get addresses from the pbe and copy the page */ 888c2ecf20Sopenharmony_ci movq pbe_address(%rdx), %rsi 898c2ecf20Sopenharmony_ci movq pbe_orig_address(%rdx), %rdi 908c2ecf20Sopenharmony_ci movq $(PAGE_SIZE >> 3), %rcx 918c2ecf20Sopenharmony_ci rep 928c2ecf20Sopenharmony_ci movsq 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* progress to the next pbe */ 958c2ecf20Sopenharmony_ci movq pbe_next(%rdx), %rdx 968c2ecf20Sopenharmony_ci jmp .Lloop 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci.Ldone: 998c2ecf20Sopenharmony_ci /* jump to the restore_registers address from the image header */ 1008c2ecf20Sopenharmony_ci jmpq *%r8 1018c2ecf20Sopenharmony_ciSYM_CODE_END(core_restore_code) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* code below belongs to the image kernel */ 1048c2ecf20Sopenharmony_ci .align PAGE_SIZE 1058c2ecf20Sopenharmony_ciSYM_FUNC_START(restore_registers) 1068c2ecf20Sopenharmony_ci /* go back to the original page tables */ 1078c2ecf20Sopenharmony_ci movq %r9, %cr3 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Flush TLB, including "global" things (vmalloc) */ 1108c2ecf20Sopenharmony_ci movq mmu_cr4_features(%rip), %rax 1118c2ecf20Sopenharmony_ci movq %rax, %rdx 1128c2ecf20Sopenharmony_ci andq $~(X86_CR4_PGE), %rdx 1138c2ecf20Sopenharmony_ci movq %rdx, %cr4; # turn off PGE 1148c2ecf20Sopenharmony_ci movq %cr3, %rcx; # flush TLB 1158c2ecf20Sopenharmony_ci movq %rcx, %cr3 1168c2ecf20Sopenharmony_ci movq %rax, %cr4; # turn PGE back on 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* We don't restore %rax, it must be 0 anyway */ 1198c2ecf20Sopenharmony_ci movq $saved_context, %rax 1208c2ecf20Sopenharmony_ci movq pt_regs_sp(%rax), %rsp 1218c2ecf20Sopenharmony_ci movq pt_regs_bp(%rax), %rbp 1228c2ecf20Sopenharmony_ci movq pt_regs_si(%rax), %rsi 1238c2ecf20Sopenharmony_ci movq pt_regs_di(%rax), %rdi 1248c2ecf20Sopenharmony_ci movq pt_regs_bx(%rax), %rbx 1258c2ecf20Sopenharmony_ci movq pt_regs_cx(%rax), %rcx 1268c2ecf20Sopenharmony_ci movq pt_regs_dx(%rax), %rdx 1278c2ecf20Sopenharmony_ci movq pt_regs_r8(%rax), %r8 1288c2ecf20Sopenharmony_ci movq pt_regs_r9(%rax), %r9 1298c2ecf20Sopenharmony_ci movq pt_regs_r10(%rax), %r10 1308c2ecf20Sopenharmony_ci movq pt_regs_r11(%rax), %r11 1318c2ecf20Sopenharmony_ci movq pt_regs_r12(%rax), %r12 1328c2ecf20Sopenharmony_ci movq pt_regs_r13(%rax), %r13 1338c2ecf20Sopenharmony_ci movq pt_regs_r14(%rax), %r14 1348c2ecf20Sopenharmony_ci movq pt_regs_r15(%rax), %r15 1358c2ecf20Sopenharmony_ci pushq pt_regs_flags(%rax) 1368c2ecf20Sopenharmony_ci popfq 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Saved in save_processor_state. */ 1398c2ecf20Sopenharmony_ci lgdt saved_context_gdt_desc(%rax) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci xorl %eax, %eax 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* tell the hibernation core that we've just restored the memory */ 1448c2ecf20Sopenharmony_ci movq %rax, in_suspend(%rip) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci RET 1478c2ecf20Sopenharmony_ciSYM_FUNC_END(restore_registers) 148