| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * sleep.c - x86-specific ACPI sleep support. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2001-2003 Patrick Mochel | 
					
						
							| 
									
										
										
										
											2010-07-18 14:27:13 +02:00
										 |  |  |  *  Copyright (C) 2001-2003 Pavel Machek <pavel@ucw.cz> | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/acpi.h>
 | 
					
						
							|  |  |  | #include <linux/bootmem.h>
 | 
					
						
							| 
									
										
										
										
											2010-08-25 13:39:17 -07:00
										 |  |  | #include <linux/memblock.h>
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | #include <linux/dmi.h>
 | 
					
						
							|  |  |  | #include <linux/cpumask.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-17 11:29:24 -07:00
										 |  |  | #include <asm/segment.h>
 | 
					
						
							| 
									
										
										
										
											2008-10-17 01:26:27 +02:00
										 |  |  | #include <asm/desc.h>
 | 
					
						
							| 
									
										
										
										
											2010-08-28 15:58:33 +02:00
										 |  |  | #include <asm/pgtable.h>
 | 
					
						
							| 
									
										
										
										
											2011-02-06 21:16:09 -08:00
										 |  |  | #include <asm/cacheflush.h>
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | #include <asm/realmode.h>
 | 
					
						
							| 
									
										
										
										
											2010-08-28 15:58:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:42 +03:00
										 |  |  | #include "../../realmode/rm/wakeup.h"
 | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #include "sleep.h"
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | unsigned long acpi_realmode_flags; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-03 19:25:48 +02:00
										 |  |  | #if defined(CONFIG_SMP) && defined(CONFIG_64BIT)
 | 
					
						
							| 
									
										
										
										
											2008-10-09 11:56:21 -05:00
										 |  |  | static char temp_stack[4096]; | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-31 09:31:18 +08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * x86_acpi_enter_sleep_state - enter sleep state | 
					
						
							|  |  |  |  * @state: Sleep state to enter. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Wrapper around acpi_enter_sleep_state() to be called by assmebly. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-05-02 00:44:37 +02:00
										 |  |  | acpi_status asmlinkage __visible x86_acpi_enter_sleep_state(u8 state) | 
					
						
							| 
									
										
										
										
											2013-10-31 09:31:18 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	return acpi_enter_sleep_state(state); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2013-05-14 17:09:16 +00:00
										 |  |  |  * x86_acpi_suspend_lowlevel - save kernel state | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Create an identity mapped page table and copy the wakeup routine to | 
					
						
							|  |  |  |  * low memory. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-05-14 17:09:16 +00:00
										 |  |  | int x86_acpi_suspend_lowlevel(void) | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | 	struct wakeup_header *header = | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:41 +03:00
										 |  |  | 		(struct wakeup_header *) __va(real_mode_header->wakeup_header); | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-14 15:42:46 -08:00
										 |  |  | 	if (header->signature != WAKEUP_HEADER_SIGNATURE) { | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 		printk(KERN_ERR "wakeup header does not match\n"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	header->video_mode = saved_video_mode; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	header->pmode_behavior = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #ifndef CONFIG_64BIT
 | 
					
						
							| 
									
										
										
										
											2013-04-05 16:42:23 -04:00
										 |  |  | 	native_store_gdt((struct desc_ptr *)&header->pmode_gdt); | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-12 16:48:12 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We have to check that we can write back the value, and not | 
					
						
							|  |  |  | 	 * just read it.  At least on 90 nm Pentium M (Family 6, Model | 
					
						
							|  |  |  | 	 * 13), reading an invalid MSR is not guaranteed to trap, see | 
					
						
							|  |  |  | 	 * Erratum X4 in "Intel Pentium M Processor on 90 nm Process | 
					
						
							|  |  |  | 	 * with 2-MB L2 Cache and Intel® Processor A100 and A110 on 90 | 
					
						
							|  |  |  | 	 * nm process with 512-KB L2 Cache Specification Update". | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	if (!rdmsr_safe(MSR_EFER, | 
					
						
							|  |  |  | 			&header->pmode_efer_low, | 
					
						
							| 
									
										
										
										
											2013-07-12 16:48:12 -07:00
										 |  |  | 			&header->pmode_efer_high) && | 
					
						
							|  |  |  | 	    !wrmsr_safe(MSR_EFER, | 
					
						
							|  |  |  | 			header->pmode_efer_low, | 
					
						
							|  |  |  | 			header->pmode_efer_high)) | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 		header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_EFER); | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #endif /* !CONFIG_64BIT */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	header->pmode_cr0 = read_cr0(); | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	if (__this_cpu_read(cpu_info.cpuid_level) >= 0) { | 
					
						
							|  |  |  | 		header->pmode_cr4 = read_cr4(); | 
					
						
							|  |  |  | 		header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_CR4); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
											  
											
												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+
											
										 
											2011-07-06 18:10:34 -07:00
										 |  |  | 	if (!rdmsr_safe(MSR_IA32_MISC_ENABLE, | 
					
						
							|  |  |  | 			&header->pmode_misc_en_low, | 
					
						
							| 
									
										
										
										
											2013-07-12 16:48:12 -07:00
										 |  |  | 			&header->pmode_misc_en_high) && | 
					
						
							|  |  |  | 	    !wrmsr_safe(MSR_IA32_MISC_ENABLE, | 
					
						
							|  |  |  | 			header->pmode_misc_en_low, | 
					
						
							|  |  |  | 			header->pmode_misc_en_high)) | 
					
						
							| 
									
										
											  
											
												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+
											
										 
											2011-07-06 18:10:34 -07:00
										 |  |  | 		header->pmode_behavior |= | 
					
						
							|  |  |  | 			(1 << WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE); | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	header->realmode_flags = acpi_realmode_flags; | 
					
						
							|  |  |  | 	header->real_magic = 0x12345678; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef CONFIG_64BIT
 | 
					
						
							|  |  |  | 	header->pmode_entry = (u32)&wakeup_pmode_return; | 
					
						
							| 
									
										
										
										
											2012-11-16 13:57:43 -08:00
										 |  |  | 	header->pmode_cr3 = (u32)__pa_symbol(initial_page_table); | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	saved_magic = 0x12345678; | 
					
						
							|  |  |  | #else /* CONFIG_64BIT */
 | 
					
						
							| 
									
										
										
										
											2008-06-13 20:31:54 +02:00
										 |  |  | #ifdef CONFIG_SMP
 | 
					
						
							| 
									
										
										
										
											2011-02-04 16:14:11 -08:00
										 |  |  | 	stack_start = (unsigned long)temp_stack + sizeof(temp_stack); | 
					
						
							| 
									
										
										
										
											2008-10-17 01:26:27 +02:00
										 |  |  | 	early_gdt_descr.address = | 
					
						
							|  |  |  | 			(unsigned long)get_cpu_gdt_table(smp_processor_id()); | 
					
						
							| 
									
										
										
										
											2009-01-13 20:41:35 +09:00
										 |  |  | 	initial_gs = per_cpu_offset(smp_processor_id()); | 
					
						
							| 
									
										
										
										
											2008-06-13 20:31:54 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	initial_code = (unsigned long)wakeup_long64; | 
					
						
							| 
									
										
										
										
											2009-04-18 13:44:57 +02:00
										 |  |  |        saved_magic = 0x123456789abcdef0L; | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #endif /* CONFIG_64BIT */
 | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-08 23:42:22 +01:00
										 |  |  | 	do_suspend_lowlevel(); | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init acpi_sleep_setup(char *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	while ((str != NULL) && (*str != '\0')) { | 
					
						
							|  |  |  | 		if (strncmp(str, "s3_bios", 7) == 0) | 
					
						
							|  |  |  | 			acpi_realmode_flags |= 1; | 
					
						
							|  |  |  | 		if (strncmp(str, "s3_mode", 7) == 0) | 
					
						
							|  |  |  | 			acpi_realmode_flags |= 2; | 
					
						
							|  |  |  | 		if (strncmp(str, "s3_beep", 7) == 0) | 
					
						
							|  |  |  | 			acpi_realmode_flags |= 4; | 
					
						
							| 
									
										
										
										
											2008-07-23 21:28:41 -07:00
										 |  |  | #ifdef CONFIG_HIBERNATION
 | 
					
						
							|  |  |  | 		if (strncmp(str, "s4_nohwsig", 10) == 0) | 
					
						
							|  |  |  | 			acpi_no_s4_hw_signature(); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2010-07-23 22:59:09 +02:00
										 |  |  | 		if (strncmp(str, "nonvs", 5) == 0) | 
					
						
							|  |  |  | 			acpi_nvs_nosave(); | 
					
						
							| 
									
										
										
										
											2012-10-26 13:39:15 +02:00
										 |  |  | 		if (strncmp(str, "nonvs_s3", 8) == 0) | 
					
						
							|  |  |  | 			acpi_nvs_nosave_s3(); | 
					
						
							| 
									
										
										
										
											2008-06-12 23:24:06 +02:00
										 |  |  | 		if (strncmp(str, "old_ordering", 12) == 0) | 
					
						
							|  |  |  | 			acpi_old_suspend_ordering(); | 
					
						
							| 
									
										
										
										
											2008-01-30 13:32:54 +01:00
										 |  |  | 		str = strchr(str, ','); | 
					
						
							|  |  |  | 		if (str != NULL) | 
					
						
							|  |  |  | 			str += strspn(str, ", \t"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __setup("acpi_sleep=", acpi_sleep_setup); |