diff options
author | Kees Cook <kees.cook@canonical.com> | 2011-07-06 21:10:34 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2011-07-06 23:09:34 -0400 |
commit | 7a3136666bc0f0419f7aaa7b1fabb4b0e0a7fb76 (patch) | |
tree | 9d9e9f1de8ab1306b8b9d26aadb4a7f401323e69 /arch/x86 | |
parent | b49c78d4827be8d7e67e5b94adac6b30a4a9ad14 (diff) |
x86, suspend: Restore MISC_ENABLE MSR in realmode wakeup
Some BIOSes will reset the Intel MISC_ENABLE MSR (specifically the
XD_DISABLE bit) when resuming from S3, which can interact poorly with
ebba638ae723d8a8fc2f7abce5ec18b688b791d7. In 32bit PAE mode, this can
lead to a fault when EFER is restored by the kernel wakeup routines,
due to it setting the NX bit for a CPU that (thanks to the BIOS reset)
now incorrectly thinks it lacks the NX feature. (64bit is not affected
because it uses a common CPU bring-up that specifically handles the
XD_DISABLE bit.)
The need for MISC_ENABLE being restored so early is specific to the S3
resume path. Normally, MISC_ENABLE is saved in save_processor_state(),
but this happens after the resume header is created, so just reproduce
the logic here. (acpi_suspend_lowlevel() creates the header, calls
do_suspend_lowlevel, which calls save_processor_state(), so the saved
processor context isn't available during resume header creation.)
[ hpa: Consider for stable if OK in mainline ]
Signed-off-by: Kees Cook <kees.cook@canonical.com>
Link: http://lkml.kernel.org/r/20110707011034.GA8523@outflux.net
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: <stable@kernel.org> 2.6.38+
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.S | 14 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.h | 6 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/sleep.c | 6 |
3 files changed, 26 insertions, 0 deletions
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.S b/arch/x86/kernel/acpi/realmode/wakeup.S index ead21b663117..b4fd836e4053 100644 --- a/arch/x86/kernel/acpi/realmode/wakeup.S +++ b/arch/x86/kernel/acpi/realmode/wakeup.S | |||
@@ -28,6 +28,8 @@ pmode_cr3: .long 0 /* Saved %cr3 */ | |||
28 | pmode_cr4: .long 0 /* Saved %cr4 */ | 28 | pmode_cr4: .long 0 /* Saved %cr4 */ |
29 | pmode_efer: .quad 0 /* Saved EFER */ | 29 | pmode_efer: .quad 0 /* Saved EFER */ |
30 | pmode_gdt: .quad 0 | 30 | pmode_gdt: .quad 0 |
31 | pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */ | ||
32 | pmode_behavior: .long 0 /* Wakeup behavior flags */ | ||
31 | realmode_flags: .long 0 | 33 | realmode_flags: .long 0 |
32 | real_magic: .long 0 | 34 | real_magic: .long 0 |
33 | trampoline_segment: .word 0 | 35 | trampoline_segment: .word 0 |
@@ -91,6 +93,18 @@ wakeup_code: | |||
91 | /* Call the C code */ | 93 | /* Call the C code */ |
92 | calll main | 94 | calll main |
93 | 95 | ||
96 | /* Restore MISC_ENABLE before entering protected mode, in case | ||
97 | BIOS decided to clear XD_DISABLE during S3. */ | ||
98 | movl pmode_behavior, %eax | ||
99 | btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax | ||
100 | jnc 1f | ||
101 | |||
102 | movl pmode_misc_en, %eax | ||
103 | movl pmode_misc_en + 4, %edx | ||
104 | movl $MSR_IA32_MISC_ENABLE, %ecx | ||
105 | wrmsr | ||
106 | 1: | ||
107 | |||
94 | /* Do any other stuff... */ | 108 | /* Do any other stuff... */ |
95 | 109 | ||
96 | #ifndef CONFIG_64BIT | 110 | #ifndef CONFIG_64BIT |
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.h b/arch/x86/kernel/acpi/realmode/wakeup.h index e1828c07e79c..97a29e1430e3 100644 --- a/arch/x86/kernel/acpi/realmode/wakeup.h +++ b/arch/x86/kernel/acpi/realmode/wakeup.h | |||
@@ -21,6 +21,9 @@ struct wakeup_header { | |||
21 | u32 pmode_efer_low; /* Protected mode EFER */ | 21 | u32 pmode_efer_low; /* Protected mode EFER */ |
22 | u32 pmode_efer_high; | 22 | u32 pmode_efer_high; |
23 | u64 pmode_gdt; | 23 | u64 pmode_gdt; |
24 | u32 pmode_misc_en_low; /* Protected mode MISC_ENABLE */ | ||
25 | u32 pmode_misc_en_high; | ||
26 | u32 pmode_behavior; /* Wakeup routine behavior flags */ | ||
24 | u32 realmode_flags; | 27 | u32 realmode_flags; |
25 | u32 real_magic; | 28 | u32 real_magic; |
26 | u16 trampoline_segment; /* segment with trampoline code, 64-bit only */ | 29 | u16 trampoline_segment; /* segment with trampoline code, 64-bit only */ |
@@ -39,4 +42,7 @@ extern struct wakeup_header wakeup_header; | |||
39 | #define WAKEUP_HEADER_SIGNATURE 0x51ee1111 | 42 | #define WAKEUP_HEADER_SIGNATURE 0x51ee1111 |
40 | #define WAKEUP_END_SIGNATURE 0x65a22c82 | 43 | #define WAKEUP_END_SIGNATURE 0x65a22c82 |
41 | 44 | ||
45 | /* Wakeup behavior bits */ | ||
46 | #define WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE 0 | ||
47 | |||
42 | #endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */ | 48 | #endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */ |
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index 18a857ba7a25..103b6ab368d3 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c | |||
@@ -77,6 +77,12 @@ int acpi_suspend_lowlevel(void) | |||
77 | 77 | ||
78 | header->pmode_cr0 = read_cr0(); | 78 | header->pmode_cr0 = read_cr0(); |
79 | header->pmode_cr4 = read_cr4_safe(); | 79 | header->pmode_cr4 = read_cr4_safe(); |
80 | header->pmode_behavior = 0; | ||
81 | if (!rdmsr_safe(MSR_IA32_MISC_ENABLE, | ||
82 | &header->pmode_misc_en_low, | ||
83 | &header->pmode_misc_en_high)) | ||
84 | header->pmode_behavior |= | ||
85 | (1 << WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE); | ||
80 | header->realmode_flags = acpi_realmode_flags; | 86 | header->realmode_flags = acpi_realmode_flags; |
81 | header->real_magic = 0x12345678; | 87 | header->real_magic = 0x12345678; |
82 | 88 | ||