// SPDX-License-Identifier: GPL-2.0 #include <linux/kernel.h> #include <linux/console.h> #include <linux/kexec.h> #include <linux/delay.h> #include <linux/reboot.h> #include <asm/cacheflush.h> #include <asm/sections.h> extern void relocate_new_kernel(unsigned long head, unsigned long start, unsigned long phys); extern const unsigned int relocate_new_kernel_size; extern unsigned int kexec_initrd_start_offset; extern unsigned int kexec_initrd_end_offset; extern unsigned int kexec_cmdline_offset; extern unsigned int kexec_free_mem_offset; static void kexec_show_segment_info(const struct kimage *kimage, unsigned long n) { pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n", n, kimage->segment[n].mem, kimage->segment[n].mem + kimage->segment[n].memsz, (unsigned long)kimage->segment[n].memsz, (unsigned long)kimage->segment[n].memsz / PAGE_SIZE); } static void kexec_image_info(const struct kimage *kimage) { unsigned long i; pr_debug("kexec kimage info:\n"); pr_debug(" type: %d\n", kimage->type); pr_debug(" start: %lx\n", kimage->start); pr_debug(" head: %lx\n", kimage->head); pr_debug(" nr_segments: %lu\n", kimage->nr_segments); for (i = 0; i < kimage->nr_segments; i++) kexec_show_segment_info(kimage, i); #ifdef CONFIG_KEXEC_FILE if (kimage->file_mode) { pr_debug("cmdline: %.*s\n", (int)kimage->cmdline_buf_len, kimage->cmdline_buf); } #endif } void machine_kexec_cleanup(struct kimage *kimage) { } void machine_crash_shutdown(struct pt_regs *regs) { } void machine_shutdown(void) { smp_send_stop(); while (num_online_cpus() > 1) { cpu_relax(); mdelay(1); } } void machine_kexec(struct kimage *image) { #ifdef CONFIG_64BIT Elf64_Fdesc desc; #endif void (*reloc)(unsigned long head, unsigned long start, unsigned long phys); unsigned long phys = page_to_phys(image->control_code_page); void *virt = (void *)__fix_to_virt(FIX_TEXT_KEXEC); struct kimage_arch *arch = &image->arch; set_fixmap(FIX_TEXT_KEXEC, phys); flush_cache_all(); #ifdef CONFIG_64BIT reloc = (void *)&desc; desc.addr = (long long)virt; #else reloc = (void *)virt; #endif memcpy(virt, dereference_function_descriptor(relocate_new_kernel), relocate_new_kernel_size); *(unsigned long *)(virt + kexec_cmdline_offset) = arch->cmdline; *(unsigned long *)(virt + kexec_initrd_start_offset) = arch->initrd_start; *(unsigned long *)(virt + kexec_initrd_end_offset) = arch->initrd_end; *(unsigned long *)(virt + kexec_free_mem_offset) = PAGE0->mem_free; flush_cache_all(); flush_tlb_all(); local_irq_disable(); reloc(image->head & PAGE_MASK, image->start, phys); } int machine_kexec_prepare(struct kimage *image) { kexec_image_info(image); return 0; }