#include <linux/sched/task_stack.h>
 #include <linux/init.h>
 #include <linux/export.h>
+#include <linux/kexec.h>
 
 #include <asm/mmu_context.h>
 #include <asm/time.h>
        .cpu_disable            = octeon_cpu_disable,
        .cpu_die                = octeon_cpu_die,
 #endif
+#ifdef CONFIG_KEXEC
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+#endif
 };
 
 static irqreturn_t octeon_78xx_reched_interrupt(int irq, void *dev_id)
        .cpu_disable            = octeon_cpu_disable,
        .cpu_die                = octeon_cpu_die,
 #endif
+#ifdef CONFIG_KEXEC
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+#endif
 };
 
 void __init octeon_setup_smp(void)
 
 extern int (*_machine_kexec_prepare)(struct kimage *);
 extern void (*_machine_kexec_shutdown)(void);
 extern void (*_machine_crash_shutdown)(struct pt_regs *regs);
-extern void default_machine_crash_shutdown(struct pt_regs *regs);
+void default_machine_crash_shutdown(struct pt_regs *regs);
+void kexec_nonboot_cpu_jump(void);
+void kexec_reboot(void);
 #ifdef CONFIG_SMP
 extern const unsigned char kexec_smp_wait[];
 extern unsigned long secondary_kexec_args[4];
-extern void (*relocated_kexec_smp_wait) (void *);
 extern atomic_t kexec_ready_to_reboot;
 extern void (*_crash_smp_send_stop)(void);
 #endif
 
        int (*cpu_disable)(void);
        void (*cpu_die)(unsigned int cpu);
 #endif
+#ifdef CONFIG_KEXEC
+       void (*kexec_nonboot_cpu)(void);
+#endif
 };
 
 extern void register_smp_ops(const struct plat_smp_ops *ops);
 
 extern void play_dead(void);
 #endif
 
+#ifdef CONFIG_KEXEC
+static inline void kexec_nonboot_cpu(void)
+{
+       extern const struct plat_smp_ops *mp_ops;       /* private */
+
+       return mp_ops->kexec_nonboot_cpu();
+}
+
+static inline void *kexec_nonboot_cpu_func(void)
+{
+       extern const struct plat_smp_ops *mp_ops;       /* private */
+
+       return mp_ops->kexec_nonboot_cpu;
+}
+#endif
+
 /*
  * This function will set up the necessary IPIs for Linux to communicate
  * with the CPUs in mask.
 
 
        while (!atomic_read(&kexec_ready_to_reboot))
                cpu_relax();
-       relocated_kexec_smp_wait(NULL);
+
+       kexec_reboot();
+
        /* NOTREACHED */
 }
 
 
 extern unsigned long kexec_start_address;
 extern unsigned long kexec_indirection_page;
 
-int (*_machine_kexec_prepare)(struct kimage *) = NULL;
-void (*_machine_kexec_shutdown)(void) = NULL;
-void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+static unsigned long reboot_code_buffer;
+
 #ifdef CONFIG_SMP
-void (*relocated_kexec_smp_wait) (void *);
+static void (*relocated_kexec_smp_wait)(void *);
+
 atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
 void (*_crash_smp_send_stop)(void) = NULL;
 #endif
 
+int (*_machine_kexec_prepare)(struct kimage *) = NULL;
+void (*_machine_kexec_shutdown)(void) = NULL;
+void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+
 static void kexec_image_info(const struct kimage *kimage)
 {
        unsigned long i;
 int
 machine_kexec_prepare(struct kimage *kimage)
 {
+#ifdef CONFIG_SMP
+       if (!kexec_nonboot_cpu_func())
+               return -EINVAL;
+#endif
+
        kexec_image_info(kimage);
 
        if (_machine_kexec_prepare)
                return _machine_kexec_prepare(kimage);
+
        return 0;
 }
 
 {
 }
 
+#ifdef CONFIG_SMP
+static void kexec_shutdown_secondary(void *param)
+{
+       int cpu = smp_processor_id();
+
+       if (!cpu_online(cpu))
+               return;
+
+       /* We won't be sent IPIs any more. */
+       set_cpu_online(cpu, false);
+
+       local_irq_disable();
+       while (!atomic_read(&kexec_ready_to_reboot))
+               cpu_relax();
+
+       kexec_reboot();
+
+       /* NOTREACHED */
+}
+#endif
+
 void
 machine_shutdown(void)
 {
        if (_machine_kexec_shutdown)
                _machine_kexec_shutdown();
+
+#ifdef CONFIG_SMP
+       smp_call_function(kexec_shutdown_secondary, NULL, 0);
+
+       while (num_online_cpus() > 1) {
+               cpu_relax();
+               mdelay(1);
+       }
+#endif
 }
 
 void
                default_machine_crash_shutdown(regs);
 }
 
-typedef void (*noretfun_t)(void) __noreturn;
+#ifdef CONFIG_SMP
+void kexec_nonboot_cpu_jump(void)
+{
+       local_flush_icache_range((unsigned long)relocated_kexec_smp_wait,
+                                reboot_code_buffer + relocate_new_kernel_size);
+
+       relocated_kexec_smp_wait(NULL);
+}
+#endif
+
+void kexec_reboot(void)
+{
+       void (*do_kexec)(void) __noreturn;
+
+#ifdef CONFIG_SMP
+       if (smp_processor_id() > 0) {
+               /*
+                * Instead of cpu_relax() or wait, this is needed for kexec
+                * smp reboot. Kdump usually doesn't require an smp new
+                * kernel, but kexec may do.
+                */
+               kexec_nonboot_cpu();
+
+               /* NOTREACHED */
+       }
+#endif
+
+       /*
+        * Make sure we get correct instructions written by the
+        * machine_kexec() CPU.
+        */
+       local_flush_icache_range(reboot_code_buffer,
+                                reboot_code_buffer + relocate_new_kernel_size);
+
+       do_kexec = (void *)reboot_code_buffer;
+       do_kexec();
+}
 
 void
 machine_kexec(struct kimage *image)
 {
-       unsigned long reboot_code_buffer;
        unsigned long entry;
        unsigned long *ptr;
 
 
        printk("Will call new kernel at %08lx\n", image->start);
        printk("Bye ...\n");
+       /* Make reboot code buffer available to the boot CPU. */
        __flush_cache_all();
 #ifdef CONFIG_SMP
        /* All secondary cpus now may jump to kexec_wait cycle */
        smp_wmb();
        atomic_set(&kexec_ready_to_reboot, 1);
 #endif
-       ((noretfun_t) reboot_code_buffer)();
+       kexec_reboot();
 }
 
 #include <linux/linkage.h>
 #include <linux/bug.h>
 #include <linux/kernel.h>
+#include <linux/kexec.h>
 
 #include <asm/time.h>
 #include <asm/pgtable.h>
        .cpu_disable            = bmips_cpu_disable,
        .cpu_die                = bmips_cpu_die,
 #endif
+#ifdef CONFIG_KEXEC
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+#endif
 };
 
 const struct plat_smp_ops bmips5000_smp_ops = {
        .cpu_disable            = bmips_cpu_disable,
        .cpu_die                = bmips_cpu_die,
 #endif
+#ifdef CONFIG_KEXEC
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+#endif
 };
 
 #endif /* CONFIG_SMP */
 
 #include <linux/sched/task_stack.h>
 #include <linux/smp.h>
 #include <linux/cpufreq.h>
+#include <linux/kexec.h>
 #include <asm/processor.h>
 #include <asm/time.h>
 #include <asm/clock.h>
        .cpu_disable = loongson3_cpu_disable,
        .cpu_die = loongson3_cpu_die,
 #endif
+#ifdef CONFIG_KEXEC
+       .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
+#endif
 };