summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/process.c
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2011-06-06 12:28:54 +0100
committerWill Deacon <will.deacon@arm.com>2011-12-12 16:07:35 +0000
commit290130a17718c1451bb8a77a5e2510e0279bd5f3 (patch)
treec5373065a8809c02092891e1494e1c6fb904ad7f /arch/arm/kernel/process.c
parent2d81f1fe81b753a5744fd2deceafab3e62ba02d5 (diff)
downloadlinux-290130a17718c1451bb8a77a5e2510e0279bd5f3.tar.bz2
ARM: reset: implement soft_restart for jumping to a physical address
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 fleshes out the soft_restart implementation so that it branches to the reset code using the identity mapping. This requires us to change to a temporary stack, held within the kernel image as a static array, to avoid conflicting with the new view of memory. Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm/kernel/process.c')
-rw-r--r--arch/arm/kernel/process.c50
1 files changed, 40 insertions, 10 deletions
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index eeb3e16c6046..423bb2019451 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -92,17 +92,23 @@ static int __init hlt_setup(char *__unused)
__setup("nohlt", nohlt_setup);
__setup("hlt", hlt_setup);
-void soft_restart(unsigned long addr)
+extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
+typedef void (*phys_reset_t)(unsigned long);
+
+/*
+ * A temporary stack to use for CPU reset. This is static so that we
+ * don't clobber it with the identity mapping. When running with this
+ * stack, any references to the current task *will not work* so you
+ * should really do as little as possible before jumping to your reset
+ * code.
+ */
+static u64 soft_restart_stack[16];
+
+static void __soft_restart(void *addr)
{
- /* Disable interrupts first */
- local_irq_disable();
- local_fiq_disable();
+ phys_reset_t phys_reset;
- /*
- * Tell the mm system that we are going to reboot -
- * we may need it to insert some 1:1 mappings so that
- * soft boot works.
- */
+ /* Take out a flat memory mapping. */
setup_mm_for_reboot();
/* Clean and invalidate caches */
@@ -114,7 +120,31 @@ void soft_restart(unsigned long addr)
/* Push out any further dirty data, and ensure cache is empty */
flush_cache_all();
- cpu_reset(addr);
+ /* Switch to the identity mapping. */
+ phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
+ phys_reset((unsigned long)addr);
+
+ /* Should never get here. */
+ BUG();
+}
+
+void soft_restart(unsigned long addr)
+{
+ u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack);
+
+ /* Disable interrupts first */
+ local_irq_disable();
+ local_fiq_disable();
+
+ /* Disable the L2 if we're the last man standing. */
+ if (num_online_cpus() == 1)
+ outer_disable();
+
+ /* Change to the new stack and continue with the reset. */
+ call_with_stack(__soft_restart, (void *)addr, (void *)stack);
+
+ /* Should never get here. */
+ BUG();
}
void arm_machine_restart(char mode, const char *cmd)