summaryrefslogtreecommitdiffstats
path: root/arch/x86/power
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2017-12-14 16:19:07 -0500
committerIngo Molnar <mingo@kernel.org>2017-12-15 06:21:38 -0500
commit7ee18d677989e99635027cee04c878950e0752b9 (patch)
tree4c99c0e08f55646b33583689ad7b762bccaf5e2e /arch/x86/power
parent896c80bef4d3b357814a476663158aaf669d0fb3 (diff)
x86/power: Make restore_processor_context() sane
My previous attempt to fix a couple of bugs in __restore_processor_context(): 5b06bbcfc2c6 ("x86/power: Fix some ordering bugs in __restore_processor_context()") ... introduced yet another bug, breaking suspend-resume. Rather than trying to come up with a minimal fix, let's try to clean it up for real. This patch fixes quite a few things: - The old code saved a nonsensical subset of segment registers. The only registers that need to be saved are those that contain userspace state or those that can't be trivially restored without percpu access working. (On x86_32, we can restore percpu access by writing __KERNEL_PERCPU to %fs. On x86_64, it's easier to save and restore the kernel's GSBASE.) With this patch, we restore hardcoded values to the kernel state where applicable and explicitly restore the user state after fixing all the descriptor tables. - We used to use an unholy mix of inline asm and C helpers for segment register access. Let's get rid of the inline asm. This fixes the reported s2ram hangs and make the code all around more logical. Analyzed-by: Linus Torvalds <torvalds@linux-foundation.org> Reported-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Reported-by: Pavel Machek <pavel@ucw.cz> Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Tested-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Andy Lutomirski <luto@kernel.org> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Thomas Gleixner <tglx@linutronix.de> Cc: Borislav Petkov <bpetkov@suse.de> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rafael J. Wysocki <rjw@rjwysocki.net> Cc: Zhang Rui <rui.zhang@intel.com> Fixes: 5b06bbcfc2c6 ("x86/power: Fix some ordering bugs in __restore_processor_context()") Link: http://lkml.kernel.org/r/398ee68e5c0f766425a7b746becfc810840770ff.1513286253.git.luto@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/power')
-rw-r--r--arch/x86/power/cpu.c79
1 files changed, 41 insertions, 38 deletions
diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c
index 033c61e6891b..36a28eddb435 100644
--- a/arch/x86/power/cpu.c
+++ b/arch/x86/power/cpu.c
@@ -99,22 +99,18 @@ static void __save_processor_state(struct saved_context *ctxt)
99 /* 99 /*
100 * segment registers 100 * segment registers
101 */ 101 */
102#ifdef CONFIG_X86_32 102#ifdef CONFIG_X86_32_LAZY_GS
103 savesegment(es, ctxt->es);
104 savesegment(fs, ctxt->fs);
105 savesegment(gs, ctxt->gs); 103 savesegment(gs, ctxt->gs);
106 savesegment(ss, ctxt->ss); 104#endif
107#else 105#ifdef CONFIG_X86_64
108/* CONFIG_X86_64 */ 106 savesegment(gs, ctxt->gs);
109 asm volatile ("movw %%ds, %0" : "=m" (ctxt->ds)); 107 savesegment(fs, ctxt->fs);
110 asm volatile ("movw %%es, %0" : "=m" (ctxt->es)); 108 savesegment(ds, ctxt->ds);
111 asm volatile ("movw %%fs, %0" : "=m" (ctxt->fs)); 109 savesegment(es, ctxt->es);
112 asm volatile ("movw %%gs, %0" : "=m" (ctxt->gs));
113 asm volatile ("movw %%ss, %0" : "=m" (ctxt->ss));
114 110
115 rdmsrl(MSR_FS_BASE, ctxt->fs_base); 111 rdmsrl(MSR_FS_BASE, ctxt->fs_base);
116 rdmsrl(MSR_GS_BASE, ctxt->gs_base); 112 rdmsrl(MSR_GS_BASE, ctxt->kernelmode_gs_base);
117 rdmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base); 113 rdmsrl(MSR_KERNEL_GS_BASE, ctxt->usermode_gs_base);
118 mtrr_save_fixed_ranges(NULL); 114 mtrr_save_fixed_ranges(NULL);
119 115
120 rdmsrl(MSR_EFER, ctxt->efer); 116 rdmsrl(MSR_EFER, ctxt->efer);
@@ -189,9 +185,12 @@ static void fix_processor_context(void)
189} 185}
190 186
191/** 187/**
192 * __restore_processor_state - restore the contents of CPU registers saved 188 * __restore_processor_state - restore the contents of CPU registers saved
193 * by __save_processor_state() 189 * by __save_processor_state()
194 * @ctxt - structure to load the registers contents from 190 * @ctxt - structure to load the registers contents from
191 *
192 * The asm code that gets us here will have restored a usable GDT, although
193 * it will be pointing to the wrong alias.
195 */ 194 */
196static void notrace __restore_processor_state(struct saved_context *ctxt) 195static void notrace __restore_processor_state(struct saved_context *ctxt)
197{ 196{
@@ -214,46 +213,50 @@ static void notrace __restore_processor_state(struct saved_context *ctxt)
214 write_cr2(ctxt->cr2); 213 write_cr2(ctxt->cr2);
215 write_cr0(ctxt->cr0); 214 write_cr0(ctxt->cr0);
216 215
216 /* Restore the IDT. */
217 load_idt(&ctxt->idt);
218
217 /* 219 /*
218 * now restore the descriptor tables to their proper values 220 * Just in case the asm code got us here with the SS, DS, or ES
219 * ltr is done i fix_processor_context(). 221 * out of sync with the GDT, update them.
220 */ 222 */
221 load_idt(&ctxt->idt); 223 loadsegment(ss, __KERNEL_DS);
224 loadsegment(ds, __USER_DS);
225 loadsegment(es, __USER_DS);
222 226
223#ifdef CONFIG_X86_64
224 /* 227 /*
225 * We need GSBASE restored before percpu access can work. 228 * Restore percpu access. Percpu access can happen in exception
226 * percpu access can happen in exception handlers or in complicated 229 * handlers or in complicated helpers like load_gs_index().
227 * helpers like load_gs_index().
228 */ 230 */
229 wrmsrl(MSR_GS_BASE, ctxt->gs_base); 231#ifdef CONFIG_X86_64
232 wrmsrl(MSR_GS_BASE, ctxt->kernelmode_gs_base);
233#else
234 loadsegment(fs, __KERNEL_PERCPU);
235 loadsegment(gs, __KERNEL_STACK_CANARY);
230#endif 236#endif
231 237
238 /* Restore the TSS, RO GDT, LDT, and usermode-relevant MSRs. */
232 fix_processor_context(); 239 fix_processor_context();
233 240
234 /* 241 /*
235 * Restore segment registers. This happens after restoring the GDT 242 * Now that we have descriptor tables fully restored and working
236 * and LDT, which happen in fix_processor_context(). 243 * exception handling, restore the usermode segments.
237 */ 244 */
238#ifdef CONFIG_X86_32 245#ifdef CONFIG_X86_64
246 loadsegment(ds, ctxt->es);
239 loadsegment(es, ctxt->es); 247 loadsegment(es, ctxt->es);
240 loadsegment(fs, ctxt->fs); 248 loadsegment(fs, ctxt->fs);
241 loadsegment(gs, ctxt->gs);
242 loadsegment(ss, ctxt->ss);
243#else
244/* CONFIG_X86_64 */
245 asm volatile ("movw %0, %%ds" :: "r" (ctxt->ds));
246 asm volatile ("movw %0, %%es" :: "r" (ctxt->es));
247 asm volatile ("movw %0, %%fs" :: "r" (ctxt->fs));
248 load_gs_index(ctxt->gs); 249 load_gs_index(ctxt->gs);
249 asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss));
250 250
251 /* 251 /*
252 * Restore FSBASE and user GSBASE after reloading the respective 252 * Restore FSBASE and GSBASE after restoring the selectors, since
253 * segment selectors. 253 * restoring the selectors clobbers the bases. Keep in mind
254 * that MSR_KERNEL_GS_BASE is horribly misnamed.
254 */ 255 */
255 wrmsrl(MSR_FS_BASE, ctxt->fs_base); 256 wrmsrl(MSR_FS_BASE, ctxt->fs_base);
256 wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base); 257 wrmsrl(MSR_KERNEL_GS_BASE, ctxt->usermode_gs_base);
258#elif defined(CONFIG_X86_32_LAZY_GS)
259 loadsegment(gs, ctxt->gs);
257#endif 260#endif
258 261
259 do_fpu_end(); 262 do_fpu_end();