2019-09-08 09:33:04 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/console.h>
|
|
|
|
#include <linux/kexec.h>
|
|
|
|
#include <linux/delay.h>
|
2023-05-09 07:01:50 +00:00
|
|
|
#include <linux/reboot.h>
|
|
|
|
|
2019-09-08 09:33:04 +00:00
|
|
|
#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);
|
2019-09-08 09:33:06 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_KEXEC_FILE
|
|
|
|
if (kimage->file_mode) {
|
|
|
|
pr_debug("cmdline: %.*s\n", (int)kimage->cmdline_buf_len,
|
|
|
|
kimage->cmdline_buf);
|
|
|
|
}
|
|
|
|
#endif
|
2019-09-08 09:33:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|