From patchwork Tue Aug 23 21:46:50 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Will Deacon X-Patchwork-Id: 1090062 Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by demeter2.kernel.org (8.14.4/8.14.4) with ESMTP id p7NLmNDj032167 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 23 Aug 2011 21:48:44 GMT Received: from canuck.infradead.org ([2001:4978:20e::1]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QvypD-0002xn-S4; Tue, 23 Aug 2011 21:48:01 +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 1QvypC-00050a-WC; Tue, 23 Aug 2011 21:47:59 +0000 Received: from cam-admin0.cambridge.arm.com ([217.140.96.50]) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1QvyoJ-0004oX-9H for linux-arm-kernel@lists.infradead.org; Tue, 23 Aug 2011 21:47:06 +0000 Received: from localhost.localdomain (e102144-lin.cambridge.arm.com [10.1.69.60]) by cam-admin0.cambridge.arm.com (8.12.6/8.12.6) with ESMTP id p7NLjb17026027; Tue, 23 Aug 2011 22:45:38 +0100 (BST) From: Will Deacon To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v4 6/8] ARM: reset: add reset functionality for jumping to a physical address Date: Tue, 23 Aug 2011 22:46:50 +0100 Message-Id: <1314136012-20533-7-git-send-email-will.deacon@arm.com> X-Mailer: git-send-email 1.7.0.4 MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <1314136012-20533-1-git-send-email-will.deacon@arm.com> References: <1314136012-20533-1-git-send-email-will.deacon@arm.com> X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110823_174703_710344_7DFE79B0 X-CRM114-Status: GOOD ( 17.93 ) X-Spam-Score: -2.8 (--) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-2.8 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [217.140.96.50 listed in list.dnswl.org] -0.5 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain Cc: Will Deacon X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list 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, 23 Aug 2011 21:48:44 +0000 (UTC) Tools such as kexec and CPU hotplug require a way to reset the processor and branch to some code in physical space. This requires various bits of jiggery pokery with the caches and MMU which, when it goes wrong, tends to lock up the system. This patch implements a new function, arm_machine_reset, for consolidating this code in one place where it can be used by multiple subsystems. Signed-off-by: Will Deacon --- arch/arm/include/asm/system.h | 1 + arch/arm/kernel/process.c | 71 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 832888d..cd2a3cd 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -108,6 +108,7 @@ extern int cpu_architecture(void); extern void cpu_init(void); void arm_machine_restart(char mode, const char *cmd); +void arm_machine_reset(unsigned long reset_code_phys); extern void (*arm_pm_restart)(char str, const char *cmd); #define UDBG_UNDEFINED (1 << 0) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 84228ec..cda91a2 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #ifdef CONFIG_CC_STACKPROTECTOR @@ -91,6 +92,8 @@ static int __init hlt_setup(char *__unused) __setup("nohlt", nohlt_setup); __setup("hlt", hlt_setup); +static char reboot_mode = 'h'; + void arm_machine_restart(char mode, const char *cmd) { /* Disable interrupts first */ @@ -127,6 +130,72 @@ void arm_machine_restart(char mode, const char *cmd) while (1); } +extern void switch_stack(void (*fn)(void *), void *arg, void *sp); +typedef void (*phys_reset_t)(unsigned long); + +struct arm_machine_reset_args { + unsigned long reset_code_phys; + pgd_t *reboot_tables; +}; + +void __arm_machine_reset(void *args) +{ + struct arm_machine_reset_args reset_args; + unsigned long reset_code_phys; + phys_reset_t phys_reset; + + /* Copy our arguments onto the new stack. */ + memcpy(&reset_args, args, sizeof(reset_args)); + reset_code_phys = reset_args.reset_code_phys; + + /* Take out a flat memory mapping. */ + setup_mm_for_reboot(reboot_mode, reset_args.reboot_tables); + + /* Clean and invalidate caches */ + flush_cache_all(); + + /* Turn off caching */ + cpu_proc_fin(); + + /* Push out any further dirty data, and ensure cache is empty */ + flush_cache_all(); + + /* Switch to the identity mapping. */ + phys_reset = (phys_reset_t)virt_to_phys(cpu_reset); + phys_reset(reset_code_phys); + + /* Should never get here. */ + BUG(); +} + +void arm_machine_reset(unsigned long reset_code_phys) +{ + phys_addr_t cpu_reset_end_phys; + void *cpu_reset_end; + struct arm_machine_reset_args reset_args; + + cpu_reset_end = (void *)PAGE_ALIGN((unsigned long)cpu_reset); + cpu_reset_end_phys = virt_to_phys(cpu_reset_end); + + /* Disable interrupts first. */ + local_irq_disable(); + local_fiq_disable(); + + /* Disable the L2. */ + outer_disable(); + + /* Populate the reset args. */ + reset_args.reset_code_phys = reset_code_phys; + reset_args.reboot_tables = pgd_alloc(&init_mm); + + /* Change to the new stack and continue with the reset. */ + switch_stack(__arm_machine_reset, (void *)&reset_args, + (void *)RESERVE_STACK_PAGE); + + /* Should never get here. */ + BUG(); +} + /* * Function pointers to optional machine specific functions */ @@ -216,8 +285,6 @@ void cpu_idle(void) } } -static char reboot_mode = 'h'; - int __init reboot_setup(char *str) { reboot_mode = str[0];