From patchwork Thu Jun 9 15:59:00 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Will Deacon X-Patchwork-Id: 866122 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 p59G27Jl015889 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 9 Jun 2011 16:02:33 GMT Received: from canuck.infradead.org ([134.117.69.58]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QUhfz-0003qa-Iq; Thu, 09 Jun 2011 16:01:44 +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 1QUhfx-0001LG-QA; Thu, 09 Jun 2011 16:01:41 +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 1QUhdg-0000pD-01 for linux-arm-kernel@lists.infradead.org; Thu, 09 Jun 2011 15:59:25 +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 p59FvmFH002664; Thu, 9 Jun 2011 16:57:50 +0100 (BST) From: Will Deacon To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v2 08/10] ARM: reset: add reset functionality for jumping to a physical address Date: Thu, 9 Jun 2011 16:59:00 +0100 Message-Id: <1307635142-11312-9-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: <1307635142-11312-1-git-send-email-will.deacon@arm.com> References: <1307635142-11312-1-git-send-email-will.deacon@arm.com> X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110609_115920_593934_78C6F471 X-CRM114-Status: GOOD ( 18.21 ) X-Spam-Score: -0.0 (/) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain Cc: frank.hofmann@tomtom.com, dave.martin@linaro.org, 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]); Thu, 09 Jun 2011 16:02:33 +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: Dave Martin Signed-off-by: Will Deacon --- arch/arm/include/asm/system.h | 1 + arch/arm/kernel/process.c | 72 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 8 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 8bd9d94..b7dc020 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -90,12 +90,10 @@ static int __init hlt_setup(char *__unused) __setup("nohlt", nohlt_setup); __setup("hlt", hlt_setup); -void arm_machine_restart(char mode, const char *cmd) -{ - /* Disable interrupts first */ - local_irq_disable(); - local_fiq_disable(); +extern void switch_stack(void (*fn)(void *), void *arg, void *sp); +static void prepare_for_reboot(char mode) +{ /* * Tell the mm system that we are going to reboot - * we may need it to insert some 1:1 mappings so that @@ -103,14 +101,20 @@ void arm_machine_restart(char mode, const char *cmd) */ setup_mm_for_reboot(mode); - /* 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(); +} + +void arm_machine_restart(char mode, const char *cmd) +{ + /* Disable interrupts first */ + local_irq_disable(); + local_fiq_disable(); + + prepare_for_reboot(mode); /* * Now call the architecture specific reboot code. @@ -126,6 +130,58 @@ void arm_machine_restart(char mode, const char *cmd) while (1); } +typedef void (*phys_reset_t)(unsigned long); +void __arm_machine_reset(void *reset_code_phys) +{ + phys_reset_t phys_reset; + + prepare_for_reboot(MODE_REMAP_KERNEL); + + /* Switch to the identity mapping. */ + phys_reset = (phys_reset_t)virt_to_phys(cpu_reset); + phys_reset((unsigned long)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, *new_stack = (void *)RESERVE_STACK_PAGE; + + cpu_reset_end = (void *)PAGE_ALIGN((unsigned long)cpu_reset); + cpu_reset_end_phys = virt_to_phys(cpu_reset_end); + + /* Check that we can safely identity map the reset code. */ + BUG_ON(cpu_reset_end_phys > TASK_SIZE && + cpu_reset_end_phys <= RESERVE_STACK_PAGE); + + /* Check that the reserve stack page is valid memory. */ + BUG_ON(!pfn_valid(__phys_to_pfn(virt_to_phys(new_stack - 1)))); + + /* Disable interrupts first */ + local_irq_disable(); + local_fiq_disable(); + + /* + * Clean and invalidate L2. + * This is racy, so we must be the last guy left. + */ + WARN_ON(num_online_cpus() > 1); + /* Flush while we still have locking available to us. */ + outer_flush_all(); + outer_disable(); + /* Data destroyed here will only be speculative. */ + outer_inv_all(); + + /* Change to the new stack and continue with the reset. */ + switch_stack(__arm_machine_reset, (void *)reset_code_phys, new_stack); + + /* Should never get here. */ + BUG(); +} + /* * Function pointers to optional machine specific functions */