On Tue, Aug 12, 2025 at 9:22 AM Youling Tang <youling.t...@linux.dev> wrote: > > Hi, Huacai > On 2025/8/11 22:07, Huacai Chen wrote: > > Hi, Youling, > > > > On Mon, Aug 11, 2025 at 5:28 PM Youling Tang <youling.t...@linux.dev> wrote: > >> From: Youling Tang <tangyoul...@kylinos.cn> > >> > >> This patch adds support for kexec_file on LoongArch. > >> > >> The image_load() as two parts: > >> - the first part loads the kernel image (vmlinuz.efi or vmlinux.efi) > >> - the second part loads other segments (eg: initrd, cmdline) > >> > >> Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are > >> supported, > >> but ELF format is not supported. > >> > >> Signed-off-by: Youling Tang <tangyoul...@kylinos.cn> > >> --- > >> arch/loongarch/Kconfig | 8 ++ > >> arch/loongarch/include/asm/image.h | 18 ++++ > >> arch/loongarch/include/asm/kexec.h | 12 +++ > >> arch/loongarch/kernel/Makefile | 1 + > >> arch/loongarch/kernel/kexec_image.c | 112 +++++++++++++++++++++ > >> arch/loongarch/kernel/machine_kexec.c | 33 ++++-- > >> arch/loongarch/kernel/machine_kexec_file.c | 46 +++++++++ > >> 7 files changed, 219 insertions(+), 11 deletions(-) > >> create mode 100644 arch/loongarch/kernel/kexec_image.c > >> create mode 100644 arch/loongarch/kernel/machine_kexec_file.c > >> > >> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig > >> index f0abc38c40ac..fd50c83f7827 100644 > >> --- a/arch/loongarch/Kconfig > >> +++ b/arch/loongarch/Kconfig > >> @@ -625,6 +625,14 @@ config CPU_HAS_PREFETCH > >> config ARCH_SUPPORTS_KEXEC > >> def_bool y > >> > >> +config ARCH_SUPPORTS_KEXEC_FILE > >> + def_bool 64BIT > >> + > >> +config ARCH_SELECTS_KEXEC_FILE > >> + def_bool y > >> + depends on KEXEC_FILE > >> + select HAVE_IMA_KEXEC if IMA > >> + > >> config ARCH_SUPPORTS_CRASH_DUMP > >> def_bool y > >> > >> diff --git a/arch/loongarch/include/asm/image.h > >> b/arch/loongarch/include/asm/image.h > >> index 1f090736e71d..829e1ecb1f5d 100644 > >> --- a/arch/loongarch/include/asm/image.h > >> +++ b/arch/loongarch/include/asm/image.h > >> @@ -36,5 +36,23 @@ struct loongarch_image_header { > >> uint32_t pe_header; > >> }; > >> > >> +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'}; > >> +static const uint8_t loongarch_pe_machtype[6] = {'P', 'E', 0x0, 0x0, > >> 0x64, 0x62}; > >> + > >> +/** > >> + * loongarch_header_check_pe_sig - Helper to check the loongarch image > >> header. > >> + * > >> + * Returns non-zero if 'MZ' signature is found. > >> + */ > >> + > >> +static inline int loongarch_header_check_pe_sig(const struct > >> loongarch_image_header *h) > >> +{ > >> + if (!h) > >> + return 0; > >> + > >> + return (h->pe_sig[0] == loongarch_image_pe_sig[0] > >> + && h->pe_sig[1] == loongarch_image_pe_sig[1]); > >> +} > >> + > >> #endif /* __ASSEMBLY__ */ > >> #endif /* __ASM_IMAGE_H */ > >> diff --git a/arch/loongarch/include/asm/kexec.h > >> b/arch/loongarch/include/asm/kexec.h > >> index cf95cd3eb2de..3ef8517a3670 100644 > >> --- a/arch/loongarch/include/asm/kexec.h > >> +++ b/arch/loongarch/include/asm/kexec.h > >> @@ -41,6 +41,18 @@ struct kimage_arch { > >> unsigned long systable_ptr; > >> }; > >> > >> +#ifdef CONFIG_KEXEC_FILE > >> +extern const struct kexec_file_ops kexec_image_ops; > >> + > >> +int arch_kimage_file_post_load_cleanup(struct kimage *image); > >> +#define arch_kimage_file_post_load_cleanup > >> arch_kimage_file_post_load_cleanup > >> + > >> +extern int load_other_segments(struct kimage *image, > >> + unsigned long kernel_load_addr, unsigned long kernel_size, > >> + char *initrd, unsigned long initrd_len, > >> + char *cmdline, unsigned long cmdline_len); > > I think the RISC-V naming "load_extra_segments" is better. > This name is also fine, but I prefer it to be consistent with > that in kexec-tools. OK, then you can keep your original name.
> > > >> +#endif > >> + > >> typedef void (*do_kexec_t)(unsigned long efi_boot, > >> unsigned long cmdline_ptr, > >> unsigned long systable_ptr, > >> diff --git a/arch/loongarch/kernel/Makefile > >> b/arch/loongarch/kernel/Makefile > >> index 6f5a4574a911..bd9405ee3888 100644 > >> --- a/arch/loongarch/kernel/Makefile > >> +++ b/arch/loongarch/kernel/Makefile > >> @@ -62,6 +62,7 @@ obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o > >> obj-$(CONFIG_RELOCATABLE) += relocate.o > >> > >> obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o > >> +obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o > > We only support the efi format, so we don't need to split a > > kexec_image.c like RISC-V, just put everything into > > machine_kexec_file.c is OK. > I hope it is separated and consistent with other architectures. > For instance, arm64 only supports one type. It seems only ARM64 and RISC-V are consistent, x86/powerpc/parisc are not. Huacai > > Youling. > > > > Huacai > > > >> obj-$(CONFIG_CRASH_DUMP) += crash_dump.o > >> > >> obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o > >> diff --git a/arch/loongarch/kernel/kexec_image.c > >> b/arch/loongarch/kernel/kexec_image.c > >> new file mode 100644 > >> index 000000000000..fdd1845b4e2e > >> --- /dev/null > >> +++ b/arch/loongarch/kernel/kexec_image.c > >> @@ -0,0 +1,112 @@ > >> +// SPDX-License-Identifier: GPL-2.0 > >> +/* > >> + * Kexec image loader for LoongArch > >> + > >> + * Author: Youling Tang <tangyoul...@kylinos.cn> > >> + * Copyright (C) 2025 KylinSoft Corporation. > >> + */ > >> + > >> +#define pr_fmt(fmt) "kexec_file(Image): " fmt > >> + > >> +#include <linux/err.h> > >> +#include <linux/errno.h> > >> +#include <linux/kernel.h> > >> +#include <linux/kexec.h> > >> +#include <linux/pe.h> > >> +#include <linux/string.h> > >> +#include <asm/byteorder.h> > >> +#include <asm/cpufeature.h> > >> +#include <asm/image.h> > >> + > >> +static int image_probe(const char *kernel_buf, unsigned long kernel_len) > >> +{ > >> + const struct loongarch_image_header *h = > >> + (const struct loongarch_image_header *)(kernel_buf); > >> + > >> + if (!h || (kernel_len < sizeof(*h))) { > >> + pr_err("No loongarch image header.\n"); > >> + return -EINVAL; > >> + } > >> + > >> + if (!loongarch_header_check_pe_sig(h)) { > >> + pr_err("Bad loongarch PE image header.\n"); > >> + return -EINVAL; > >> + } > >> + > >> + return 0; > >> +} > >> + > >> +static void *image_load(struct kimage *image, > >> + char *kernel, unsigned long kernel_len, > >> + char *initrd, unsigned long initrd_len, > >> + char *cmdline, unsigned long cmdline_len) > >> +{ > >> + struct loongarch_image_header *h; > >> + struct kexec_buf kbuf; > >> + unsigned long text_offset, kernel_segment_number; > >> + struct kexec_segment *kernel_segment; > >> + int ret; > >> + > >> + h = (struct loongarch_image_header *)kernel; > >> + if (!h->image_size) > >> + return ERR_PTR(-EINVAL); > >> + > >> + /* Load the kernel */ > >> + kbuf.image = image; > >> + kbuf.buf_min = 0; > >> + kbuf.buf_max = ULONG_MAX; > >> + kbuf.top_down = false; > >> + > >> + kbuf.buffer = kernel; > >> + kbuf.bufsz = kernel_len; > >> + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > >> + kbuf.memsz = le64_to_cpu(h->image_size); > >> + text_offset = le64_to_cpu(h->text_offset); > >> + kbuf.buf_align = SZ_2M; > >> + > >> + kernel_segment_number = image->nr_segments; > >> + > >> + /* > >> + * The location of the kernel segment may make it impossible to > >> satisfy > >> + * the other segment requirements, so we try repeatedly to find a > >> + * location that will work. > >> + */ > >> + while ((ret = kexec_add_buffer(&kbuf)) == 0) { > >> + /* Try to load additional data */ > >> + kernel_segment = &image->segment[kernel_segment_number]; > >> + ret = load_other_segments(image, kernel_segment->mem, > >> + kernel_segment->memsz, initrd, > >> + initrd_len, cmdline, > >> cmdline_len); > >> + if (!ret) > >> + break; > >> + > >> + /* > >> + * We couldn't find space for the other segments; erase the > >> + * kernel segment and try the next available hole. > >> + */ > >> + image->nr_segments -= 1; > >> + kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz; > >> + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > >> + } > >> + > >> + if (ret) { > >> + pr_err("Could not find any suitable kernel location!"); > >> + return ERR_PTR(ret); > >> + } > >> + > >> + kernel_segment = &image->segment[kernel_segment_number]; > >> + > >> + /* Make sure the second kernel jumps to the correct > >> "kernel_entry". */ > >> + image->start = kernel_segment->mem + h->kernel_entry - text_offset; > >> + > >> + kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n", > >> + kernel_segment->mem, kbuf.bufsz, > >> + kernel_segment->memsz); > >> + > >> + return NULL; > >> +} > >> + > >> +const struct kexec_file_ops kexec_image_ops = { > >> + .probe = image_probe, > >> + .load = image_load, > >> +}; > >> diff --git a/arch/loongarch/kernel/machine_kexec.c > >> b/arch/loongarch/kernel/machine_kexec.c > >> index f9381800e291..008f43e26120 100644 > >> --- a/arch/loongarch/kernel/machine_kexec.c > >> +++ b/arch/loongarch/kernel/machine_kexec.c > >> @@ -70,18 +70,28 @@ int machine_kexec_prepare(struct kimage *kimage) > >> kimage->arch.efi_boot = fw_arg0; > >> kimage->arch.systable_ptr = fw_arg2; > >> > >> - /* Find the command line */ > >> - for (i = 0; i < kimage->nr_segments; i++) { > >> - if (!strncmp(bootloader, (char __user > >> *)kimage->segment[i].buf, strlen(bootloader))) { > >> - if (!copy_from_user(cmdline_ptr, > >> kimage->segment[i].buf, COMMAND_LINE_SIZE)) > >> - kimage->arch.cmdline_ptr = (unsigned > >> long)cmdline_ptr; > >> - break; > >> + if (kimage->file_mode == 1) { > >> + /* > >> + * kimage->cmdline_buf will be released in > >> kexec_file_load, so copy to > >> + * the KEXEC_CMDLINE_ADDR safe area. > >> + */ > >> + memcpy((void *)KEXEC_CMDLINE_ADDR, (void > >> *)kimage->arch.cmdline_ptr, > >> + strlen((char > >> *)kimage->arch.cmdline_ptr) + 1); > >> + kimage->arch.cmdline_ptr = (unsigned > >> long)KEXEC_CMDLINE_ADDR; > >> + } else { > >> + /* Find the command line */ > >> + for (i = 0; i < kimage->nr_segments; i++) { > >> + if (!strncmp(bootloader, (char __user > >> *)kimage->segment[i].buf, strlen(bootloader))) { > >> + if (!copy_from_user(cmdline_ptr, > >> kimage->segment[i].buf, COMMAND_LINE_SIZE)) > >> + kimage->arch.cmdline_ptr = > >> (unsigned long)cmdline_ptr; > >> + break; > >> + } > >> } > >> - } > >> > >> - if (!kimage->arch.cmdline_ptr) { > >> - pr_err("Command line not included in the provided > >> image\n"); > >> - return -EINVAL; > >> + if (!kimage->arch.cmdline_ptr) { > >> + pr_err("Command line not included in the provided > >> image\n"); > >> + return -EINVAL; > >> + } > >> } > >> > >> /* kexec/kdump need a safe page to save reboot_code_buffer */ > >> @@ -288,7 +298,8 @@ void machine_kexec(struct kimage *image) > >> local_irq_disable(); > >> > >> pr_notice("EFI boot flag 0x%lx\n", efi_boot); > >> - pr_notice("Command line at 0x%lx\n", cmdline_ptr); > >> + pr_notice("Command line addr at 0x%lx\n", cmdline_ptr); > >> + pr_notice("Command line at %s\n", (char *)cmdline_ptr); > >> pr_notice("System table at 0x%lx\n", systable_ptr); > >> pr_notice("We will call new kernel at 0x%lx\n", start_addr); > >> pr_notice("Bye ...\n"); > >> diff --git a/arch/loongarch/kernel/machine_kexec_file.c > >> b/arch/loongarch/kernel/machine_kexec_file.c > >> new file mode 100644 > >> index 000000000000..bc91ae0afa4c > >> --- /dev/null > >> +++ b/arch/loongarch/kernel/machine_kexec_file.c > >> @@ -0,0 +1,46 @@ > >> +// SPDX-License-Identifier: GPL-2.0 > >> +/* > >> + * kexec_file for LoongArch > >> + * > >> + * Author: Youling Tang <tangyoul...@kylinos.cn> > >> + * Copyright (C) 2025 KylinSoft Corporation. > >> + * > >> + * Most code is derived from LoongArch port of kexec-tools > >> + */ > >> + > >> +#define pr_fmt(fmt) "kexec_file: " fmt > >> + > >> +#include <linux/ioport.h> > >> +#include <linux/kernel.h> > >> +#include <linux/kexec.h> > >> +#include <linux/memblock.h> > >> +#include <linux/slab.h> > >> +#include <linux/string.h> > >> +#include <linux/types.h> > >> +#include <linux/vmalloc.h> > >> +#include <asm/bootinfo.h> > >> + > >> +const struct kexec_file_ops * const kexec_file_loaders[] = { > >> + &kexec_image_ops, > >> + NULL > >> +}; > >> + > >> +int arch_kimage_file_post_load_cleanup(struct kimage *image) > >> +{ > >> + vfree(image->elf_headers); > >> + image->elf_headers = NULL; > >> + image->elf_headers_sz = 0; > >> + > >> + return kexec_image_post_load_cleanup_default(image); > >> +} > >> + > >> +int load_other_segments(struct kimage *image, > >> + unsigned long kernel_load_addr, > >> + unsigned long kernel_size, > >> + char *initrd, unsigned long initrd_len, > >> + char *cmdline, unsigned long cmdline_len) > >> +{ > >> + image->arch.cmdline_ptr = (unsigned long)cmdline; > >> + > >> + return 0; > >> +} > >> -- > >> 2.34.1 > >>