From patchwork Tue Jun 7 16:48:17 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Hofmann X-Patchwork-Id: 857812 Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p57GmZKn027888 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 7 Jun 2011 16:48:59 GMT Received: from canuck.infradead.org ([2001:4978:20e::1]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QTzS5-0003Yl-Fu; Tue, 07 Jun 2011 16:48:25 +0000 Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1QTzS5-0006hx-1K; Tue, 07 Jun 2011 16:48:25 +0000 Received: from mxvs2.esa.t-systems.com ([81.7.202.143]) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1QTzS0-0006hM-AA for linux-arm-kernel@lists.infradead.org; Tue, 07 Jun 2011 16:48:22 +0000 Received: from unknown (HELO nl-exc-01.intra.local) ([82.210.235.24]) by mx.esa.t-systems.com with ESMTP; 07 Jun 2011 16:48:18 +0000 Received: from magrathea ([10.101.8.37]) by nl-exc-01.intra.local with Microsoft SMTPSVC(6.0.3790.3959); Tue, 7 Jun 2011 18:48:18 +0200 Date: Tue, 7 Jun 2011 17:48:17 +0100 (BST) From: Frank Hofmann To: linux-pm@lists.linux-foundation.org, tuxonice-devel@tuxonice.net, linux-arm-kernel@lists.infradead.org Subject: [RFC PATCH v4] ARM hibernation/suspend-to-disk support Message-ID: User-Agent: Alpine 2.00 (DEB 1167 2008-08-23) MIME-Version: 1.0 X-OriginalArrivalTime: 07 Jun 2011 16:48:18.0102 (UTC) FILETIME=[B3BD4560:01CC2532] X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110607_124820_870454_7C49F6BD X-CRM114-Status: GOOD ( 27.02 ) X-Spam-Score: -0.7 (/) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-0.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [81.7.202.143 listed in list.dnswl.org] X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list Reply-To: frank.hofmann@tomtom.com List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Tue, 07 Jun 2011 16:48:59 +0000 (UTC) Hi, time for another round on this one... This got quite a bit cleaned up now. There's now no more need for a "swsusp context" at all. The code uses cpu_suspend/resume and keeps the snapshot state on the stack while writing it out. There are a few dependencies this patch brings in: * due to the use of cpu_suspend / cpu_resume, it'll only apply as-is to kernels no older than f6b0fa02e8b0708d17d631afce456524eadf87ff, where Russell King introduced the generic interface. Patching these into older kernels is a little work. * it temporarily uses swapper_pg_dir and establishes 1:1 mappings there for a MMU-off transition, which is necessary before resume. In order to tear these down afterwards, identity_mapping_del() needs to be called; for some reason that's #ifdef CONFIG_SMP ... * it needs to "catch" sleep_save_sp after cpu_suspend() so that resume can be provided with the proper starting point. This requires an ENTRY(sleep_save_sp) in arch/arm/kernel/sleep.S so that the symbol becomes public. * it assumes cpu_reset will disable the MMU. cpu_v6_reset/cpu_v7_reset are currently not doing so (amongst some other minor chip types). * there's kind of a circular dependency between CONFIG_HIBERNATION and CONFIG_PM_SLEEP, on ARM. The latter is necessary so that cpu_suspend and cpu_resume are compiled in, but it cannot be selected via ARCH_HIBERNATION_POSSIBLE because CONFIG_PM_SLEEP depends on CONFIG_HIBERNATION_INTERFACE - selected by CONFIG_HIBERNATION. Consequence is that right now, both CONFIG_PM_SLEEP and ...HIBERNATION must be set in your defconfig file to be able to compile. (my head swirls from writing this ...) Otherwise, this is by far the cleanest in the series yet. I've tested this on ARM1176; still need to do OMAP3 (Cortex-A8), will report on that. Please let me know what you think, FrankH. arch/arm/include/asm/memory.h | 1 + arch/arm/kernel/Makefile | 1 + arch/arm/mm/Kconfig | 5 ++ arch/arm/kernel/cpu.c | 94 +++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/swsusp.S | 84 ++++++++++++++++++++++++++++++++++++ 5 files changed, 185 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/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 diff --git a/arch/arm/kernel/cpu.c b/arch/arm/kernel/cpu.c new file mode 100644 index 0000000..2cdfa85 --- /dev/null +++ b/arch/arm/kernel/cpu.c @@ -0,0 +1,94 @@ +/* + * 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 + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include + +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) +{ + flush_thread(); + local_fiq_disable(); +} + +void notrace restore_processor_state(void) +{ + flush_tlb_all(); + flush_cache_all(); + local_fiq_enable(); +} + +u8 __swsusp_resume_stk[PAGE_SIZE/2] __nosavedata; +u32 __swsusp_save_sp; + +int __swsusp_arch_resume_finish(void) +{ + identity_mapping_del(swapper_pg_dir, __pa(_stext), __pa(_etext)); + return 0; +} + +/* + * 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. + */ +void notrace __swsusp_arch_restore_image(void) +{ + extern struct pbe *restore_pblist; + extern void cpu_resume(void); + extern unsigned long sleep_save_sp; + struct pbe *pbe; + typeof(cpu_reset) *phys_reset = (typeof(cpu_reset) *)virt_to_phys(cpu_reset); + + cpu_switch_mm(swapper_pg_dir, &init_mm); + + for (pbe = restore_pblist; pbe; pbe = pbe->next) + copy_page(pbe->orig_address, pbe->address); + + sleep_save_sp = __swsusp_save_sp; + flush_tlb_all(); + flush_cache_all(); + + identity_mapping_add(swapper_pg_dir, __pa(_stext), __pa(_etext)); + + flush_tlb_all(); + flush_cache_all(); + cpu_proc_fin(); + + flush_tlb_all(); + flush_cache_all(); + + phys_reset(virt_to_phys(cpu_resume)); +} + diff --git a/arch/arm/kernel/swsusp.S b/arch/arm/kernel/swsusp.S new file mode 100644 index 0000000..c3a4b83 --- /dev/null +++ b/arch/arm/kernel/swsusp.S @@ -0,0 +1,84 @@ +/* + * 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 + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include + +/* + * Save the current CPU state before suspend / poweroff. + * cpu_suspend() allocates space on the stack to save all necessary + * information. This has two consequences: + * - swsusp_save() has to be called without changing anything on + * the stack. One cannot just return into it. + * - should swsusp_save() fail for some reason, the previous value + * of sp has to be restored from a safe place. + */ +ENTRY(swsusp_arch_suspend) + mrs r1, cpsr + mrs r2, spsr + stmfd sp!, {r1-r11,lr} @ save registers + ldr r0, =.Ltemp_sp + str sp, [r0] @ temp + ldr r1, =(PHYS_OFFSET - PAGE_OFFSET) + adr r3, .Lresume_post_mmu @ resume here + bl cpu_suspend @ snapshot state (to stack) + ldr r1, =sleep_save_sp + ldr r0, =__swsusp_save_sp + ldr r2, [r1] + str r2, [r0] + bl swsusp_save @ write snapshot + ldr r1, =.Ltemp_sp + ldr sp, [r1] @ restore stack + ldmfd sp!, {r1-r11, pc} @ return +ENDPROC(swsusp_arch_suspend) + +/* + * Restore the memory image from the pagelists, and load the CPU registers + * from saved state. + */ +ENTRY(swsusp_arch_resume) + setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r2 + /* + * Switch stack to a nosavedata region to make sure image restore + * doesn't clobber it underneath itself. + * Note that this effectively nukes "current"; from here on, the + * executing code runs context-less and no functions can be called + * that have side effects beyond accessing global variables. + */ + ldr sp, =(__swsusp_resume_stk + PAGE_SIZE / 2) + b __swsusp_arch_restore_image +.ltorg +.align 5 + /* + * Execution returns here via resuming the saved context. + * MMU is active again and CPU core state has been restored, all + * that remains to be done now is to restore the CPU registers. + */ +.Lresume_post_mmu: + ldmfd sp!, {r1-r11} + msr cpsr, r1 + msr spsr, r2 + bl cpu_init @ reinitialize other modes + ldmfd sp!, {lr} + b __swsusp_arch_resume_finish @ cleanup +ENDPROC(swsusp_arch_resume) + +.data +.Ltemp_sp: + .long 0