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