aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/suspend_asm_64.S
blob: 48344b666d2c2e60f56d3393d82ecc48e0b9388f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* Copyright 2004,2005 Pavel Machek <pavel@suse.cz>, Andi Kleen <ak@suse.de>, Rafael J. Wysocki <rjw@sisk.pl>
 *
 * Distribute under GPLv2.
 *
 * swsusp_arch_resume must not use any stack or any nonlocal variables while
 * copying pages:
 *
 * Its rewriting one kernel image with another. What is stack in "old"
 * image could very well be data page in "new" image, and overwriting
 * your own stack under you is bad idea.
 */
	
	.text
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/asm-offsets.h>

ENTRY(swsusp_arch_suspend)

	movq %rsp, saved_context_esp(%rip)
	movq %rax, saved_context_eax(%rip)
	movq %rbx, saved_context_ebx(%rip)
	movq %rcx, saved_context_ecx(%rip)
	movq %rdx, saved_context_edx(%rip)
	movq %rbp, saved_context_ebp(%rip)
	movq %rsi, saved_context_esi(%rip)
	movq %rdi, saved_context_edi(%rip)
	movq %r8,  saved_context_r08(%rip)
	movq %r9,  saved_context_r09(%rip)
	movq %r10, saved_context_r10(%rip)
	movq %r11, saved_context_r11(%rip)
	movq %r12, saved_context_r12(%rip)
	movq %r13, saved_context_r13(%rip)
	movq %r14, saved_context_r14(%rip)
	movq %r15, saved_context_r15(%rip)
	pushfq ; popq saved_context_eflags(%rip)

	/* save the address of restore_registers */
	movq	$restore_registers, %rax
	movq	%rax, restore_jump_address(%rip)
	/* save cr3 */
	movq	%cr3, %rax
	movq	%rax, restore_cr3(%rip)

	call swsusp_save
	ret

ENTRY(restore_image)
	/* switch to temporary page tables */
	movq	$__PAGE_OFFSET, %rdx
	movq	temp_level4_pgt(%rip), %rax
	subq	%rdx, %rax
	movq	%rax, %cr3
	/* Flush TLB */
	movq	mmu_cr4_features(%rip), %rax
	movq	%rax, %rdx
	andq	$~(1<<7), %rdx	# PGE
	movq	%rdx, %cr4;  # turn off PGE
	movq	%cr3, %rcx;  # flush TLB
	movq	%rcx, %cr3;
	movq	%rax, %cr4;  # turn PGE back on

	/* prepare to jump to the image kernel */
	movq	restore_jump_address(%rip), %rax
	movq	restore_cr3(%rip), %rbx

	/* prepare to copy image data to their original locations */
	movq	restore_pblist(%rip), %rdx
	movq	relocated_restore_code(%rip), %rcx
	jmpq	*%rcx

	/* code below has been relocated to a safe page */
ENTRY(core_restore_code)
loop:
	testq	%rdx, %rdx
	jz	done

	/* get addresses from the pbe and copy the page */
	movq	pbe_address(%rdx), %rsi
	movq	pbe_orig_address(%rdx), %rdi
	movq	$(PAGE_SIZE >> 3), %rcx
	rep
	movsq

	/* progress to the next pbe */
	movq	pbe_next(%rdx), %rdx
	jmp	loop
done:
	/* jump to the restore_registers address from the image header */
	jmpq	*%rax
	/*
	 * NOTE: This assumes that the boot kernel's text mapping covers the
	 * image kernel's page containing restore_registers and the address of
	 * this page is the same as in the image kernel's text mapping (it
	 * should always be true, because the text mapping is linear, starting
	 * from 0, and is supposed to cover the entire kernel text for every
	 * kernel).
	 *
	 * code below belongs to the image kernel
	 */

ENTRY(restore_registers)
	/* go back to the original page tables */
	movq    %rbx, %cr3

	/* Flush TLB, including "global" things (vmalloc) */
	movq	mmu_cr4_features(%rip), %rax
	movq	%rax, %rdx
	andq	$~(1<<7), %rdx;  # PGE
	movq	%rdx, %cr4;  # turn off PGE
	movq	%cr3, %rcx;  # flush TLB
	movq	%rcx, %cr3
	movq	%rax, %cr4;  # turn PGE back on

	movq saved_context_esp(%rip), %rsp
	movq saved_context_ebp(%rip), %rbp
	/* restore GPRs (we don't restore %rax, it must be 0 anyway) */
	movq saved_context_ebx(%rip), %rbx
	movq saved_context_ecx(%rip), %rcx
	movq saved_context_edx(%rip), %rdx
	movq saved_context_esi(%rip), %rsi
	movq saved_context_edi(%rip), %rdi
	movq saved_context_r08(%rip), %r8
	movq saved_context_r09(%rip), %r9
	movq saved_context_r10(%rip), %r10
	movq saved_context_r11(%rip), %r11
	movq saved_context_r12(%rip), %r12
	movq saved_context_r13(%rip), %r13
	movq saved_context_r14(%rip), %r14
	movq saved_context_r15(%rip), %r15
	pushq saved_context_eflags(%rip) ; popfq

	xorq	%rax, %rax

	/* tell the hibernation core that we've just restored the memory */
	movq	%rax, in_suspend(%rip)

	ret