diff options
Diffstat (limited to 'arch/mips/kernel/machine_kexec.c')
| -rw-r--r-- | arch/mips/kernel/machine_kexec.c | 51 |
1 files changed, 38 insertions, 13 deletions
diff --git a/arch/mips/kernel/machine_kexec.c b/arch/mips/kernel/machine_kexec.c index e0ad754c7ed..992e18474da 100644 --- a/arch/mips/kernel/machine_kexec.c +++ b/arch/mips/kernel/machine_kexec.c @@ -5,7 +5,7 @@ * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ - +#include <linux/compiler.h> #include <linux/kexec.h> #include <linux/mm.h> #include <linux/delay.h> @@ -13,15 +13,25 @@ #include <asm/cacheflush.h> #include <asm/page.h> -const extern unsigned char relocate_new_kernel[]; -const extern unsigned int relocate_new_kernel_size; +extern const unsigned char relocate_new_kernel[]; +extern const size_t relocate_new_kernel_size; 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; +#ifdef CONFIG_SMP +void (*relocated_kexec_smp_wait) (void *); +atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0); +#endif + int machine_kexec_prepare(struct kimage *kimage) { + if (_machine_kexec_prepare) + return _machine_kexec_prepare(kimage); return 0; } @@ -33,13 +43,21 @@ machine_kexec_cleanup(struct kimage *kimage) void machine_shutdown(void) { + if (_machine_kexec_shutdown) + _machine_kexec_shutdown(); } void machine_crash_shutdown(struct pt_regs *regs) { + if (_machine_crash_shutdown) + _machine_crash_shutdown(regs); + else + default_machine_crash_shutdown(regs); } +typedef void (*noretfun_t)(void) __noreturn; + void machine_kexec(struct kimage *image) { @@ -50,8 +68,11 @@ machine_kexec(struct kimage *image) reboot_code_buffer = (unsigned long)page_address(image->control_code_page); - kexec_start_address = image->start; - kexec_indirection_page = phys_to_virt(image->head & PAGE_MASK); + kexec_start_address = + (unsigned long) phys_to_virt(image->start); + + kexec_indirection_page = + (unsigned long) phys_to_virt(image->head & PAGE_MASK); memcpy((void*)reboot_code_buffer, relocate_new_kernel, relocate_new_kernel_size); @@ -60,14 +81,14 @@ machine_kexec(struct kimage *image) * The generic kexec code builds a page list with physical * addresses. they are directly accessible through KSEG0 (or * CKSEG0 or XPHYS if on 64bit system), hence the - * pys_to_virt() call. + * phys_to_virt() call. */ for (ptr = &image->head; (entry = *ptr) && !(entry &IND_DONE); ptr = (entry & IND_INDIRECTION) ? phys_to_virt(entry & PAGE_MASK) : ptr + 1) { if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || *ptr & IND_DESTINATION) - *ptr = phys_to_virt(*ptr); + *ptr = (unsigned long) phys_to_virt(*ptr); } /* @@ -75,11 +96,15 @@ machine_kexec(struct kimage *image) */ local_irq_disable(); - flush_icache_range(reboot_code_buffer, - reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); - - printk("Will call new kernel at %08x\n", image->start); + printk("Will call new kernel at %08lx\n", image->start); printk("Bye ...\n"); - flush_cache_all(); - ((void (*)(void))reboot_code_buffer)(); + __flush_cache_all(); +#ifdef CONFIG_SMP + /* All secondary cpus now may jump to kexec_wait cycle */ + relocated_kexec_smp_wait = reboot_code_buffer + + (void *)(kexec_smp_wait - relocate_new_kernel); + smp_wmb(); + atomic_set(&kexec_ready_to_reboot, 1); +#endif + ((noretfun_t) reboot_code_buffer)(); } |
