@@ -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)
@@ -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
*/