Skip to content

Commit 7ee18d6

Browse files
amlutoIngo Molnar
authored andcommitted
x86/power: Make restore_processor_context() sane
My previous attempt to fix a couple of bugs in __restore_processor_context(): 5b06bbc ("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 <[email protected]> Reported-by: Jarkko Nikula <[email protected]> Reported-by: Pavel Machek <[email protected]> Tested-by: Jarkko Nikula <[email protected]> Tested-by: Pavel Machek <[email protected]> Signed-off-by: Andy Lutomirski <[email protected]> Acked-by: Rafael J. Wysocki <[email protected]> Acked-by: Thomas Gleixner <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Cc: Zhang Rui <[email protected]> Fixes: 5b06bbc ("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 <[email protected]>
1 parent 896c80b commit 7ee18d6

File tree

3 files changed

+62
-41
lines changed

3 files changed

+62
-41
lines changed

arch/x86/include/asm/suspend_32.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212

1313
/* image of the saved processor state */
1414
struct saved_context {
15-
u16 es, fs, gs, ss;
15+
/*
16+
* On x86_32, all segment registers, with the possible exception of
17+
* gs, are saved at kernel entry in pt_regs.
18+
*/
19+
#ifdef CONFIG_X86_32_LAZY_GS
20+
u16 gs;
21+
#endif
1622
unsigned long cr0, cr2, cr3, cr4;
1723
u64 misc_enable;
1824
bool misc_enable_saved;

arch/x86/include/asm/suspend_64.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,20 @@
2020
*/
2121
struct saved_context {
2222
struct pt_regs regs;
23-
u16 ds, es, fs, gs, ss;
24-
unsigned long gs_base, gs_kernel_base, fs_base;
23+
24+
/*
25+
* User CS and SS are saved in current_pt_regs(). The rest of the
26+
* segment selectors need to be saved and restored here.
27+
*/
28+
u16 ds, es, fs, gs;
29+
30+
/*
31+
* Usermode FSBASE and GSBASE may not match the fs and gs selectors,
32+
* so we save them separately. We save the kernelmode GSBASE to
33+
* restore percpu access after resume.
34+
*/
35+
unsigned long kernelmode_gs_base, usermode_gs_base, fs_base;
36+
2537
unsigned long cr0, cr2, cr3, cr4, cr8;
2638
u64 misc_enable;
2739
bool misc_enable_saved;

arch/x86/power/cpu.c

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,18 @@ static void __save_processor_state(struct saved_context *ctxt)
9999
/*
100100
* segment registers
101101
*/
102-
#ifdef CONFIG_X86_32
103-
savesegment(es, ctxt->es);
104-
savesegment(fs, ctxt->fs);
102+
#ifdef CONFIG_X86_32_LAZY_GS
105103
savesegment(gs, ctxt->gs);
106-
savesegment(ss, ctxt->ss);
107-
#else
108-
/* CONFIG_X86_64 */
109-
asm volatile ("movw %%ds, %0" : "=m" (ctxt->ds));
110-
asm volatile ("movw %%es, %0" : "=m" (ctxt->es));
111-
asm volatile ("movw %%fs, %0" : "=m" (ctxt->fs));
112-
asm volatile ("movw %%gs, %0" : "=m" (ctxt->gs));
113-
asm volatile ("movw %%ss, %0" : "=m" (ctxt->ss));
104+
#endif
105+
#ifdef CONFIG_X86_64
106+
savesegment(gs, ctxt->gs);
107+
savesegment(fs, ctxt->fs);
108+
savesegment(ds, ctxt->ds);
109+
savesegment(es, ctxt->es);
114110

115111
rdmsrl(MSR_FS_BASE, ctxt->fs_base);
116-
rdmsrl(MSR_GS_BASE, ctxt->gs_base);
117-
rdmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
112+
rdmsrl(MSR_GS_BASE, ctxt->kernelmode_gs_base);
113+
rdmsrl(MSR_KERNEL_GS_BASE, ctxt->usermode_gs_base);
118114
mtrr_save_fixed_ranges(NULL);
119115

120116
rdmsrl(MSR_EFER, ctxt->efer);
@@ -189,9 +185,12 @@ static void fix_processor_context(void)
189185
}
190186

191187
/**
192-
* __restore_processor_state - restore the contents of CPU registers saved
193-
* by __save_processor_state()
194-
* @ctxt - structure to load the registers contents from
188+
* __restore_processor_state - restore the contents of CPU registers saved
189+
* by __save_processor_state()
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.
195194
*/
196195
static void notrace __restore_processor_state(struct saved_context *ctxt)
197196
{
@@ -214,46 +213,50 @@ static void notrace __restore_processor_state(struct saved_context *ctxt)
214213
write_cr2(ctxt->cr2);
215214
write_cr0(ctxt->cr0);
216215

216+
/* Restore the IDT. */
217+
load_idt(&ctxt->idt);
218+
217219
/*
218-
* now restore the descriptor tables to their proper values
219-
* ltr is done i fix_processor_context().
220+
* Just in case the asm code got us here with the SS, DS, or ES
221+
* out of sync with the GDT, update them.
220222
*/
221-
load_idt(&ctxt->idt);
223+
loadsegment(ss, __KERNEL_DS);
224+
loadsegment(ds, __USER_DS);
225+
loadsegment(es, __USER_DS);
222226

223-
#ifdef CONFIG_X86_64
224227
/*
225-
* We need GSBASE restored before percpu access can work.
226-
* percpu access can happen in exception handlers or in complicated
227-
* helpers like load_gs_index().
228+
* Restore percpu access. Percpu access can happen in exception
229+
* handlers or in complicated helpers like load_gs_index().
228230
*/
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);
230236
#endif
231237

238+
/* Restore the TSS, RO GDT, LDT, and usermode-relevant MSRs. */
232239
fix_processor_context();
233240

234241
/*
235-
* Restore segment registers. This happens after restoring the GDT
236-
* and LDT, which happen in fix_processor_context().
242+
* Now that we have descriptor tables fully restored and working
243+
* exception handling, restore the usermode segments.
237244
*/
238-
#ifdef CONFIG_X86_32
245+
#ifdef CONFIG_X86_64
246+
loadsegment(ds, ctxt->es);
239247
loadsegment(es, ctxt->es);
240248
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));
248249
load_gs_index(ctxt->gs);
249-
asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss));
250250

251251
/*
252-
* Restore FSBASE and user GSBASE after reloading the respective
253-
* segment selectors.
252+
* Restore FSBASE and GSBASE after restoring the selectors, since
253+
* restoring the selectors clobbers the bases. Keep in mind
254+
* that MSR_KERNEL_GS_BASE is horribly misnamed.
254255
*/
255256
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);
257260
#endif
258261

259262
do_fpu_end();

0 commit comments

Comments
 (0)