162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * machine_kexec.c for kexec 462306a36Sopenharmony_ci * Created by <nschichan@corp.free.fr> on Thu Oct 12 15:15:06 2006 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/compiler.h> 762306a36Sopenharmony_ci#include <linux/kexec.h> 862306a36Sopenharmony_ci#include <linux/mm.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/libfdt.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/cacheflush.h> 1362306a36Sopenharmony_ci#include <asm/page.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciextern const unsigned char relocate_new_kernel[]; 1662306a36Sopenharmony_ciextern const size_t relocate_new_kernel_size; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciextern unsigned long kexec_start_address; 1962306a36Sopenharmony_ciextern unsigned long kexec_indirection_page; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic unsigned long reboot_code_buffer; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#ifdef CONFIG_SMP 2462306a36Sopenharmony_cistatic void (*relocated_kexec_smp_wait)(void *); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciatomic_t kexec_ready_to_reboot = ATOMIC_INIT(0); 2762306a36Sopenharmony_civoid (*_crash_smp_send_stop)(void) = NULL; 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_civoid (*_machine_kexec_shutdown)(void) = NULL; 3162306a36Sopenharmony_civoid (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void kexec_image_info(const struct kimage *kimage) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned long i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci pr_debug("kexec kimage info:\n"); 3862306a36Sopenharmony_ci pr_debug(" type: %d\n", kimage->type); 3962306a36Sopenharmony_ci pr_debug(" start: %lx\n", kimage->start); 4062306a36Sopenharmony_ci pr_debug(" head: %lx\n", kimage->head); 4162306a36Sopenharmony_ci pr_debug(" nr_segments: %lu\n", kimage->nr_segments); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci for (i = 0; i < kimage->nr_segments; i++) { 4462306a36Sopenharmony_ci pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n", 4562306a36Sopenharmony_ci i, 4662306a36Sopenharmony_ci kimage->segment[i].mem, 4762306a36Sopenharmony_ci kimage->segment[i].mem + kimage->segment[i].memsz, 4862306a36Sopenharmony_ci (unsigned long)kimage->segment[i].memsz, 4962306a36Sopenharmony_ci (unsigned long)kimage->segment[i].memsz / PAGE_SIZE); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#ifdef CONFIG_UHI_BOOT 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int uhi_machine_kexec_prepare(struct kimage *kimage) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int i; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* 6062306a36Sopenharmony_ci * In case DTB file is not passed to the new kernel, a flat device 6162306a36Sopenharmony_ci * tree will be created by kexec tool. It holds modified command 6262306a36Sopenharmony_ci * line for the new kernel. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci for (i = 0; i < kimage->nr_segments; i++) { 6562306a36Sopenharmony_ci struct fdt_header fdt; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (kimage->segment[i].memsz <= sizeof(fdt)) 6862306a36Sopenharmony_ci continue; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (copy_from_user(&fdt, kimage->segment[i].buf, sizeof(fdt))) 7162306a36Sopenharmony_ci continue; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (fdt_check_header(&fdt)) 7462306a36Sopenharmony_ci continue; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci kexec_args[0] = -2; 7762306a36Sopenharmony_ci kexec_args[1] = (unsigned long) 7862306a36Sopenharmony_ci phys_to_virt((unsigned long)kimage->segment[i].mem); 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciint (*_machine_kexec_prepare)(struct kimage *) = uhi_machine_kexec_prepare; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#else 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciint (*_machine_kexec_prepare)(struct kimage *) = NULL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#endif /* CONFIG_UHI_BOOT */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ciint 9462306a36Sopenharmony_cimachine_kexec_prepare(struct kimage *kimage) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci#ifdef CONFIG_SMP 9762306a36Sopenharmony_ci if (!kexec_nonboot_cpu_func()) 9862306a36Sopenharmony_ci return -EINVAL; 9962306a36Sopenharmony_ci#endif 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci kexec_image_info(kimage); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (_machine_kexec_prepare) 10462306a36Sopenharmony_ci return _machine_kexec_prepare(kimage); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_civoid 11062306a36Sopenharmony_cimachine_kexec_cleanup(struct kimage *kimage) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#ifdef CONFIG_SMP 11562306a36Sopenharmony_cistatic void kexec_shutdown_secondary(void *param) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int cpu = smp_processor_id(); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!cpu_online(cpu)) 12062306a36Sopenharmony_ci return; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* We won't be sent IPIs any more. */ 12362306a36Sopenharmony_ci set_cpu_online(cpu, false); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci local_irq_disable(); 12662306a36Sopenharmony_ci while (!atomic_read(&kexec_ready_to_reboot)) 12762306a36Sopenharmony_ci cpu_relax(); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci kexec_reboot(); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* NOTREACHED */ 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci#endif 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid 13662306a36Sopenharmony_cimachine_shutdown(void) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci if (_machine_kexec_shutdown) 13962306a36Sopenharmony_ci _machine_kexec_shutdown(); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#ifdef CONFIG_SMP 14262306a36Sopenharmony_ci smp_call_function(kexec_shutdown_secondary, NULL, 0); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci while (num_online_cpus() > 1) { 14562306a36Sopenharmony_ci cpu_relax(); 14662306a36Sopenharmony_ci mdelay(1); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci#endif 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid 15262306a36Sopenharmony_cimachine_crash_shutdown(struct pt_regs *regs) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci if (_machine_crash_shutdown) 15562306a36Sopenharmony_ci _machine_crash_shutdown(regs); 15662306a36Sopenharmony_ci else 15762306a36Sopenharmony_ci default_machine_crash_shutdown(regs); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#ifdef CONFIG_SMP 16162306a36Sopenharmony_civoid kexec_nonboot_cpu_jump(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci local_flush_icache_range((unsigned long)relocated_kexec_smp_wait, 16462306a36Sopenharmony_ci reboot_code_buffer + relocate_new_kernel_size); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci relocated_kexec_smp_wait(NULL); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci#endif 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_civoid kexec_reboot(void) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci void (*do_kexec)(void) __noreturn; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* 17562306a36Sopenharmony_ci * We know we were online, and there will be no incoming IPIs at 17662306a36Sopenharmony_ci * this point. Mark online again before rebooting so that the crash 17762306a36Sopenharmony_ci * analysis tool will see us correctly. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci set_cpu_online(smp_processor_id(), true); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Ensure remote CPUs observe that we're online before rebooting. */ 18262306a36Sopenharmony_ci smp_mb__after_atomic(); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#ifdef CONFIG_SMP 18562306a36Sopenharmony_ci if (smp_processor_id() > 0) { 18662306a36Sopenharmony_ci /* 18762306a36Sopenharmony_ci * Instead of cpu_relax() or wait, this is needed for kexec 18862306a36Sopenharmony_ci * smp reboot. Kdump usually doesn't require an smp new 18962306a36Sopenharmony_ci * kernel, but kexec may do. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci kexec_nonboot_cpu(); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* NOTREACHED */ 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci#endif 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * Make sure we get correct instructions written by the 19962306a36Sopenharmony_ci * machine_kexec() CPU. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci local_flush_icache_range(reboot_code_buffer, 20262306a36Sopenharmony_ci reboot_code_buffer + relocate_new_kernel_size); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci do_kexec = (void *)reboot_code_buffer; 20562306a36Sopenharmony_ci do_kexec(); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_civoid 20962306a36Sopenharmony_cimachine_kexec(struct kimage *image) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci unsigned long entry; 21262306a36Sopenharmony_ci unsigned long *ptr; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci reboot_code_buffer = 21562306a36Sopenharmony_ci (unsigned long)page_address(image->control_code_page); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci kexec_start_address = 21862306a36Sopenharmony_ci (unsigned long) phys_to_virt(image->start); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (image->type == KEXEC_TYPE_DEFAULT) { 22162306a36Sopenharmony_ci kexec_indirection_page = 22262306a36Sopenharmony_ci (unsigned long) phys_to_virt(image->head & PAGE_MASK); 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci kexec_indirection_page = (unsigned long)&image->head; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci memcpy((void*)reboot_code_buffer, relocate_new_kernel, 22862306a36Sopenharmony_ci relocate_new_kernel_size); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * The generic kexec code builds a page list with physical 23262306a36Sopenharmony_ci * addresses. they are directly accessible through KSEG0 (or 23362306a36Sopenharmony_ci * CKSEG0 or XPHYS if on 64bit system), hence the 23462306a36Sopenharmony_ci * phys_to_virt() call. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci for (ptr = &image->head; (entry = *ptr) && !(entry &IND_DONE); 23762306a36Sopenharmony_ci ptr = (entry & IND_INDIRECTION) ? 23862306a36Sopenharmony_ci phys_to_virt(entry & PAGE_MASK) : ptr + 1) { 23962306a36Sopenharmony_ci if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || 24062306a36Sopenharmony_ci *ptr & IND_DESTINATION) 24162306a36Sopenharmony_ci *ptr = (unsigned long) phys_to_virt(*ptr); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Mark offline BEFORE disabling local irq. */ 24562306a36Sopenharmony_ci set_cpu_online(smp_processor_id(), false); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * we do not want to be bothered. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci local_irq_disable(); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci printk("Will call new kernel at %08lx\n", image->start); 25362306a36Sopenharmony_ci printk("Bye ...\n"); 25462306a36Sopenharmony_ci /* Make reboot code buffer available to the boot CPU. */ 25562306a36Sopenharmony_ci __flush_cache_all(); 25662306a36Sopenharmony_ci#ifdef CONFIG_SMP 25762306a36Sopenharmony_ci /* All secondary cpus now may jump to kexec_wait cycle */ 25862306a36Sopenharmony_ci relocated_kexec_smp_wait = reboot_code_buffer + 25962306a36Sopenharmony_ci (void *)(kexec_smp_wait - relocate_new_kernel); 26062306a36Sopenharmony_ci smp_wmb(); 26162306a36Sopenharmony_ci atomic_set(&kexec_ready_to_reboot, 1); 26262306a36Sopenharmony_ci#endif 26362306a36Sopenharmony_ci kexec_reboot(); 26462306a36Sopenharmony_ci} 265