root/arch/parisc/kernel/kexec.c
// 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;
}