Patches adding support for hibernation on ARM - ARM hibernation / suspend-to-disk - Change soft_restart to use non-tracing raw_local_irq_disable
Patches based on v3.13 tag, verified hibernation on beaglebone black on a branch based on 3.13 merged with initial omap support from Russ Dill which can be found here (includes v1 patchset): http://git.linaro.org/git-ro/people/sebastian.capella/linux.git hibernation_3.13_russMerge
[PATCH v4 1/2] ARM: avoid tracers in soft_restart arch/arm/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Use raw_local_irq_disable in place of local_irq_disable to avoid infinite abort recursion while tracing. (unchanged since v3)
[PATCH v4 2/2] ARM hibernation / suspend-to-disk arch/arm/include/asm/memory.h | 1 + arch/arm/kernel/Makefile | 1 + arch/arm/kernel/hibernate.c | 113 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 5 ++ include/linux/suspend.h | 2 + 5 files changed, 122 insertions(+)
Adds support for ARM based hibernation
Additional notes: -----------------
There are two checkpatch warnings added by this patch. These follow behavior in existing hibernation implementations on other platforms.
WARNING: externs should be avoided in .c files #116: FILE: arch/arm/kernel/hibernate.c:26: +extern const void __nosave_begin, __nosave_end;
This extern is picking up the linker nosave region definitions, only used in hibernate. Follows same extern line used mips, powerpc, s390, sh, sparc, x86 & unicore32
WARNING: externs should be avoided in .c files #191: FILE: arch/arm/kernel/hibernate.c:101: +extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
This extern is used in the arch/arm/ in hibernate, process and bL_switcher
Changes in v4: -------------- * updated comment for soft_restart with review feedback * dropped freeze_processes patch which was queued separately to 3.14 by Rafael Wysocki: https://lkml.org/lkml/2014/2/25/683
Changes in v3: -------------- * added comment to use of soft_restart * drop irq disable soft_restart patch * add patch to avoid tracers in soft_restart by using raw_local_irq_*
Changes in v2: -------------- * Removed unneeded flush_thread, use of __naked and cpu_init. * dropped Cyril Chemparathy cyril@ti.com from Cc: list as emails are bouncing.
Thanks,
Sebastian Capella
Use of tracers in local_irq_disable is causes recursive aborts when called with irqs disabled and using a temporary stack (hibernation). Replace local_irq_disable with raw_local_irq_disable instead to avoid tracers.
Signed-off-by: Sebastian Capella sebastian.capella@linaro.org Cc: Russell King linux@arm.linux.org.uk Cc: Andrew Morton akpm@linux-foundation.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Will Deacon will.deacon@arm.com Cc: Robin Holt robin.m.holt@gmail.com Cc: Lorenzo Pieralisi lorenzo.pieralisi@arm.com --- arch/arm/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 92f7b15..f58b723 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -100,7 +100,7 @@ void soft_restart(unsigned long addr) u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack);
/* Disable interrupts first */ - local_irq_disable(); + raw_local_irq_disable(); local_fiq_disable();
/* Disable the L2 if we're the last man standing. */
On Wed, Feb 26, 2014 at 11:10:56AM -0800, Sebastian Capella wrote:
Use of tracers in local_irq_disable is causes recursive aborts when called with irqs disabled and using a temporary stack (hibernation). Replace local_irq_disable with raw_local_irq_disable instead to avoid tracers.
Signed-off-by: Sebastian Capella sebastian.capella@linaro.org Cc: Russell King linux@arm.linux.org.uk Cc: Andrew Morton akpm@linux-foundation.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Will Deacon will.deacon@arm.com Cc: Robin Holt robin.m.holt@gmail.com Cc: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
arch/arm/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 92f7b15..f58b723 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -100,7 +100,7 @@ void soft_restart(unsigned long addr) u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack); /* Disable interrupts first */
- local_irq_disable();
- raw_local_irq_disable(); local_fiq_disable();
Great, I'm happy with this now. I assume that the plan is to submit this to my patch system when everyone's given their fill of acks?
Quoting Russell King - ARM Linux (2014-02-26 15:37:44)
On Wed, Feb 26, 2014 at 11:10:56AM -0800, Sebastian Capella wrote:
Use of tracers in local_irq_disable is causes recursive aborts when called with irqs disabled and using a temporary stack (hibernation). Replace local_irq_disable with raw_local_irq_disable instead to avoid tracers.
Signed-off-by: Sebastian Capella sebastian.capella@linaro.org Cc: Russell King linux@arm.linux.org.uk Cc: Andrew Morton akpm@linux-foundation.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Will Deacon will.deacon@arm.com Cc: Robin Holt robin.m.holt@gmail.com Cc: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
arch/arm/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 92f7b15..f58b723 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -100,7 +100,7 @@ void soft_restart(unsigned long addr) u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack); /* Disable interrupts first */
local_irq_disable();
raw_local_irq_disable(); local_fiq_disable();
Great, I'm happy with this now. I assume that the plan is to submit this to my patch system when everyone's given their fill of acks?
Thanks Russell!
I'll look into how to send it to your patch system and wait for people to respond.
Sebastian Capella
From: Russ Dill Russ.Dill@ti.com
Enable hibernation for ARM architectures and provide ARM architecture specific calls used during hibernation.
The swsusp hibernation framework depends on the platform first having functional suspend/resume.
Then, in order to enable hibernation on a given platform, a platform_hibernation_ops structure may need to be registered with the system in order to save/restore any SoC-specific / cpu specific state needing (re)init over a suspend-to-disk/resume-from-disk cycle.
For example:
- "secure" SoCs that have different sets of control registers and/or different CR reg access patterns.
- SoCs with L2 caches as the activation sequence there is SoC-dependent; a full off-on cycle for L2 is not done by the hibernation support code.
- SoCs requiring steps on wakeup _before_ the "generic" parts done by cpu_suspend / cpu_resume can work correctly.
- SoCs having persistent state which is maintained during suspend and resume, but will be lost during the power off cycle after suspend-to-disk.
This is a rebase/rework of Frank Hofmann's v5 hibernation patchset.
Cc: Russ Dill Russ.Dill@ti.com Cc: "Rafael J. Wysocki" rjw@rjwysocki.net Signed-off-by: Sebastian Capella sebastian.capella@linaro.org Cc: Russell King linux@arm.linux.org.uk Cc: Len Brown len.brown@intel.com Cc: Pavel Machek pavel@ucw.cz Cc: Nicolas Pitre nico@linaro.org Cc: Santosh Shilimkar santosh.shilimkar@ti.com Cc: Will Deacon will.deacon@arm.com Cc: Jonathan Austin jonathan.austin@arm.com Cc: Catalin Marinas catalin.marinas@arm.com Cc: "Uwe Kleine-König" u.kleine-koenig@pengutronix.de Cc: Stephen Boyd sboyd@codeaurora.org Cc: Lorenzo Pieralisi lorenzo.pieralisi@arm.com --- arch/arm/include/asm/memory.h | 1 + arch/arm/kernel/Makefile | 1 + arch/arm/kernel/hibernate.c | 113 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 5 ++ include/linux/suspend.h | 2 + 5 files changed, 122 insertions(+) create mode 100644 arch/arm/kernel/hibernate.c
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 8756e4b..1079ea8 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -291,6 +291,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((phys_addr_t)(x))) +#define __pa_symbol(x) __pa(RELOC_HIDE((unsigned long)(x), 0)) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
extern phys_addr_t (*arch_virt_to_idmap)(unsigned long x); diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index a30fc9b..8afa848 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o +obj-$(CONFIG_HIBERNATION) += hibernate.o obj-$(CONFIG_SMP) += smp.o ifdef CONFIG_MMU obj-$(CONFIG_SMP) += smp_tlb.o diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c new file mode 100644 index 0000000..70e17d2 --- /dev/null +++ b/arch/arm/kernel/hibernate.c @@ -0,0 +1,113 @@ +/* + * 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/suspend.h> +#include <asm/tlbflush.h> +#include <asm/cacheflush.h> +#include <asm/system_misc.h> +#include <asm/idmap.h> +#include <asm/suspend.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 notrace save_processor_state(void) +{ + WARN_ON(num_online_cpus() != 1); + local_fiq_disable(); +} + +void notrace restore_processor_state(void) +{ + local_fiq_enable(); +} + +/* + * Snapshot kernel memory and reset the system. + * + * swsusp_save() is executed in the suspend finisher so that the CPU + * context pointer and memory are part of the saved image, which is + * required by the resume kernel image to restart execution from + * swsusp_arch_suspend(). + * + * soft_restart is not technically needed, but is used to get success + * returned from cpu_suspend. + * + * When soft reboot completes, the hibernation snapshot is written out. + */ +static int notrace __swsusp_arch_save_image(unsigned long unused) +{ + int ret; + + ret = swsusp_save(); + if (ret == 0) + soft_restart(virt_to_phys(cpu_resume)); + return ret; +} + +/* + * Save the current CPU state before suspend / poweroff. + */ +int notrace swsusp_arch_suspend(void) +{ + return cpu_suspend(0, __swsusp_arch_save_image); +} + +/* + * The framework loads the hibernation image into a linked list anchored + * at restore_pblist, 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. + */ +static void notrace __swsusp_arch_restore_image(void *unused) +{ + struct pbe *pbe; + + cpu_switch_mm(idmap_pgd, &init_mm); + for (pbe = restore_pblist; pbe; pbe = pbe->next) + copy_page(pbe->orig_address, pbe->address); + + soft_restart(virt_to_phys(cpu_resume)); +} + +static u8 __swsusp_resume_stk[PAGE_SIZE/2] __nosavedata; + +/* + * Resume from the hibernation image. + * Due to the kernel heap / data restore, stack contents change underneath + * and that would make function calls impossible; switch to a temporary + * stack within the nosave region to avoid that problem. + */ +int swsusp_arch_resume(void) +{ + extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); + call_with_stack(__swsusp_arch_restore_image, 0, + __swsusp_resume_stk + sizeof(__swsusp_resume_stk)); + return 0; +} diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 1f8fed9..83707702 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -611,6 +611,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_LPAE diff --git a/include/linux/suspend.h b/include/linux/suspend.h index f73cabf..38bbf95 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -320,6 +320,8 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); extern bool system_entering_hibernation(void); +asmlinkage int swsusp_save(void); +extern struct pbe *restore_pblist; #else /* CONFIG_HIBERNATION */ static inline void register_nosave_region(unsigned long b, unsigned long e) {} static inline void register_nosave_region_late(unsigned long b, unsigned long e) {}
On 02/26/2014 11:10 AM, Sebastian Capella wrote:
From: Russ Dill Russ.Dill@ti.com
Enable hibernation for ARM architectures and provide ARM architecture specific calls used during hibernation.
The swsusp hibernation framework depends on the platform first having functional suspend/resume.
Then, in order to enable hibernation on a given platform, a platform_hibernation_ops structure may need to be registered with the system in order to save/restore any SoC-specific / cpu specific state needing (re)init over a suspend-to-disk/resume-from-disk cycle.
For example:
- "secure" SoCs that have different sets of control registers and/or different CR reg access patterns. - SoCs with L2 caches as the activation sequence there is SoC-dependent; a full off-on cycle for L2 is not done by the hibernation support code. - SoCs requiring steps on wakeup _before_ the "generic" parts done by cpu_suspend / cpu_resume can work correctly. - SoCs having persistent state which is maintained during suspend and resume, but will be lost during the power off cycle after suspend-to-disk.
This is a rebase/rework of Frank Hofmann's v5 hibernation patchset.
Cc: Russ Dill Russ.Dill@ti.com Cc: "Rafael J. Wysocki" rjw@rjwysocki.net Signed-off-by: Sebastian Capella sebastian.capella@linaro.org Cc: Russell King linux@arm.linux.org.uk Cc: Len Brown len.brown@intel.com Cc: Pavel Machek pavel@ucw.cz Cc: Nicolas Pitre nico@linaro.org Cc: Santosh Shilimkar santosh.shilimkar@ti.com Cc: Will Deacon will.deacon@arm.com Cc: Jonathan Austin jonathan.austin@arm.com Cc: Catalin Marinas catalin.marinas@arm.com Cc: "Uwe Kleine-König" u.kleine-koenig@pengutronix.de Cc: Stephen Boyd sboyd@codeaurora.org Cc: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
Acked-by: Russ Dill russ.dill@ti.com
arch/arm/include/asm/memory.h | 1 + arch/arm/kernel/Makefile | 1 + arch/arm/kernel/hibernate.c | 113 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 5 ++ include/linux/suspend.h | 2 + 5 files changed, 122 insertions(+) create mode 100644 arch/arm/kernel/hibernate.c
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 8756e4b..1079ea8 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -291,6 +291,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((phys_addr_t)(x))) +#define __pa_symbol(x) __pa(RELOC_HIDE((unsigned long)(x), 0)) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) extern phys_addr_t (*arch_virt_to_idmap)(unsigned long x); diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index a30fc9b..8afa848 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o +obj-$(CONFIG_HIBERNATION) += hibernate.o obj-$(CONFIG_SMP) += smp.o ifdef CONFIG_MMU obj-$(CONFIG_SMP) += smp_tlb.o diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c new file mode 100644 index 0000000..70e17d2 --- /dev/null +++ b/arch/arm/kernel/hibernate.c @@ -0,0 +1,113 @@ +/*
- 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.)
- Copyright (C) 2006 Rafael J. Wysocki rjw@sisk.pl
- License terms: GNU General Public License (GPL) version 2
- */
+#include <linux/mm.h> +#include <linux/suspend.h> +#include <asm/tlbflush.h> +#include <asm/cacheflush.h> +#include <asm/system_misc.h> +#include <asm/idmap.h> +#include <asm/suspend.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 notrace save_processor_state(void) +{
- WARN_ON(num_online_cpus() != 1);
- local_fiq_disable();
+}
+void notrace restore_processor_state(void) +{
- local_fiq_enable();
+}
+/*
- Snapshot kernel memory and reset the system.
- swsusp_save() is executed in the suspend finisher so that the CPU
- context pointer and memory are part of the saved image, which is
- required by the resume kernel image to restart execution from
- swsusp_arch_suspend().
- soft_restart is not technically needed, but is used to get success
- returned from cpu_suspend.
- When soft reboot completes, the hibernation snapshot is written out.
- */
+static int notrace __swsusp_arch_save_image(unsigned long unused) +{
- int ret;
- ret = swsusp_save();
- if (ret == 0)
soft_restart(virt_to_phys(cpu_resume));
- return ret;
+}
+/*
- Save the current CPU state before suspend / poweroff.
- */
+int notrace swsusp_arch_suspend(void) +{
- return cpu_suspend(0, __swsusp_arch_save_image);
+}
+/*
- The framework loads the hibernation image into a linked list anchored
- at restore_pblist, 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.
- */
+static void notrace __swsusp_arch_restore_image(void *unused) +{
- struct pbe *pbe;
- cpu_switch_mm(idmap_pgd, &init_mm);
- for (pbe = restore_pblist; pbe; pbe = pbe->next)
copy_page(pbe->orig_address, pbe->address);
- soft_restart(virt_to_phys(cpu_resume));
+}
+static u8 __swsusp_resume_stk[PAGE_SIZE/2] __nosavedata;
+/*
- Resume from the hibernation image.
- Due to the kernel heap / data restore, stack contents change underneath
- and that would make function calls impossible; switch to a temporary
- stack within the nosave region to avoid that problem.
- */
+int swsusp_arch_resume(void) +{
- extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
- call_with_stack(__swsusp_arch_restore_image, 0,
__swsusp_resume_stk + sizeof(__swsusp_resume_stk));
- return 0;
+} diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 1f8fed9..83707702 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -611,6 +611,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_LPAE diff --git a/include/linux/suspend.h b/include/linux/suspend.h index f73cabf..38bbf95 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -320,6 +320,8 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); extern bool system_entering_hibernation(void); +asmlinkage int swsusp_save(void); +extern struct pbe *restore_pblist; #else /* CONFIG_HIBERNATION */ static inline void register_nosave_region(unsigned long b, unsigned long e) {} static inline void register_nosave_region_late(unsigned long b, unsigned long e) {}
Accidentally missed a trailing space on a comment line.. will submit patch v5 to fix this.
Sebastian
On 26 February 2014 11:10, Sebastian Capella sebastian.capella@linaro.orgwrote:
From: Russ Dill Russ.Dill@ti.com
Enable hibernation for ARM architectures and provide ARM architecture specific calls used during hibernation.
The swsusp hibernation framework depends on the platform first having functional suspend/resume.
Then, in order to enable hibernation on a given platform, a platform_hibernation_ops structure may need to be registered with the system in order to save/restore any SoC-specific / cpu specific state needing (re)init over a suspend-to-disk/resume-from-disk cycle.
For example:
- "secure" SoCs that have different sets of control registers and/or different CR reg access patterns. - SoCs with L2 caches as the activation sequence there is SoC-dependent; a full off-on cycle for L2 is not done by the hibernation support code. - SoCs requiring steps on wakeup _before_ the "generic" parts done by cpu_suspend / cpu_resume can work correctly. - SoCs having persistent state which is maintained during suspend and resume, but will be lost during the power off cycle after suspend-to-disk.
This is a rebase/rework of Frank Hofmann's v5 hibernation patchset.
Cc: Russ Dill Russ.Dill@ti.com Cc: "Rafael J. Wysocki" rjw@rjwysocki.net Signed-off-by: Sebastian Capella sebastian.capella@linaro.org Cc: Russell King linux@arm.linux.org.uk Cc: Len Brown len.brown@intel.com Cc: Pavel Machek pavel@ucw.cz Cc: Nicolas Pitre nico@linaro.org Cc: Santosh Shilimkar santosh.shilimkar@ti.com Cc: Will Deacon will.deacon@arm.com Cc: Jonathan Austin jonathan.austin@arm.com Cc: Catalin Marinas catalin.marinas@arm.com Cc: "Uwe Kleine-König" u.kleine-koenig@pengutronix.de Cc: Stephen Boyd sboyd@codeaurora.org Cc: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
arch/arm/include/asm/memory.h | 1 + arch/arm/kernel/Makefile | 1 + arch/arm/kernel/hibernate.c | 113 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 5 ++ include/linux/suspend.h | 2 + 5 files changed, 122 insertions(+) create mode 100644 arch/arm/kernel/hibernate.c
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 8756e4b..1079ea8 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -291,6 +291,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((phys_addr_t)(x))) +#define __pa_symbol(x) __pa(RELOC_HIDE((unsigned long)(x), 0)) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
extern phys_addr_t (*arch_virt_to_idmap)(unsigned long x); diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index a30fc9b..8afa848 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o +obj-$(CONFIG_HIBERNATION) += hibernate.o obj-$(CONFIG_SMP) += smp.o ifdef CONFIG_MMU obj-$(CONFIG_SMP) += smp_tlb.o diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c new file mode 100644 index 0000000..70e17d2 --- /dev/null +++ b/arch/arm/kernel/hibernate.c @@ -0,0 +1,113 @@ +/*
- 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://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html
- Copyright (C) 2006 Rafael J. Wysocki rjw@sisk.pl
- License terms: GNU General Public License (GPL) version 2
- */
+#include <linux/mm.h> +#include <linux/suspend.h> +#include <asm/tlbflush.h> +#include <asm/cacheflush.h> +#include <asm/system_misc.h> +#include <asm/idmap.h> +#include <asm/suspend.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 notrace save_processor_state(void) +{
WARN_ON(num_online_cpus() != 1);
local_fiq_disable();
+}
+void notrace restore_processor_state(void) +{
local_fiq_enable();
+}
+/*
- Snapshot kernel memory and reset the system.
- swsusp_save() is executed in the suspend finisher so that the CPU
- context pointer and memory are part of the saved image, which is
- required by the resume kernel image to restart execution from
- swsusp_arch_suspend().
- soft_restart is not technically needed, but is used to get success
- returned from cpu_suspend.
- When soft reboot completes, the hibernation snapshot is written out.
- */
+static int notrace __swsusp_arch_save_image(unsigned long unused) +{
int ret;
ret = swsusp_save();
if (ret == 0)
soft_restart(virt_to_phys(cpu_resume));
return ret;
+}
+/*
- Save the current CPU state before suspend / poweroff.
- */
+int notrace swsusp_arch_suspend(void) +{
return cpu_suspend(0, __swsusp_arch_save_image);
+}
+/*
- The framework loads the hibernation image into a linked list anchored
- at restore_pblist, 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.
- */
+static void notrace __swsusp_arch_restore_image(void *unused) +{
struct pbe *pbe;
cpu_switch_mm(idmap_pgd, &init_mm);
for (pbe = restore_pblist; pbe; pbe = pbe->next)
copy_page(pbe->orig_address, pbe->address);
soft_restart(virt_to_phys(cpu_resume));
+}
+static u8 __swsusp_resume_stk[PAGE_SIZE/2] __nosavedata;
+/*
- Resume from the hibernation image.
- Due to the kernel heap / data restore, stack contents change underneath
- and that would make function calls impossible; switch to a temporary
- stack within the nosave region to avoid that problem.
- */
+int swsusp_arch_resume(void) +{
extern void call_with_stack(void (*fn)(void *), void *arg, void
*sp);
call_with_stack(__swsusp_arch_restore_image, 0,
__swsusp_resume_stk + sizeof(__swsusp_resume_stk));
return 0;
+} diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 1f8fed9..83707702 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -611,6 +611,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_LPAE diff --git a/include/linux/suspend.h b/include/linux/suspend.h index f73cabf..38bbf95 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -320,6 +320,8 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); extern bool system_entering_hibernation(void); +asmlinkage int swsusp_save(void); +extern struct pbe *restore_pblist; #else /* CONFIG_HIBERNATION */ static inline void register_nosave_region(unsigned long b, unsigned long e) {} static inline void register_nosave_region_late(unsigned long b, unsigned long e) {} -- 1.7.9.5
linaro-kernel@lists.linaro.org