diff mbox

[RFC,v3] ARM hibernation/suspend-to-disk support

Message ID alpine.DEB.2.00.1105251251330.4319@localhost6.localdomain6
State New
Headers show

Commit Message

Frank Hofmann May 25, 2011, 1:16 p.m. UTC
Hi,

I've cleaned this up by the suggestions in the previous thread; this is 
the result.

- now baselined against rmk/devel-stable
- didn't create the <asm/suspend.h> because Rafael is just removing
   that everywhere anyway
- Fixes re prev suggestion:
 	local_fiq_enable/disable bracketing
 	save only absolutely essential regs and let cpu_init do the rest
 	thumb2 clean assembly
 	allows mach hooks (but they're not defined by this code)

- Also: uses the "generic suspend/resume support" code
   (commit f6b0fa02e8b0708d17d631afce456524eadf87ff, rmk/devel-stable)

Via the latter, the previously-used hooks into machine-dependent code, 
__save/__restore_processor_state, have become unnecessary.

This now simply calls the cpu_do_suspend/resume utilities provided by the 
generic code.


I'm still figuring out how to best test a recent devel-stable kernel ...


Please let me know what you think,
Thanks in advance,

FrankH.


==============================================================================
Note on non-current kernels (older than 2.6.38):
==============================================================================
The use of generic CPU suspend/resume also ties the code to a 
sufficiently-recent kernel - it requires the code from:

http://ftp.arm.linux.org.uk/git/gitweb.cgi?p=linux-2.6-arm.git;a=commitdiff;h=f6b0fa02e8b0708d17d631afce456524eadf87ff

Which is, for many that (want to) use the feature, a hurdle...

If you're using an older kernel (2.6.37 or below), try the "generic" part 
posted via:

https://patchwork.kernel.org/patch/809212/

and add a __save/__restore_processor_state for your CPU type, as mentioned 
here:

http://lists.arm.linux.org.uk/lurker/message/20110520.123937.760c528f.en.html
==============================================================================

Comments

Nicolas Pitre May 27, 2011, 7:27 p.m. UTC | #1
On Fri, 27 May 2011, Frank Hofmann wrote:

>  /*
>   * r0 = control register value
>   * r1 = v:p offset (preserved by cpu_do_resume)
> + *      if this is zero, do not reenable MMU (it's on)

This is wrong.  It is well possible for this to be zero when the MMU is 
active.

The best way to determine if MMU is on or off is:

	mrc	p15, 0, rx, c1, c0	@ load ctrl reg
	tst	rx, #1			@ test M bit

> I wonder; is there a proper/suggested way to switch MMU off (and not end in
> binary nirvana), to have the reentry / reenable work ?

This is slightly complicated.  You first need to turn of and disable the 
caches, and ideally set up a 1:1 mapping for the transition.  There are 
cpu_proc_fin() and cpu_reset(branch_location).

You may also investigate how kexec is handled which purpose is to let 
the kernel boot another kernel.


Nicolas
Pavel Machek June 2, 2011, 10:18 p.m. UTC | #2
Hi!

> Please let me know what you think,
> Thanks in advance,

> +void save_processor_state(void)
> +{
> +	flush_thread();
> +	local_fiq_disable();
> +#ifdef CONFIG_ARM_MACH_HIBERNATION_HOOK
> +	mach_save_state();
> +#endif
> +}

Could we always define mach_save_state, and get rid of ifdef?
Frank Hofmann June 14, 2011, 8 a.m. UTC | #3
On Fri, 3 Jun 2011, Pavel Machek wrote:

> Hi!
>
>> Please let me know what you think,
>> Thanks in advance,
>
>> +void save_processor_state(void)
>> +{
>> +	flush_thread();
>> +	local_fiq_disable();
>> +#ifdef CONFIG_ARM_MACH_HIBERNATION_HOOK
>> +	mach_save_state();
>> +#endif
>> +}
>
> Could we always define mach_save_state, and get rid of ifdef?

Yes ... but it addresses only part of the issue(s). There's some state 
which cannot be saved / restored separately from the core state that's 
dealt with inside swsusp_arch_suspend/resume. This hook couldn't deal with 
that.

Also, if having mach-specific hooks everywhere, wouldn't it be better to 
simply use a platform ops based approach ?

FrankH.



>
> -- 
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
>
diff mbox

Patch

 arch/arm/include/asm/memory.h |    1 +
 arch/arm/kernel/Makefile      |    1 +
 arch/arm/kernel/cpu.c         |   70 +++++++++++++++++++++++++++++++++
 arch/arm/kernel/swsusp.S      |   87 +++++++++++++++++++++++++++++++++++++++++
 arch/arm/mm/Kconfig           |    5 ++
 5 files changed, 164 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 431077c..c7ef454 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -250,6 +250,7 @@  static inline void *phys_to_virt(phys_addr_t x)
  */
 #define __pa(x)			__virt_to_phys((unsigned long)(x))
 #define __va(x)			((void *)__phys_to_virt((unsigned long)(x)))
+#define __pa_symbol(x)		__pa(RELOC_HIDE((unsigned long)(x),0))
 #define pfn_to_kaddr(pfn)	__va((pfn) << PAGE_SHIFT)
 
 /*
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 8d95446..b76a403 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -30,6 +30,7 @@  obj-$(CONFIG_ARTHUR)		+= arthur.o
 obj-$(CONFIG_ISA_DMA)		+= dma-isa.o
 obj-$(CONFIG_PCI)		+= bios32.o isa.o
 obj-$(CONFIG_PM_SLEEP)		+= sleep.o
+obj-$(CONFIG_HIBERNATION)	+= cpu.o swsusp.o
 obj-$(CONFIG_HAVE_SCHED_CLOCK)	+= sched_clock.o
 obj-$(CONFIG_SMP)		+= smp.o smp_tlb.o
 obj-$(CONFIG_HAVE_ARM_SCU)	+= smp_scu.o
diff --git a/arch/arm/kernel/cpu.c b/arch/arm/kernel/cpu.c
new file mode 100644
index 0000000..2dfb40b
--- /dev/null
+++ b/arch/arm/kernel/cpu.c
@@ -0,0 +1,70 @@ 
+/*
+ * Hibernation support specific for ARM
+ *
+ * Derived from work on ARM hibernation support by:
+ *
+ * Ubuntu project, hibernation support for mach-dove
+ * Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu)
+ * Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.)
+ *	https://lkml.org/lkml/2010/6/18/4
+ *	https://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html
+ *	https://patchwork.kernel.org/patch/96442/
+ *
+ * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+#include <asm/tlbflush.h>
+
+extern const void __nosave_begin, __nosave_end;
+
+int pfn_is_nosave(unsigned long pfn)
+{
+	unsigned long nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT;
+	unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT;
+
+	return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
+}
+
+void save_processor_state(void)
+{
+	flush_thread();
+	local_fiq_disable();
+#ifdef CONFIG_ARM_MACH_HIBERNATION_HOOK
+	mach_save_state();
+#endif
+}
+
+void restore_processor_state(void)
+{
+	local_flush_tlb_all();
+#ifdef CONFIG_ARM_MACH_HIBERNATION_HOOK
+	mach_restore_state();
+#endif
+	local_fiq_enable();
+}
+
+u8 __swsusp_arch_ctx[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+u8 __swsusp_resume_stk[PAGE_SIZE/2] __nosavedata;
+
+/*
+ * The framework loads the hibernation image into this linked list,
+ * for swsusp_arch_resume() to copy back to the proper destinations.
+ *
+ * To make this work if resume is triggered from initramfs, the
+ * pagetables need to be switched to allow writes to kernel mem.
+ */
+void notrace __swsusp_arch_restore_image(void)
+{
+	extern struct pbe *restore_pblist;
+	struct pbe *pbe;
+
+	cpu_switch_mm(__virt_to_phys(swapper_pg_dir), &init_mm);
+
+	for (pbe = restore_pblist; pbe; pbe = pbe->next)
+		copy_page(pbe->orig_address, pbe->address);
+}
diff --git a/arch/arm/kernel/swsusp.S b/arch/arm/kernel/swsusp.S
new file mode 100644
index 0000000..7308d6c
--- /dev/null
+++ b/arch/arm/kernel/swsusp.S
@@ -0,0 +1,87 @@ 
+/*
+ * Hibernation support specific for ARM
+ *
+ * Based on work by:
+ *
+ * Ubuntu project, hibernation support for mach-dove,
+ * Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu)
+ * Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.)
+ *	https://lkml.org/lkml/2010/6/18/4
+ *	https://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html
+ *	https://patchwork.kernel.org/patch/96442/
+ *
+ * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/linkage.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/asm-offsets.h>
+#include <asm/glue-proc.h>
+
+
+/*
+ * Save the current CPU state before suspend / poweroff.
+ */
+ENTRY(swsusp_arch_suspend)
+	ldr	r0, =__swsusp_arch_ctx
+	mrs	r1, cpsr
+	mrs	r2, spsr
+	stmia	r0!, {r1-r11,lr}	@ CPSR, SPSR, nonvolatile regs
+	str	sp, [r0], #4		@ stack
+	stmfd	sp!, {lr}
+#ifdef	MULTI_CPU
+	ldr	r1, =processor
+	mov	lr, pc
+	ldr	pc, [r1, #CPU_DO_SUSPEND]
+#else
+	bl	cpu_do_suspend
+#endif
+	ldmfd	sp!, {lr}
+	b	swsusp_save		@ let framework write snapshot out
+ENDPROC(swsusp_arch_suspend)
+
+/*
+ * Restore the memory image from the pagelists, and load the CPU registers
+ * from saved state.
+ */
+ENTRY(swsusp_arch_resume)
+	/*
+	 * Switch stack to a nosavedata region to make sure image restore
+	 * doesn't clobber it underneath itself.
+	 */
+	ldr	sp, =(__swsusp_resume_stk + PAGE_SIZE / 2)
+	bl	__swsusp_arch_restore_image
+
+	/*
+	 * Restore the CPU registers.
+	 */
+	ldr	r0, =__swsusp_arch_ctx
+	ldmia	r0!, {r1,r2}		@ CPSR / SPSR
+	msr	cpsr, r1
+	msr	spsr, r2
+	ldr	r0, =__swsusp_arch_ctx	@ reload in case regset switched
+	ldmia	r0!, {r1-r11,lr}	@ nonvolatile regs
+	ldr	sp, [r0], #4		@ stack
+
+	/*
+	 * From here on we have a valid stack again. Core state is
+	 * not restored yet, redirect to the machine-specific
+	 * implementation to get that done.
+	 * Resume has succeeded at this point; if the machine-specific
+	 * code wants to fail it needs to panic.
+	 */
+	mov	r1, #0
+	stmfd	sp!, {r1,r4-r11,lr}
+#ifdef MULTI_CPU
+	ldr	r1, =processor
+	mov	lr, pc
+	ldr	pc, [r1, #CPU_DO_RESUME]
+#else
+	bl	cpu_do_resume
+#endif
+	bl	cpu_init			@ reinitialize other modes
+	ldmfd	sp!, {r0,r4-r11,pc}
+ENDPROC(swsusp_arch_resume)
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 0074b8d..c668f8f 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -627,6 +627,11 @@  config CPU_USE_DOMAINS
 config IO_36
 	bool
 
+config ARCH_HIBERNATION_POSSIBLE
+	bool
+	depends on MMU
+	default y if CPU_ARM920T || CPU_ARM926T || CPU_SA1100 || CPU_XSCALE || CPU_XSC3 || CPU_V6 || CPU_V6K || CPU_V7
+
 comment "Processor Features"
 
 config ARM_THUMB