2008-01-30 13:32:54 +01:00
/*
* sleep . c - x86 - specific ACPI sleep support .
*
* Copyright ( C ) 2001 - 2003 Patrick Mochel
* Copyright ( C ) 2001 - 2003 Pavel Machek < pavel @ suse . cz >
*/
# include <linux/acpi.h>
# include <linux/bootmem.h>
# include <linux/dmi.h>
# include <linux/cpumask.h>
2008-04-10 23:28:10 +02:00
# include "realmode/wakeup.h"
# include "sleep.h"
2008-01-30 13:32:54 +01:00
2008-02-22 23:11:39 +01:00
unsigned long acpi_wakeup_address ;
2008-01-30 13:32:54 +01:00
unsigned long acpi_realmode_flags ;
2008-04-10 23:28:10 +02:00
/* address in low memory of the wakeup routine. */
static unsigned long acpi_realmode ;
# ifdef CONFIG_64BIT
static char temp_stack [ 10240 ] ;
# endif
2008-01-30 13:32:54 +01:00
/**
* acpi_save_state_mem - save kernel state
*
* Create an identity mapped page table and copy the wakeup routine to
* low memory .
2008-04-10 23:28:10 +02:00
*
* Note that this is too late to change acpi_wakeup_address .
2008-01-30 13:32:54 +01:00
*/
int acpi_save_state_mem ( void )
{
2008-04-10 23:28:10 +02:00
struct wakeup_header * header ;
if ( ! acpi_realmode ) {
printk ( KERN_ERR " Could not allocate memory during boot, "
" S3 disabled \n " ) ;
2008-01-30 13:32:54 +01:00
return - ENOMEM ;
}
2008-04-10 23:28:10 +02:00
memcpy ( ( void * ) acpi_realmode , & wakeup_code_start , WAKEUP_SIZE ) ;
header = ( struct wakeup_header * ) ( acpi_realmode + HEADER_OFFSET ) ;
if ( header - > signature ! = 0x51ee1111 ) {
printk ( KERN_ERR " wakeup header does not match \n " ) ;
return - EINVAL ;
}
header - > video_mode = saved_video_mode ;
# ifndef CONFIG_64BIT
store_gdt ( ( struct desc_ptr * ) & header - > pmode_gdt ) ;
header - > pmode_efer_low = nx_enabled ;
if ( header - > pmode_efer_low & 1 ) {
/* This is strange, why not save efer, always? */
rdmsr ( MSR_EFER , header - > pmode_efer_low ,
header - > pmode_efer_high ) ;
}
# endif /* !CONFIG_64BIT */
header - > pmode_cr0 = read_cr0 ( ) ;
header - > pmode_cr4 = read_cr4 ( ) ;
header - > realmode_flags = acpi_realmode_flags ;
header - > real_magic = 0x12345678 ;
# ifndef CONFIG_64BIT
header - > pmode_entry = ( u32 ) & wakeup_pmode_return ;
header - > pmode_cr3 = ( u32 ) ( swsusp_pg_dir - __PAGE_OFFSET ) ;
saved_magic = 0x12345678 ;
# else /* CONFIG_64BIT */
header - > trampoline_segment = setup_trampoline ( ) > > 4 ;
init_rsp = ( unsigned long ) temp_stack + 4096 ;
initial_code = ( unsigned long ) wakeup_long64 ;
saved_magic = 0x123456789abcdef0 ;
# endif /* CONFIG_64BIT */
2008-01-30 13:32:54 +01:00
return 0 ;
}
/*
* acpi_restore_state - undo effects of acpi_save_state_mem
*/
void acpi_restore_state_mem ( void )
{
}
/**
* acpi_reserve_bootmem - do _very_ early ACPI initialisation
*
* We allocate a page from the first 1 MB of memory for the wakeup
* routine for when we come back from a sleep state . The
* runtime allocator allows specification of < 16 MB pages , but not
* < 1 MB pages .
*/
void __init acpi_reserve_bootmem ( void )
{
2008-04-10 23:28:10 +02:00
if ( ( & wakeup_code_end - & wakeup_code_start ) > WAKEUP_SIZE ) {
2008-01-30 13:32:54 +01:00
printk ( KERN_ERR
" ACPI: Wakeup code way too big, S3 disabled. \n " ) ;
return ;
}
2008-04-10 23:28:10 +02:00
acpi_realmode = ( unsigned long ) alloc_bootmem_low ( WAKEUP_SIZE ) ;
if ( ! acpi_realmode ) {
2008-01-30 13:32:54 +01:00
printk ( KERN_ERR " ACPI: Cannot allocate lowmem, S3 disabled. \n " ) ;
2008-04-10 23:28:10 +02:00
return ;
}
acpi_wakeup_address = acpi_realmode ;
2008-01-30 13:32:54 +01:00
}
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 ;
str = strchr ( str , ' , ' ) ;
if ( str ! = NULL )
str + = strspn ( str , " , \t " ) ;
}
return 1 ;
}
__setup ( " acpi_sleep= " , acpi_sleep_setup ) ;