Commit 58981827 authored by Patrick Mochel's avatar Patrick Mochel

Merge kernel.bkbits.net:linux-2.6-power

into digitalimplant.org:/home/mochel/src/linux-2.6-power
parents c0ddaa13 9c3776e7
obj-$(CONFIG_PM) += cpu.o obj-$(CONFIG_PM) += cpu.o
obj-$(CONFIG_PM_DISK) += pmdisk.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
/* Originally gcc generated, modified by hand */
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
.text
ENTRY(pmdisk_arch_suspend)
cmpl $0,4(%esp)
jne .L1450
movl %esp, saved_context_esp
movl %ebx, saved_context_ebx
movl %ebp, saved_context_ebp
movl %esi, saved_context_esi
movl %edi, saved_context_edi
pushfl ; popl saved_context_eflags
call pmdisk_suspend
jmp .L1449
.p2align 4,,7
.L1450:
movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3
movl pm_pagedir_nosave,%ebx
xorl %eax, %eax
xorl %edx, %edx
.p2align 4,,7
.L1455:
movl 4(%ebx,%edx),%edi
movl (%ebx,%edx),%esi
movl $1024, %ecx
rep
movsl
movl %cr3, %ecx;
movl %ecx, %cr3; # flush TLB
incl %eax
addl $16, %edx
cmpl pmdisk_pages,%eax
jb .L1455
.p2align 4,,7
.L1453:
movl saved_context_esp, %esp
movl saved_context_ebp, %ebp
movl saved_context_ebx, %ebx
movl saved_context_esi, %esi
movl saved_context_edi, %edi
pushl saved_context_eflags ; popfl
call pmdisk_resume
.L1449:
ret
...@@ -15,83 +15,47 @@ ...@@ -15,83 +15,47 @@
.text .text
ENTRY(do_magic) ENTRY(swsusp_arch_suspend)
pushl %ebx
cmpl $0,8(%esp)
jne resume
call do_magic_suspend_1
call save_processor_state
movl %esp, saved_context_esp movl %esp, saved_context_esp
movl %eax, saved_context_eax
movl %ebx, saved_context_ebx movl %ebx, saved_context_ebx
movl %ecx, saved_context_ecx
movl %edx, saved_context_edx
movl %ebp, saved_context_ebp movl %ebp, saved_context_ebp
movl %esi, saved_context_esi movl %esi, saved_context_esi
movl %edi, saved_context_edi movl %edi, saved_context_edi
pushfl ; popl saved_context_eflags pushfl ; popl saved_context_eflags
call do_magic_suspend_2 call swsusp_save
popl %ebx
ret ret
resume: ENTRY(swsusp_arch_resume)
movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3 movl %ecx,%cr3
call do_magic_resume_1 movl pagedir_nosave,%ebx
movl $0,loop xorl %eax, %eax
cmpl $0,nr_copy_pages xorl %edx, %edx
je copy_done
copy_loop:
movl $0,loop2
.p2align 4,,7 .p2align 4,,7
copy_one_page:
movl pagedir_nosave,%ecx
movl loop,%eax
movl loop2,%edx
sall $4,%eax
movl 4(%ecx,%eax),%ebx
movl (%ecx,%eax),%eax
movb (%edx,%eax),%al
movb %al,(%edx,%ebx)
movl loop2,%eax copy_loop:
leal 1(%eax),%edx movl 4(%ebx,%edx),%edi
movl %edx,loop2 movl (%ebx,%edx),%esi
movl %edx,%eax
cmpl $4095,%eax movl $1024, %ecx
jbe copy_one_page rep
movl loop,%eax movsl
leal 1(%eax),%edx
movl %edx,loop
movl %edx,%eax
cmpl nr_copy_pages,%eax
jb copy_loop
copy_done: incl %eax
movl $__USER_DS,%eax addl $16, %edx
cmpl nr_copy_pages,%eax
jb copy_loop
.p2align 4,,7
movw %ax, %ds
movw %ax, %es
movl saved_context_esp, %esp movl saved_context_esp, %esp
movl saved_context_ebp, %ebp movl saved_context_ebp, %ebp
movl saved_context_eax, %eax
movl saved_context_ebx, %ebx movl saved_context_ebx, %ebx
movl saved_context_ecx, %ecx
movl saved_context_edx, %edx
movl saved_context_esi, %esi movl saved_context_esi, %esi
movl saved_context_edi, %edi movl saved_context_edi, %edi
call restore_processor_state
pushl saved_context_eflags ; popfl pushl saved_context_eflags ; popfl
call do_magic_resume_2 call swsusp_restore
popl %ebx
ret ret
.section .data.nosave
loop:
.quad 0
loop2:
.quad 0
.previous
/* originally gcc generated, but now changed. don't overwrite. */ /* Originally gcc generated, modified by hand
*
* This may not use any stack, nor any variable that is not "NoSave":
*
* Its rewriting one kernel image with another. What is stack in "old"
* image could very well be data page in "new" image, and overwriting
* your own stack under you is bad idea.
*/
.text .text
#include <linux/linkage.h> #include <linux/linkage.h>
#include <asm/segment.h> #include <asm/segment.h>
#include <asm/page.h> #include <asm/page.h>
/* Input: ENTRY(swsusp_arch_suspend)
* rdi resume flag
*/
ENTRY(do_magic)
.LFB5:
subq $8, %rsp
.LCFI2:
testl %edi, %edi
jne .L90
call do_magic_suspend_1
call save_processor_state
movq %rsp, saved_context_esp(%rip) movq %rsp, saved_context_esp(%rip)
movq %rax, saved_context_eax(%rip) movq %rax, saved_context_eax(%rip)
...@@ -36,9 +32,10 @@ ENTRY(do_magic) ...@@ -36,9 +32,10 @@ ENTRY(do_magic)
movq %r15, saved_context_r15(%rip) movq %r15, saved_context_r15(%rip)
pushfq ; popq saved_context_eflags(%rip) pushfq ; popq saved_context_eflags(%rip)
addq $8, %rsp call swsusp_save
jmp do_magic_suspend_2 ret
.L90:
ENTRY(swsusp_arch_resume)
/* set up cr3 */ /* set up cr3 */
leaq init_level4_pgt(%rip),%rax leaq init_level4_pgt(%rip),%rax
subq $__START_KERNEL_map,%rax subq $__START_KERNEL_map,%rax
...@@ -53,7 +50,6 @@ ENTRY(do_magic) ...@@ -53,7 +50,6 @@ ENTRY(do_magic)
movq %rcx, %cr3; movq %rcx, %cr3;
movq %rax, %cr4; # turn PGE back on movq %rax, %cr4; # turn PGE back on
call do_magic_resume_1
movl nr_copy_pages(%rip), %eax movl nr_copy_pages(%rip), %eax
xorl %ecx, %ecx xorl %ecx, %ecx
movq $0, loop(%rip) movq $0, loop(%rip)
...@@ -113,9 +109,8 @@ ENTRY(do_magic) ...@@ -113,9 +109,8 @@ ENTRY(do_magic)
movq saved_context_r14(%rip), %r14 movq saved_context_r14(%rip), %r14
movq saved_context_r15(%rip), %r15 movq saved_context_r15(%rip), %r15
pushq saved_context_eflags(%rip) ; popfq pushq saved_context_eflags(%rip) ; popfq
call restore_processor_state call swsusp_restore
addq $8, %rsp ret
jmp do_magic_resume_2
.section .data.nosave .section .data.nosave
loop: loop:
......
...@@ -23,16 +23,6 @@ typedef struct pbe { ...@@ -23,16 +23,6 @@ typedef struct pbe {
#define SWAP_FILENAME_MAXLENGTH 32 #define SWAP_FILENAME_MAXLENGTH 32
struct suspend_header {
u32 version_code;
unsigned long num_physpages;
char machine[8];
char version[20];
int num_cpus;
int page_size;
suspend_pagedir_t *suspend_pagedir;
unsigned int num_pbes;
};
#define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1) #define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
...@@ -45,16 +35,12 @@ extern void drain_local_pages(void); ...@@ -45,16 +35,12 @@ extern void drain_local_pages(void);
/* kernel/power/swsusp.c */ /* kernel/power/swsusp.c */
extern int software_suspend(void); extern int software_suspend(void);
extern unsigned int nr_copy_pages __nosavedata;
extern suspend_pagedir_t *pagedir_nosave __nosavedata;
#else /* CONFIG_SOFTWARE_SUSPEND */ #else /* CONFIG_SOFTWARE_SUSPEND */
static inline int software_suspend(void) static inline int software_suspend(void)
{ {
printk("Warning: fake suspend called\n"); printk("Warning: fake suspend called\n");
return -EPERM; return -EPERM;
} }
#define software_resume() do { } while(0)
#endif /* CONFIG_SOFTWARE_SUSPEND */ #endif /* CONFIG_SOFTWARE_SUSPEND */
...@@ -78,12 +64,6 @@ static inline void disable_nonboot_cpus(void) {} ...@@ -78,12 +64,6 @@ static inline void disable_nonboot_cpus(void) {}
static inline void enable_nonboot_cpus(void) {} static inline void enable_nonboot_cpus(void) {}
#endif #endif
asmlinkage void do_magic(int is_resume);
asmlinkage void do_magic_resume_1(void);
asmlinkage void do_magic_resume_2(void);
asmlinkage void do_magic_suspend_1(void);
asmlinkage void do_magic_suspend_2(void);
void save_processor_state(void); void save_processor_state(void);
void restore_processor_state(void); void restore_processor_state(void);
struct saved_context; struct saved_context;
......
...@@ -18,6 +18,13 @@ config PM ...@@ -18,6 +18,13 @@ config PM
will issue the hlt instruction if nothing is to be done, thereby will issue the hlt instruction if nothing is to be done, thereby
sending the processor to sleep and saving power. sending the processor to sleep and saving power.
config PM_DEBUG
bool "Power Management Debug Support"
---help---
This option enables verbose debugging support in the Power Management
code. This is helpful when debugging and reporting various PM bugs,
like suspend support.
config SOFTWARE_SUSPEND config SOFTWARE_SUSPEND
bool "Software Suspend (EXPERIMENTAL)" bool "Software Suspend (EXPERIMENTAL)"
depends on EXPERIMENTAL && PM && SWAP depends on EXPERIMENTAL && PM && SWAP
...@@ -42,33 +49,12 @@ config SOFTWARE_SUSPEND ...@@ -42,33 +49,12 @@ config SOFTWARE_SUSPEND
For more information take a look at Documentation/power/swsusp.txt. For more information take a look at Documentation/power/swsusp.txt.
config PM_DISK config PM_STD_PARTITION
bool "Suspend-to-Disk Support"
depends on PM && SWAP && X86 && !X86_64
---help---
Suspend-to-disk is a power management state in which the contents
of memory are stored on disk and the entire system is shut down or
put into a low-power state (e.g. ACPI S4). When the computer is
turned back on, the stored image is loaded from disk and execution
resumes from where it left off before suspending.
This config option enables the core infrastructure necessary to
perform the suspend and resume transition.
Currently, this suspend-to-disk implementation is based on a forked
version of the swsusp code base. As such, it's still experimental,
and still relies on CONFIG_SWAP.
More information can be found in Documentation/power/.
If unsure, Say N.
config PM_DISK_PARTITION
string "Default resume partition" string "Default resume partition"
depends on PM_DISK depends on SOFTWARE_SUSPEND
default "" default ""
---help--- ---help---
The default resume partition is the partition that the pmdisk suspend- The default resume partition is the partition that the suspend-
to-disk implementation will look for a suspended disk image. to-disk implementation will look for a suspended disk image.
The partition specified here will be different for almost every user. The partition specified here will be different for almost every user.
...@@ -77,16 +63,10 @@ config PM_DISK_PARTITION ...@@ -77,16 +63,10 @@ config PM_DISK_PARTITION
The partition specified can be overridden by specifying: The partition specified can be overridden by specifying:
pmdisk=/dev/<other device> resume=/dev/<other device>
which will set the resume partition to the device specified. which will set the resume partition to the device specified.
One may also do:
pmdisk=off
to inform the kernel not to perform a resume transition.
Note there is currently not a way to specify which device to save the Note there is currently not a way to specify which device to save the
suspended image to. It will simply pick the first available swap suspended image to. It will simply pick the first available swap
device. device.
......
ifeq ($(CONFIG_PM_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
swsusp-smp-$(CONFIG_SMP) += smp.o swsusp-smp-$(CONFIG_SMP) += smp.o
obj-y := main.o process.o console.o pm.o obj-y := main.o process.o console.o pm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) disk.o
obj-$(CONFIG_PM_DISK) += disk.o pmdisk.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
...@@ -8,13 +8,11 @@ ...@@ -8,13 +8,11 @@
* *
*/ */
#define DEBUG
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/fs.h> #include <linux/fs.h>
#include "power.h" #include "power.h"
...@@ -23,13 +21,16 @@ ...@@ -23,13 +21,16 @@
extern u32 pm_disk_mode; extern u32 pm_disk_mode;
extern struct pm_ops * pm_ops; extern struct pm_ops * pm_ops;
extern int pmdisk_save(void); extern int swsusp_suspend(void);
extern int pmdisk_write(void); extern int swsusp_write(void);
extern int pmdisk_read(void); extern int swsusp_read(void);
extern int pmdisk_restore(void); extern int swsusp_resume(void);
extern int pmdisk_free(void); extern int swsusp_free(void);
static int noresume = 0;
char resume_file[256] = CONFIG_PM_STD_PARTITION;
/** /**
* power_down - Shut machine down for hibernate. * power_down - Shut machine down for hibernate.
* @mode: Suspend-to-disk mode * @mode: Suspend-to-disk mode
...@@ -46,16 +47,18 @@ static int power_down(u32 mode) ...@@ -46,16 +47,18 @@ static int power_down(u32 mode)
int error = 0; int error = 0;
local_irq_save(flags); local_irq_save(flags);
device_power_down(PM_SUSPEND_DISK);
switch(mode) { switch(mode) {
case PM_DISK_PLATFORM: case PM_DISK_PLATFORM:
device_power_down(PM_SUSPEND_DISK);
error = pm_ops->enter(PM_SUSPEND_DISK); error = pm_ops->enter(PM_SUSPEND_DISK);
break; break;
case PM_DISK_SHUTDOWN: case PM_DISK_SHUTDOWN:
printk("Powering off system\n"); printk("Powering off system\n");
device_shutdown();
machine_power_off(); machine_power_off();
break; break;
case PM_DISK_REBOOT: case PM_DISK_REBOOT:
device_shutdown();
machine_restart(NULL); machine_restart(NULL);
break; break;
} }
...@@ -99,6 +102,7 @@ static void finish(void) ...@@ -99,6 +102,7 @@ static void finish(void)
{ {
device_resume(); device_resume();
platform_finish(); platform_finish();
enable_nonboot_cpus();
thaw_processes(); thaw_processes();
pm_restore_console(); pm_restore_console();
} }
...@@ -126,6 +130,7 @@ static int prepare(void) ...@@ -126,6 +130,7 @@ static int prepare(void)
/* Free memory before shutting down devices. */ /* Free memory before shutting down devices. */
free_some_memory(); free_some_memory();
disable_nonboot_cpus();
if ((error = device_suspend(PM_SUSPEND_DISK))) if ((error = device_suspend(PM_SUSPEND_DISK)))
goto Finish; goto Finish;
...@@ -133,6 +138,7 @@ static int prepare(void) ...@@ -133,6 +138,7 @@ static int prepare(void)
Finish: Finish:
platform_finish(); platform_finish();
Thaw: Thaw:
enable_nonboot_cpus();
thaw_processes(); thaw_processes();
pm_restore_console(); pm_restore_console();
return error; return error;
...@@ -161,7 +167,7 @@ int pm_suspend_disk(void) ...@@ -161,7 +167,7 @@ int pm_suspend_disk(void)
pr_debug("PM: snapshotting memory.\n"); pr_debug("PM: snapshotting memory.\n");
in_suspend = 1; in_suspend = 1;
if ((error = pmdisk_save())) if ((error = swsusp_suspend()))
goto Done; goto Done;
if (in_suspend) { if (in_suspend) {
...@@ -173,14 +179,14 @@ int pm_suspend_disk(void) ...@@ -173,14 +179,14 @@ int pm_suspend_disk(void)
mb(); mb();
barrier(); barrier();
error = pmdisk_write(); error = swsusp_write();
if (!error) { if (!error) {
error = power_down(pm_disk_mode); error = power_down(pm_disk_mode);
pr_debug("PM: Power down failed.\n"); pr_debug("PM: Power down failed.\n");
} }
} else } else
pr_debug("PM: Image restored successfully.\n"); pr_debug("PM: Image restored successfully.\n");
pmdisk_free(); swsusp_free();
Done: Done:
finish(); finish();
return error; return error;
...@@ -188,7 +194,7 @@ int pm_suspend_disk(void) ...@@ -188,7 +194,7 @@ int pm_suspend_disk(void)
/** /**
* pm_resume - Resume from a saved image. * software_resume - Resume from a saved image.
* *
* Called as a late_initcall (so all devices are discovered and * Called as a late_initcall (so all devices are discovered and
* initialized), we call pmdisk to see if we have a saved image or not. * initialized), we call pmdisk to see if we have a saved image or not.
...@@ -199,13 +205,21 @@ int pm_suspend_disk(void) ...@@ -199,13 +205,21 @@ int pm_suspend_disk(void)
* *
*/ */
static int pm_resume(void) static int software_resume(void)
{ {
int error; int error;
if (noresume) {
/**
* FIXME: If noresume is specified, we need to find the partition
* and reset it back to normal swap space.
*/
return 0;
}
pr_debug("PM: Reading pmdisk image.\n"); pr_debug("PM: Reading pmdisk image.\n");
if ((error = pmdisk_read())) if ((error = swsusp_read()))
goto Done; goto Done;
pr_debug("PM: Preparing system for restore.\n"); pr_debug("PM: Preparing system for restore.\n");
...@@ -216,28 +230,18 @@ static int pm_resume(void) ...@@ -216,28 +230,18 @@ static int pm_resume(void)
barrier(); barrier();
mb(); mb();
/* FIXME: The following (comment and mdelay()) are from swsusp.
* Are they really necessary?
*
* We do not want some readahead with DMA to corrupt our memory, right?
* Do it with disabled interrupts for best effect. That way, if some
* driver scheduled DMA, we have good chance for DMA to finish ;-).
*/
pr_debug("PM: Waiting for DMAs to settle down.\n");
mdelay(1000);
pr_debug("PM: Restoring saved image.\n"); pr_debug("PM: Restoring saved image.\n");
pmdisk_restore(); swsusp_resume();
pr_debug("PM: Restore failed, recovering.n"); pr_debug("PM: Restore failed, recovering.n");
finish(); finish();
Free: Free:
pmdisk_free(); swsusp_free();
Done: Done:
pr_debug("PM: Resume from disk failed.\n"); pr_debug("PM: Resume from disk failed.\n");
return 0; return 0;
} }
late_initcall(pm_resume); late_initcall(software_resume);
static char * pm_disk_modes[] = { static char * pm_disk_modes[] = {
...@@ -336,3 +340,22 @@ static int __init pm_disk_init(void) ...@@ -336,3 +340,22 @@ static int __init pm_disk_init(void)
} }
core_initcall(pm_disk_init); core_initcall(pm_disk_init);
static int __init resume_setup(char *str)
{
if (noresume)
return 1;
strncpy( resume_file, str, 255 );
return 1;
}
static int __init noresume_setup(char *str)
{
noresume = 1;
return 1;
}
__setup("noresume", noresume_setup);
__setup("resume=", resume_setup);
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
* *
*/ */
#define DEBUG
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/string.h> #include <linux/string.h>
...@@ -169,6 +167,15 @@ static int enter_state(u32 state) ...@@ -169,6 +167,15 @@ static int enter_state(u32 state)
return error; return error;
} }
/*
* This is main interface to the outside world. It needs to be
* called from process context.
*/
int software_suspend(void)
{
return enter_state(PM_SUSPEND_DISK);
}
/** /**
* pm_suspend - Externally visible function for suspending system. * pm_suspend - Externally visible function for suspending system.
......
This diff is collapsed.
#include <linux/suspend.h>
#include <linux/utsname.h>
/* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but /* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but
we probably do not take enough locks for switching consoles, etc, we probably do not take enough locks for switching consoles, etc,
...@@ -9,7 +10,20 @@ ...@@ -9,7 +10,20 @@
#endif #endif
#ifdef CONFIG_PM_DISK struct swsusp_info {
struct new_utsname uts;
u32 version_code;
unsigned long num_physpages;
int cpus;
unsigned long image_pages;
unsigned long pagedir_pages;
suspend_pagedir_t * suspend_pagedir;
swp_entry_t pagedir[768];
} __attribute__((aligned(PAGE_SIZE)));
#ifdef CONFIG_SOFTWARE_SUSPEND
extern int pm_suspend_disk(void); extern int pm_suspend_disk(void);
#else #else
...@@ -18,7 +32,6 @@ static inline int pm_suspend_disk(void) ...@@ -18,7 +32,6 @@ static inline int pm_suspend_disk(void)
return -EPERM; return -EPERM;
} }
#endif #endif
extern struct semaphore pm_sem; extern struct semaphore pm_sem;
#define power_attr(_name) \ #define power_attr(_name) \
static struct subsys_attribute _name##_attr = { \ static struct subsys_attribute _name##_attr = { \
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment