diff options
-rw-r--r-- | arch/x86/kernel/suspend_64.c | 54 | ||||
-rw-r--r-- | arch/x86/kernel/suspend_asm_64.S | 41 | ||||
-rw-r--r-- | arch/x86_64/Kconfig | 5 | ||||
-rw-r--r-- | include/asm-x86/suspend_64.h | 2 |
4 files changed, 94 insertions, 8 deletions
diff --git a/arch/x86/kernel/suspend_64.c b/arch/x86/kernel/suspend_64.c index 573c0a6e0ac6..01fbfb018ca9 100644 --- a/arch/x86/kernel/suspend_64.c +++ b/arch/x86/kernel/suspend_64.c | |||
@@ -150,8 +150,16 @@ void fix_processor_context(void) | |||
150 | /* Defined in arch/x86_64/kernel/suspend_asm.S */ | 150 | /* Defined in arch/x86_64/kernel/suspend_asm.S */ |
151 | extern int restore_image(void); | 151 | extern int restore_image(void); |
152 | 152 | ||
153 | /* | ||
154 | * Address to jump to in the last phase of restore in order to get to the image | ||
155 | * kernel's text (this value is passed in the image header). | ||
156 | */ | ||
157 | unsigned long restore_jump_address; | ||
158 | |||
153 | pgd_t *temp_level4_pgt; | 159 | pgd_t *temp_level4_pgt; |
154 | 160 | ||
161 | void *relocated_restore_code; | ||
162 | |||
155 | static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end) | 163 | static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end) |
156 | { | 164 | { |
157 | long i, j; | 165 | long i, j; |
@@ -175,7 +183,7 @@ static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long en | |||
175 | 183 | ||
176 | if (paddr >= end) | 184 | if (paddr >= end) |
177 | break; | 185 | break; |
178 | pe = _PAGE_NX | _PAGE_PSE | _KERNPG_TABLE | paddr; | 186 | pe = __PAGE_KERNEL_LARGE_EXEC | paddr; |
179 | pe &= __supported_pte_mask; | 187 | pe &= __supported_pte_mask; |
180 | set_pmd(pmd, __pmd(pe)); | 188 | set_pmd(pmd, __pmd(pe)); |
181 | } | 189 | } |
@@ -222,6 +230,13 @@ int swsusp_arch_resume(void) | |||
222 | /* We have got enough memory and from now on we cannot recover */ | 230 | /* We have got enough memory and from now on we cannot recover */ |
223 | if ((error = set_up_temporary_mappings())) | 231 | if ((error = set_up_temporary_mappings())) |
224 | return error; | 232 | return error; |
233 | |||
234 | relocated_restore_code = (void *)get_safe_page(GFP_ATOMIC); | ||
235 | if (!relocated_restore_code) | ||
236 | return -ENOMEM; | ||
237 | memcpy(relocated_restore_code, &core_restore_code, | ||
238 | &restore_registers - &core_restore_code); | ||
239 | |||
225 | restore_image(); | 240 | restore_image(); |
226 | return 0; | 241 | return 0; |
227 | } | 242 | } |
@@ -236,4 +251,41 @@ int pfn_is_nosave(unsigned long pfn) | |||
236 | unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT; | 251 | unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT; |
237 | return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); | 252 | return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); |
238 | } | 253 | } |
254 | |||
255 | struct restore_data_record { | ||
256 | unsigned long jump_address; | ||
257 | unsigned long control; | ||
258 | }; | ||
259 | |||
260 | #define RESTORE_MAGIC 0x0123456789ABCDEFUL | ||
261 | |||
262 | /** | ||
263 | * arch_hibernation_header_save - populate the architecture specific part | ||
264 | * of a hibernation image header | ||
265 | * @addr: address to save the data at | ||
266 | */ | ||
267 | int arch_hibernation_header_save(void *addr, unsigned int max_size) | ||
268 | { | ||
269 | struct restore_data_record *rdr = addr; | ||
270 | |||
271 | if (max_size < sizeof(struct restore_data_record)) | ||
272 | return -EOVERFLOW; | ||
273 | rdr->jump_address = restore_jump_address; | ||
274 | rdr->control = (restore_jump_address ^ RESTORE_MAGIC); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * arch_hibernation_header_restore - read the architecture specific data | ||
280 | * from the hibernation image header | ||
281 | * @addr: address to read the data from | ||
282 | */ | ||
283 | int arch_hibernation_header_restore(void *addr) | ||
284 | { | ||
285 | struct restore_data_record *rdr = addr; | ||
286 | |||
287 | restore_jump_address = rdr->jump_address; | ||
288 | return (rdr->control == (restore_jump_address ^ RESTORE_MAGIC)) ? | ||
289 | 0 : -EINVAL; | ||
290 | } | ||
239 | #endif /* CONFIG_HIBERNATION */ | 291 | #endif /* CONFIG_HIBERNATION */ |
diff --git a/arch/x86/kernel/suspend_asm_64.S b/arch/x86/kernel/suspend_asm_64.S index 16d183f67bc1..40a209e0525c 100644 --- a/arch/x86/kernel/suspend_asm_64.S +++ b/arch/x86/kernel/suspend_asm_64.S | |||
@@ -2,8 +2,8 @@ | |||
2 | * | 2 | * |
3 | * Distribute under GPLv2. | 3 | * Distribute under GPLv2. |
4 | * | 4 | * |
5 | * swsusp_arch_resume may not use any stack, nor any variable that is | 5 | * swsusp_arch_resume must not use any stack or any nonlocal variables while |
6 | * not "NoSave" during copying pages: | 6 | * copying pages: |
7 | * | 7 | * |
8 | * Its rewriting one kernel image with another. What is stack in "old" | 8 | * Its rewriting one kernel image with another. What is stack in "old" |
9 | * image could very well be data page in "new" image, and overwriting | 9 | * image could very well be data page in "new" image, and overwriting |
@@ -36,6 +36,10 @@ ENTRY(swsusp_arch_suspend) | |||
36 | movq %r15, saved_context_r15(%rip) | 36 | movq %r15, saved_context_r15(%rip) |
37 | pushfq ; popq saved_context_eflags(%rip) | 37 | pushfq ; popq saved_context_eflags(%rip) |
38 | 38 | ||
39 | /* save the address of restore_registers */ | ||
40 | movq $restore_registers, %rax | ||
41 | movq %rax, restore_jump_address(%rip) | ||
42 | |||
39 | call swsusp_save | 43 | call swsusp_save |
40 | ret | 44 | ret |
41 | 45 | ||
@@ -54,7 +58,16 @@ ENTRY(restore_image) | |||
54 | movq %rcx, %cr3; | 58 | movq %rcx, %cr3; |
55 | movq %rax, %cr4; # turn PGE back on | 59 | movq %rax, %cr4; # turn PGE back on |
56 | 60 | ||
61 | /* prepare to jump to the image kernel */ | ||
62 | movq restore_jump_address(%rip), %rax | ||
63 | |||
64 | /* prepare to copy image data to their original locations */ | ||
57 | movq restore_pblist(%rip), %rdx | 65 | movq restore_pblist(%rip), %rdx |
66 | movq relocated_restore_code(%rip), %rcx | ||
67 | jmpq *%rcx | ||
68 | |||
69 | /* code below has been relocated to a safe page */ | ||
70 | ENTRY(core_restore_code) | ||
58 | loop: | 71 | loop: |
59 | testq %rdx, %rdx | 72 | testq %rdx, %rdx |
60 | jz done | 73 | jz done |
@@ -62,7 +75,7 @@ loop: | |||
62 | /* get addresses from the pbe and copy the page */ | 75 | /* get addresses from the pbe and copy the page */ |
63 | movq pbe_address(%rdx), %rsi | 76 | movq pbe_address(%rdx), %rsi |
64 | movq pbe_orig_address(%rdx), %rdi | 77 | movq pbe_orig_address(%rdx), %rdi |
65 | movq $512, %rcx | 78 | movq $(PAGE_SIZE >> 3), %rcx |
66 | rep | 79 | rep |
67 | movsq | 80 | movsq |
68 | 81 | ||
@@ -70,6 +83,20 @@ loop: | |||
70 | movq pbe_next(%rdx), %rdx | 83 | movq pbe_next(%rdx), %rdx |
71 | jmp loop | 84 | jmp loop |
72 | done: | 85 | done: |
86 | /* jump to the restore_registers address from the image header */ | ||
87 | jmpq *%rax | ||
88 | /* | ||
89 | * NOTE: This assumes that the boot kernel's text mapping covers the | ||
90 | * image kernel's page containing restore_registers and the address of | ||
91 | * this page is the same as in the image kernel's text mapping (it | ||
92 | * should always be true, because the text mapping is linear, starting | ||
93 | * from 0, and is supposed to cover the entire kernel text for every | ||
94 | * kernel). | ||
95 | * | ||
96 | * code below belongs to the image kernel | ||
97 | */ | ||
98 | |||
99 | ENTRY(restore_registers) | ||
73 | /* go back to the original page tables */ | 100 | /* go back to the original page tables */ |
74 | movq $(init_level4_pgt - __START_KERNEL_map), %rax | 101 | movq $(init_level4_pgt - __START_KERNEL_map), %rax |
75 | addq phys_base(%rip), %rax | 102 | addq phys_base(%rip), %rax |
@@ -84,12 +111,9 @@ done: | |||
84 | movq %rcx, %cr3 | 111 | movq %rcx, %cr3 |
85 | movq %rax, %cr4; # turn PGE back on | 112 | movq %rax, %cr4; # turn PGE back on |
86 | 113 | ||
87 | movl $24, %eax | ||
88 | movl %eax, %ds | ||
89 | |||
90 | movq saved_context_esp(%rip), %rsp | 114 | movq saved_context_esp(%rip), %rsp |
91 | movq saved_context_ebp(%rip), %rbp | 115 | movq saved_context_ebp(%rip), %rbp |
92 | /* Don't restore %rax, it must be 0 anyway */ | 116 | /* restore GPRs (we don't restore %rax, it must be 0 anyway) */ |
93 | movq saved_context_ebx(%rip), %rbx | 117 | movq saved_context_ebx(%rip), %rbx |
94 | movq saved_context_ecx(%rip), %rcx | 118 | movq saved_context_ecx(%rip), %rcx |
95 | movq saved_context_edx(%rip), %rdx | 119 | movq saved_context_edx(%rip), %rdx |
@@ -107,4 +131,7 @@ done: | |||
107 | 131 | ||
108 | xorq %rax, %rax | 132 | xorq %rax, %rax |
109 | 133 | ||
134 | /* tell the hibernation core that we've just restored the memory */ | ||
135 | movq %rax, in_suspend(%rip) | ||
136 | |||
110 | ret | 137 | ret |
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 43fafe9e9c08..78cb68f2ebbd 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig | |||
@@ -716,6 +716,11 @@ menu "Power management options" | |||
716 | 716 | ||
717 | source kernel/power/Kconfig | 717 | source kernel/power/Kconfig |
718 | 718 | ||
719 | config ARCH_HIBERNATION_HEADER | ||
720 | bool | ||
721 | depends on HIBERNATION | ||
722 | default y | ||
723 | |||
719 | source "drivers/acpi/Kconfig" | 724 | source "drivers/acpi/Kconfig" |
720 | 725 | ||
721 | source "arch/x86/kernel/cpufreq/Kconfig" | 726 | source "arch/x86/kernel/cpufreq/Kconfig" |
diff --git a/include/asm-x86/suspend_64.h b/include/asm-x86/suspend_64.h index b897e8cb55fb..9440a7a1b99a 100644 --- a/include/asm-x86/suspend_64.h +++ b/include/asm-x86/suspend_64.h | |||
@@ -53,3 +53,5 @@ extern unsigned long saved_rdi; | |||
53 | 53 | ||
54 | /* routines for saving/restoring kernel state */ | 54 | /* routines for saving/restoring kernel state */ |
55 | extern int acpi_save_state_mem(void); | 55 | extern int acpi_save_state_mem(void); |
56 | extern char core_restore_code; | ||
57 | extern char restore_registers; | ||