diff options
author | Zhimin Gu <kookoo.gu@intel.com> | 2018-09-21 02:26:58 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2018-10-03 05:56:33 -0400 |
commit | 25862a049e6f04cc982f4bed25ed3e6f0a0a5a61 (patch) | |
tree | 7ab865a3abbb99450a2ecd40268106d1bb63d016 /arch/x86/power | |
parent | 8e5b2a3c5a773f161b57eee7156a63089edd2c5c (diff) |
x86, hibernate: Extract the common code of 64/32 bit system
Reduce the hibernation code duplication between x86-32 and x86-64
by extracting the common code into hibernate.c.
Currently only pfn_is_nosave() is the activated common
function in hibernate.c
No functional change.
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Zhimin Gu <kookoo.gu@intel.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'arch/x86/power')
-rw-r--r-- | arch/x86/power/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/power/hibernate.c | 246 | ||||
-rw-r--r-- | arch/x86/power/hibernate_32.c | 15 | ||||
-rw-r--r-- | arch/x86/power/hibernate_64.c | 221 |
4 files changed, 248 insertions, 236 deletions
diff --git a/arch/x86/power/Makefile b/arch/x86/power/Makefile index a4701389562c..37923d715741 100644 --- a/arch/x86/power/Makefile +++ b/arch/x86/power/Makefile | |||
@@ -7,4 +7,4 @@ nostackp := $(call cc-option, -fno-stack-protector) | |||
7 | CFLAGS_cpu.o := $(nostackp) | 7 | CFLAGS_cpu.o := $(nostackp) |
8 | 8 | ||
9 | obj-$(CONFIG_PM_SLEEP) += cpu.o | 9 | obj-$(CONFIG_PM_SLEEP) += cpu.o |
10 | obj-$(CONFIG_HIBERNATION) += hibernate_$(BITS).o hibernate_asm_$(BITS).o | 10 | obj-$(CONFIG_HIBERNATION) += hibernate_$(BITS).o hibernate_asm_$(BITS).o hibernate.o |
diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c new file mode 100644 index 000000000000..f63793b8de3f --- /dev/null +++ b/arch/x86/power/hibernate.c | |||
@@ -0,0 +1,246 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Hibernation support for x86 | ||
4 | * | ||
5 | * Copyright (c) 2007 Rafael J. Wysocki <rjw@sisk.pl> | ||
6 | * Copyright (c) 2002 Pavel Machek <pavel@ucw.cz> | ||
7 | * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org> | ||
8 | */ | ||
9 | #include <linux/gfp.h> | ||
10 | #include <linux/smp.h> | ||
11 | #include <linux/suspend.h> | ||
12 | #include <linux/scatterlist.h> | ||
13 | #include <linux/kdebug.h> | ||
14 | |||
15 | #include <crypto/hash.h> | ||
16 | |||
17 | #include <asm/e820/api.h> | ||
18 | #include <asm/init.h> | ||
19 | #include <asm/proto.h> | ||
20 | #include <asm/page.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/mtrr.h> | ||
23 | #include <asm/sections.h> | ||
24 | #include <asm/suspend.h> | ||
25 | #include <asm/tlbflush.h> | ||
26 | |||
27 | /* | ||
28 | * Address to jump to in the last phase of restore in order to get to the image | ||
29 | * kernel's text (this value is passed in the image header). | ||
30 | */ | ||
31 | unsigned long restore_jump_address __visible; | ||
32 | unsigned long jump_address_phys; | ||
33 | |||
34 | /* | ||
35 | * Value of the cr3 register from before the hibernation (this value is passed | ||
36 | * in the image header). | ||
37 | */ | ||
38 | unsigned long restore_cr3 __visible; | ||
39 | unsigned long temp_level4_pgt __visible; | ||
40 | unsigned long relocated_restore_code __visible; | ||
41 | |||
42 | /** | ||
43 | * pfn_is_nosave - check if given pfn is in the 'nosave' section | ||
44 | */ | ||
45 | int pfn_is_nosave(unsigned long pfn) | ||
46 | { | ||
47 | unsigned long nosave_begin_pfn; | ||
48 | unsigned long nosave_end_pfn; | ||
49 | |||
50 | nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT; | ||
51 | nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT; | ||
52 | |||
53 | return pfn >= nosave_begin_pfn && pfn < nosave_end_pfn; | ||
54 | } | ||
55 | |||
56 | #ifdef CONFIG_X86_64 | ||
57 | |||
58 | #define MD5_DIGEST_SIZE 16 | ||
59 | |||
60 | struct restore_data_record { | ||
61 | unsigned long jump_address; | ||
62 | unsigned long jump_address_phys; | ||
63 | unsigned long cr3; | ||
64 | unsigned long magic; | ||
65 | u8 e820_digest[MD5_DIGEST_SIZE]; | ||
66 | }; | ||
67 | |||
68 | #if IS_BUILTIN(CONFIG_CRYPTO_MD5) | ||
69 | /** | ||
70 | * get_e820_md5 - calculate md5 according to given e820 table | ||
71 | * | ||
72 | * @table: the e820 table to be calculated | ||
73 | * @buf: the md5 result to be stored to | ||
74 | */ | ||
75 | static int get_e820_md5(struct e820_table *table, void *buf) | ||
76 | { | ||
77 | struct crypto_shash *tfm; | ||
78 | struct shash_desc *desc; | ||
79 | int size; | ||
80 | int ret = 0; | ||
81 | |||
82 | tfm = crypto_alloc_shash("md5", 0, 0); | ||
83 | if (IS_ERR(tfm)) | ||
84 | return -ENOMEM; | ||
85 | |||
86 | desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm), | ||
87 | GFP_KERNEL); | ||
88 | if (!desc) { | ||
89 | ret = -ENOMEM; | ||
90 | goto free_tfm; | ||
91 | } | ||
92 | |||
93 | desc->tfm = tfm; | ||
94 | desc->flags = 0; | ||
95 | |||
96 | size = offsetof(struct e820_table, entries) + | ||
97 | sizeof(struct e820_entry) * table->nr_entries; | ||
98 | |||
99 | if (crypto_shash_digest(desc, (u8 *)table, size, buf)) | ||
100 | ret = -EINVAL; | ||
101 | |||
102 | kzfree(desc); | ||
103 | |||
104 | free_tfm: | ||
105 | crypto_free_shash(tfm); | ||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | static int hibernation_e820_save(void *buf) | ||
110 | { | ||
111 | return get_e820_md5(e820_table_firmware, buf); | ||
112 | } | ||
113 | |||
114 | static bool hibernation_e820_mismatch(void *buf) | ||
115 | { | ||
116 | int ret; | ||
117 | u8 result[MD5_DIGEST_SIZE]; | ||
118 | |||
119 | memset(result, 0, MD5_DIGEST_SIZE); | ||
120 | /* If there is no digest in suspend kernel, let it go. */ | ||
121 | if (!memcmp(result, buf, MD5_DIGEST_SIZE)) | ||
122 | return false; | ||
123 | |||
124 | ret = get_e820_md5(e820_table_firmware, result); | ||
125 | if (ret) | ||
126 | return true; | ||
127 | |||
128 | return memcmp(result, buf, MD5_DIGEST_SIZE) ? true : false; | ||
129 | } | ||
130 | #else | ||
131 | static int hibernation_e820_save(void *buf) | ||
132 | { | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static bool hibernation_e820_mismatch(void *buf) | ||
137 | { | ||
138 | /* If md5 is not builtin for restore kernel, let it go. */ | ||
139 | return false; | ||
140 | } | ||
141 | #endif | ||
142 | |||
143 | #define RESTORE_MAGIC 0x23456789ABCDEF01UL | ||
144 | |||
145 | /** | ||
146 | * arch_hibernation_header_save - populate the architecture specific part | ||
147 | * of a hibernation image header | ||
148 | * @addr: address to save the data at | ||
149 | */ | ||
150 | int arch_hibernation_header_save(void *addr, unsigned int max_size) | ||
151 | { | ||
152 | struct restore_data_record *rdr = addr; | ||
153 | |||
154 | if (max_size < sizeof(struct restore_data_record)) | ||
155 | return -EOVERFLOW; | ||
156 | rdr->magic = RESTORE_MAGIC; | ||
157 | rdr->jump_address = (unsigned long)restore_registers; | ||
158 | rdr->jump_address_phys = __pa_symbol(restore_registers); | ||
159 | |||
160 | /* | ||
161 | * The restore code fixes up CR3 and CR4 in the following sequence: | ||
162 | * | ||
163 | * [in hibernation asm] | ||
164 | * 1. CR3 <= temporary page tables | ||
165 | * 2. CR4 <= mmu_cr4_features (from the kernel that restores us) | ||
166 | * 3. CR3 <= rdr->cr3 | ||
167 | * 4. CR4 <= mmu_cr4_features (from us, i.e. the image kernel) | ||
168 | * [in restore_processor_state()] | ||
169 | * 5. CR4 <= saved CR4 | ||
170 | * 6. CR3 <= saved CR3 | ||
171 | * | ||
172 | * Our mmu_cr4_features has CR4.PCIDE=0, and toggling | ||
173 | * CR4.PCIDE while CR3's PCID bits are nonzero is illegal, so | ||
174 | * rdr->cr3 needs to point to valid page tables but must not | ||
175 | * have any of the PCID bits set. | ||
176 | */ | ||
177 | rdr->cr3 = restore_cr3 & ~CR3_PCID_MASK; | ||
178 | |||
179 | return hibernation_e820_save(rdr->e820_digest); | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * arch_hibernation_header_restore - read the architecture specific data | ||
184 | * from the hibernation image header | ||
185 | * @addr: address to read the data from | ||
186 | */ | ||
187 | int arch_hibernation_header_restore(void *addr) | ||
188 | { | ||
189 | struct restore_data_record *rdr = addr; | ||
190 | |||
191 | if (rdr->magic != RESTORE_MAGIC) { | ||
192 | pr_crit("Unrecognized hibernate image header format!\n"); | ||
193 | return -EINVAL; | ||
194 | } | ||
195 | |||
196 | restore_jump_address = rdr->jump_address; | ||
197 | jump_address_phys = rdr->jump_address_phys; | ||
198 | restore_cr3 = rdr->cr3; | ||
199 | |||
200 | if (hibernation_e820_mismatch(rdr->e820_digest)) { | ||
201 | pr_crit("Hibernate inconsistent memory map detected!\n"); | ||
202 | return -ENODEV; | ||
203 | } | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | int relocate_restore_code(void) | ||
209 | { | ||
210 | pgd_t *pgd; | ||
211 | p4d_t *p4d; | ||
212 | pud_t *pud; | ||
213 | pmd_t *pmd; | ||
214 | pte_t *pte; | ||
215 | |||
216 | relocated_restore_code = get_safe_page(GFP_ATOMIC); | ||
217 | if (!relocated_restore_code) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | memcpy((void *)relocated_restore_code, core_restore_code, PAGE_SIZE); | ||
221 | |||
222 | /* Make the page containing the relocated code executable */ | ||
223 | pgd = (pgd_t *)__va(read_cr3_pa()) + | ||
224 | pgd_index(relocated_restore_code); | ||
225 | p4d = p4d_offset(pgd, relocated_restore_code); | ||
226 | if (p4d_large(*p4d)) { | ||
227 | set_p4d(p4d, __p4d(p4d_val(*p4d) & ~_PAGE_NX)); | ||
228 | goto out; | ||
229 | } | ||
230 | pud = pud_offset(p4d, relocated_restore_code); | ||
231 | if (pud_large(*pud)) { | ||
232 | set_pud(pud, __pud(pud_val(*pud) & ~_PAGE_NX)); | ||
233 | goto out; | ||
234 | } | ||
235 | pmd = pmd_offset(pud, relocated_restore_code); | ||
236 | if (pmd_large(*pmd)) { | ||
237 | set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_NX)); | ||
238 | goto out; | ||
239 | } | ||
240 | pte = pte_offset_kernel(pmd, relocated_restore_code); | ||
241 | set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_NX)); | ||
242 | out: | ||
243 | __flush_tlb_all(); | ||
244 | return 0; | ||
245 | } | ||
246 | #endif | ||
diff --git a/arch/x86/power/hibernate_32.c b/arch/x86/power/hibernate_32.c index afc4ed7b1578..f82fbd279d08 100644 --- a/arch/x86/power/hibernate_32.c +++ b/arch/x86/power/hibernate_32.c | |||
@@ -14,9 +14,7 @@ | |||
14 | #include <asm/pgtable.h> | 14 | #include <asm/pgtable.h> |
15 | #include <asm/mmzone.h> | 15 | #include <asm/mmzone.h> |
16 | #include <asm/sections.h> | 16 | #include <asm/sections.h> |
17 | 17 | #include <asm/suspend.h> | |
18 | /* Defined in hibernate_asm_32.S */ | ||
19 | extern int restore_image(void); | ||
20 | 18 | ||
21 | /* Pointer to the temporary resume page tables */ | 19 | /* Pointer to the temporary resume page tables */ |
22 | pgd_t *resume_pg_dir; | 20 | pgd_t *resume_pg_dir; |
@@ -162,14 +160,3 @@ asmlinkage int swsusp_arch_resume(void) | |||
162 | restore_image(); | 160 | restore_image(); |
163 | return 0; | 161 | return 0; |
164 | } | 162 | } |
165 | |||
166 | /* | ||
167 | * pfn_is_nosave - check if given pfn is in the 'nosave' section | ||
168 | */ | ||
169 | |||
170 | int pfn_is_nosave(unsigned long pfn) | ||
171 | { | ||
172 | unsigned long nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT; | ||
173 | unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT; | ||
174 | return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); | ||
175 | } | ||
diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c index e0de959ad02d..8bc2eb0dc464 100644 --- a/arch/x86/power/hibernate_64.c +++ b/arch/x86/power/hibernate_64.c | |||
@@ -26,26 +26,6 @@ | |||
26 | #include <asm/suspend.h> | 26 | #include <asm/suspend.h> |
27 | #include <asm/tlbflush.h> | 27 | #include <asm/tlbflush.h> |
28 | 28 | ||
29 | /* Defined in hibernate_asm_64.S */ | ||
30 | extern asmlinkage __visible int restore_image(void); | ||
31 | |||
32 | /* | ||
33 | * Address to jump to in the last phase of restore in order to get to the image | ||
34 | * kernel's text (this value is passed in the image header). | ||
35 | */ | ||
36 | unsigned long restore_jump_address __visible; | ||
37 | unsigned long jump_address_phys; | ||
38 | |||
39 | /* | ||
40 | * Value of the cr3 register from before the hibernation (this value is passed | ||
41 | * in the image header). | ||
42 | */ | ||
43 | unsigned long restore_cr3 __visible; | ||
44 | |||
45 | unsigned long temp_level4_pgt __visible; | ||
46 | |||
47 | unsigned long relocated_restore_code __visible; | ||
48 | |||
49 | static int set_up_temporary_text_mapping(pgd_t *pgd) | 29 | static int set_up_temporary_text_mapping(pgd_t *pgd) |
50 | { | 30 | { |
51 | pmd_t *pmd; | 31 | pmd_t *pmd; |
@@ -145,45 +125,6 @@ static int set_up_temporary_mappings(void) | |||
145 | return 0; | 125 | return 0; |
146 | } | 126 | } |
147 | 127 | ||
148 | static int relocate_restore_code(void) | ||
149 | { | ||
150 | pgd_t *pgd; | ||
151 | p4d_t *p4d; | ||
152 | pud_t *pud; | ||
153 | pmd_t *pmd; | ||
154 | pte_t *pte; | ||
155 | |||
156 | relocated_restore_code = get_safe_page(GFP_ATOMIC); | ||
157 | if (!relocated_restore_code) | ||
158 | return -ENOMEM; | ||
159 | |||
160 | memcpy((void *)relocated_restore_code, core_restore_code, PAGE_SIZE); | ||
161 | |||
162 | /* Make the page containing the relocated code executable */ | ||
163 | pgd = (pgd_t *)__va(read_cr3_pa()) + | ||
164 | pgd_index(relocated_restore_code); | ||
165 | p4d = p4d_offset(pgd, relocated_restore_code); | ||
166 | if (p4d_large(*p4d)) { | ||
167 | set_p4d(p4d, __p4d(p4d_val(*p4d) & ~_PAGE_NX)); | ||
168 | goto out; | ||
169 | } | ||
170 | pud = pud_offset(p4d, relocated_restore_code); | ||
171 | if (pud_large(*pud)) { | ||
172 | set_pud(pud, __pud(pud_val(*pud) & ~_PAGE_NX)); | ||
173 | goto out; | ||
174 | } | ||
175 | pmd = pmd_offset(pud, relocated_restore_code); | ||
176 | if (pmd_large(*pmd)) { | ||
177 | set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_NX)); | ||
178 | goto out; | ||
179 | } | ||
180 | pte = pte_offset_kernel(pmd, relocated_restore_code); | ||
181 | set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_NX)); | ||
182 | out: | ||
183 | __flush_tlb_all(); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | asmlinkage int swsusp_arch_resume(void) | 128 | asmlinkage int swsusp_arch_resume(void) |
188 | { | 129 | { |
189 | int error; | 130 | int error; |
@@ -200,165 +141,3 @@ asmlinkage int swsusp_arch_resume(void) | |||
200 | restore_image(); | 141 | restore_image(); |
201 | return 0; | 142 | return 0; |
202 | } | 143 | } |
203 | |||
204 | /* | ||
205 | * pfn_is_nosave - check if given pfn is in the 'nosave' section | ||
206 | */ | ||
207 | |||
208 | int pfn_is_nosave(unsigned long pfn) | ||
209 | { | ||
210 | unsigned long nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT; | ||
211 | unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT; | ||
212 | return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); | ||
213 | } | ||
214 | |||
215 | #define MD5_DIGEST_SIZE 16 | ||
216 | |||
217 | struct restore_data_record { | ||
218 | unsigned long jump_address; | ||
219 | unsigned long jump_address_phys; | ||
220 | unsigned long cr3; | ||
221 | unsigned long magic; | ||
222 | u8 e820_digest[MD5_DIGEST_SIZE]; | ||
223 | }; | ||
224 | |||
225 | #define RESTORE_MAGIC 0x23456789ABCDEF01UL | ||
226 | |||
227 | #if IS_BUILTIN(CONFIG_CRYPTO_MD5) | ||
228 | /** | ||
229 | * get_e820_md5 - calculate md5 according to given e820 table | ||
230 | * | ||
231 | * @table: the e820 table to be calculated | ||
232 | * @buf: the md5 result to be stored to | ||
233 | */ | ||
234 | static int get_e820_md5(struct e820_table *table, void *buf) | ||
235 | { | ||
236 | struct crypto_shash *tfm; | ||
237 | struct shash_desc *desc; | ||
238 | int size; | ||
239 | int ret = 0; | ||
240 | |||
241 | tfm = crypto_alloc_shash("md5", 0, 0); | ||
242 | if (IS_ERR(tfm)) | ||
243 | return -ENOMEM; | ||
244 | |||
245 | desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm), | ||
246 | GFP_KERNEL); | ||
247 | if (!desc) { | ||
248 | ret = -ENOMEM; | ||
249 | goto free_tfm; | ||
250 | } | ||
251 | |||
252 | desc->tfm = tfm; | ||
253 | desc->flags = 0; | ||
254 | |||
255 | size = offsetof(struct e820_table, entries) + | ||
256 | sizeof(struct e820_entry) * table->nr_entries; | ||
257 | |||
258 | if (crypto_shash_digest(desc, (u8 *)table, size, buf)) | ||
259 | ret = -EINVAL; | ||
260 | |||
261 | kzfree(desc); | ||
262 | |||
263 | free_tfm: | ||
264 | crypto_free_shash(tfm); | ||
265 | return ret; | ||
266 | } | ||
267 | |||
268 | static int hibernation_e820_save(void *buf) | ||
269 | { | ||
270 | return get_e820_md5(e820_table_firmware, buf); | ||
271 | } | ||
272 | |||
273 | static bool hibernation_e820_mismatch(void *buf) | ||
274 | { | ||
275 | int ret; | ||
276 | u8 result[MD5_DIGEST_SIZE]; | ||
277 | |||
278 | memset(result, 0, MD5_DIGEST_SIZE); | ||
279 | /* If there is no digest in suspend kernel, let it go. */ | ||
280 | if (!memcmp(result, buf, MD5_DIGEST_SIZE)) | ||
281 | return false; | ||
282 | |||
283 | ret = get_e820_md5(e820_table_firmware, result); | ||
284 | if (ret) | ||
285 | return true; | ||
286 | |||
287 | return memcmp(result, buf, MD5_DIGEST_SIZE) ? true : false; | ||
288 | } | ||
289 | #else | ||
290 | static int hibernation_e820_save(void *buf) | ||
291 | { | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static bool hibernation_e820_mismatch(void *buf) | ||
296 | { | ||
297 | /* If md5 is not builtin for restore kernel, let it go. */ | ||
298 | return false; | ||
299 | } | ||
300 | #endif | ||
301 | |||
302 | /** | ||
303 | * arch_hibernation_header_save - populate the architecture specific part | ||
304 | * of a hibernation image header | ||
305 | * @addr: address to save the data at | ||
306 | */ | ||
307 | int arch_hibernation_header_save(void *addr, unsigned int max_size) | ||
308 | { | ||
309 | struct restore_data_record *rdr = addr; | ||
310 | |||
311 | if (max_size < sizeof(struct restore_data_record)) | ||
312 | return -EOVERFLOW; | ||
313 | rdr->jump_address = (unsigned long)restore_registers; | ||
314 | rdr->jump_address_phys = __pa_symbol(restore_registers); | ||
315 | |||
316 | /* | ||
317 | * The restore code fixes up CR3 and CR4 in the following sequence: | ||
318 | * | ||
319 | * [in hibernation asm] | ||
320 | * 1. CR3 <= temporary page tables | ||
321 | * 2. CR4 <= mmu_cr4_features (from the kernel that restores us) | ||
322 | * 3. CR3 <= rdr->cr3 | ||
323 | * 4. CR4 <= mmu_cr4_features (from us, i.e. the image kernel) | ||
324 | * [in restore_processor_state()] | ||
325 | * 5. CR4 <= saved CR4 | ||
326 | * 6. CR3 <= saved CR3 | ||
327 | * | ||
328 | * Our mmu_cr4_features has CR4.PCIDE=0, and toggling | ||
329 | * CR4.PCIDE while CR3's PCID bits are nonzero is illegal, so | ||
330 | * rdr->cr3 needs to point to valid page tables but must not | ||
331 | * have any of the PCID bits set. | ||
332 | */ | ||
333 | rdr->cr3 = restore_cr3 & ~CR3_PCID_MASK; | ||
334 | |||
335 | rdr->magic = RESTORE_MAGIC; | ||
336 | |||
337 | return hibernation_e820_save(rdr->e820_digest); | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * arch_hibernation_header_restore - read the architecture specific data | ||
342 | * from the hibernation image header | ||
343 | * @addr: address to read the data from | ||
344 | */ | ||
345 | int arch_hibernation_header_restore(void *addr) | ||
346 | { | ||
347 | struct restore_data_record *rdr = addr; | ||
348 | |||
349 | restore_jump_address = rdr->jump_address; | ||
350 | jump_address_phys = rdr->jump_address_phys; | ||
351 | restore_cr3 = rdr->cr3; | ||
352 | |||
353 | if (rdr->magic != RESTORE_MAGIC) { | ||
354 | pr_crit("Unrecognized hibernate image header format!\n"); | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | |||
358 | if (hibernation_e820_mismatch(rdr->e820_digest)) { | ||
359 | pr_crit("Hibernate inconsistent memory map detected!\n"); | ||
360 | return -ENODEV; | ||
361 | } | ||
362 | |||
363 | return 0; | ||
364 | } | ||