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-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 ) ;