[PATCH v2 2/5] LoongArch: Add kexec_file support

Youling Tang posted 5 patches 1 month, 2 weeks ago
There is a newer version of this series
[PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Youling Tang 1 month, 2 weeks ago
From: Youling Tang <tangyouling@kylinos.cn>

This patch adds support for kexec_file on LoongArch.

The efi_kexec_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)

This initrd will be passed to the second kernel via the command line
'initrd=start,size'.

Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are supported,
but ELF format is not supported.

Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
---
 arch/loongarch/Kconfig                     |   9 ++
 arch/loongarch/include/asm/image.h         |  17 +++
 arch/loongarch/include/asm/kexec.h         |  12 +++
 arch/loongarch/kernel/Makefile             |   1 +
 arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
 arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
 arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
 7 files changed, 289 insertions(+), 11 deletions(-)
 create mode 100644 arch/loongarch/kernel/kexec_efi.c
 create mode 100644 arch/loongarch/kernel/machine_kexec_file.c

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index f0abc38c40ac..490dc6eed749 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -625,6 +625,15 @@ 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
+	select RELOCATABLE
+
 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..655d5836c4e8 100644
--- a/arch/loongarch/include/asm/image.h
+++ b/arch/loongarch/include/asm/image.h
@@ -36,5 +36,22 @@ struct loongarch_image_header {
 	uint32_t pe_header;
 };
 
+static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
+
+/**
+ * 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..34799db933fb 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_efi_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);
+#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..dd6183f353e6 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_efi.o
 obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 
 obj-$(CONFIG_UNWINDER_GUESS)	+= unwind_guess.o
diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
new file mode 100644
index 000000000000..7741f1139a12
--- /dev/null
+++ b/arch/loongarch/kernel/kexec_efi.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Load EFI vmlinux file for the kexec_file_load syscall.
+ *
+ * Author: Youling Tang <tangyouling@kylinos.cn>
+ * Copyright (C) 2025 KylinSoft Corporation.
+ */
+
+#define pr_fmt(fmt)	"kexec_file(EFI): " 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 efi_kexec_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_warn("No loongarch PE image header.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *efi_kexec_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_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_min = 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_efi_ops = {
+	.probe = efi_kexec_probe,
+	.load = efi_kexec_load,
+};
diff --git a/arch/loongarch/kernel/machine_kexec.c b/arch/loongarch/kernel/machine_kexec.c
index f9381800e291..ba8d64bc6549 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: %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..a713acf32db8
--- /dev/null
+++ b/arch/loongarch/kernel/machine_kexec_file.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * kexec_file for LoongArch
+ *
+ * Author: Youling Tang <tangyouling@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_efi_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);
+}
+
+/* Adds the "initrd=start,size" command line parameter to command line. */
+static void cmdline_add_initrd(struct kimage *image, unsigned long *cmdline_tmplen,
+				char *modified_cmdline, unsigned long initrd)
+{
+	int initrd_strlen;
+
+	initrd_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "initrd=0x%lx,0x%lx ",
+		initrd, image->initrd_buf_len);
+	*cmdline_tmplen += initrd_strlen;
+}
+
+/*
+ * Tries to add the initrd to the image. If it is not possible to find
+ * valid locations, this function will undo changes to the image and return non
+ * zero.
+ */
+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)
+{
+	struct kexec_buf kbuf;
+	unsigned long orig_segments = image->nr_segments;
+	char *modified_cmdline = NULL;
+	unsigned long cmdline_tmplen = 0;
+	unsigned long initrd_load_addr = 0;
+	int ret = 0;
+
+
+	kbuf.image = image;
+	/* not allocate anything below the kernel */
+	kbuf.buf_min = kernel_load_addr + kernel_size;
+
+	modified_cmdline = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
+	if (!modified_cmdline)
+		return -EINVAL;
+
+	/* Ensure it's nul terminated */
+	modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+
+	/* load initrd */
+	if (initrd) {
+		kbuf.buffer = initrd;
+		kbuf.bufsz = initrd_len;
+		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
+		kbuf.memsz = initrd_len;
+		kbuf.buf_align = 0;
+		/* within 1GB-aligned window of up to 32GB in size */
+		kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
+						+ (unsigned long)SZ_1G * 32;
+		kbuf.top_down = false;
+
+		ret = kexec_add_buffer(&kbuf);
+		if (ret)
+			goto out_err;
+		initrd_load_addr = kbuf.mem;
+
+		kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
+			      initrd_load_addr, kbuf.bufsz, kbuf.memsz);
+
+		/* Add the initrd=start,size parameter to the command line */
+		cmdline_add_initrd(image, &cmdline_tmplen, modified_cmdline, initrd_load_addr);
+	}
+
+	if (cmdline_len + cmdline_tmplen > COMMAND_LINE_SIZE) {
+		pr_err("Appending command line exceeds cmdline size\n");
+		ret = -EINVAL;
+		goto out_err;
+	}
+	memcpy(modified_cmdline + cmdline_tmplen, cmdline, cmdline_len);
+	cmdline = modified_cmdline;
+	image->arch.cmdline_ptr = (unsigned long)cmdline;
+
+	return 0;
+
+out_err:
+	image->nr_segments = orig_segments;
+	kfree(modified_cmdline);
+	return ret;
+}
-- 
2.43.0
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by kernel test robot 1 month, 1 week ago
Hi Youling,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.17-rc3 next-20250825]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Youling-Tang/LoongArch-Add-struct-loongarch_image_header-for-kernel-image/20250820-140025
base:   linus/master
patch link:    https://lore.kernel.org/r/20250820055700.24344-3-youling.tang%40linux.dev
patch subject: [PATCH v2 2/5] LoongArch: Add kexec_file support
config: loongarch-randconfig-r121-20250826 (https://download.01.org/0day-ci/archive/20250826/202508260914.Bjz2YQwS-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce: (https://download.01.org/0day-ci/archive/20250826/202508260914.Bjz2YQwS-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508260914.Bjz2YQwS-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> arch/loongarch/kernel/kexec_efi.c:61:22: sparse: sparse: cast to restricted __le64
   arch/loongarch/kernel/kexec_efi.c:62:23: sparse: sparse: cast to restricted __le64

vim +61 arch/loongarch/kernel/kexec_efi.c

    37	
    38	static void *efi_kexec_load(struct kimage *image,
    39					char *kernel, unsigned long kernel_len,
    40					char *initrd, unsigned long initrd_len,
    41					char *cmdline, unsigned long cmdline_len)
    42	{
    43		struct loongarch_image_header *h;
    44		struct kexec_buf kbuf;
    45		unsigned long text_offset, kernel_segment_number;
    46		struct kexec_segment *kernel_segment;
    47		int ret;
    48	
    49		h = (struct loongarch_image_header *)kernel;
    50		if (!h->image_size)
    51			return ERR_PTR(-EINVAL);
    52	
    53		/* Load the kernel */
    54		kbuf.image = image;
    55		kbuf.buf_max = ULONG_MAX;
    56		kbuf.top_down = false;
    57	
    58		kbuf.buffer = kernel;
    59		kbuf.bufsz = kernel_len;
    60		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
  > 61		kbuf.memsz = le64_to_cpu(h->image_size);
    62		text_offset = le64_to_cpu(h->text_offset);
    63		kbuf.buf_min = text_offset;
    64		kbuf.buf_align = SZ_2M;
    65	
    66		kernel_segment_number = image->nr_segments;
    67	
    68		/*
    69		 * The location of the kernel segment may make it impossible to satisfy
    70		 * the other segment requirements, so we try repeatedly to find a
    71		 * location that will work.
    72		 */
    73		while ((ret = kexec_add_buffer(&kbuf)) == 0) {
    74			/* Try to load additional data */
    75			kernel_segment = &image->segment[kernel_segment_number];
    76			ret = load_other_segments(image, kernel_segment->mem,
    77						  kernel_segment->memsz, initrd,
    78						  initrd_len, cmdline, cmdline_len);
    79			if (!ret)
    80				break;
    81	
    82			/*
    83			 * We couldn't find space for the other segments; erase the
    84			 * kernel segment and try the next available hole.
    85			 */
    86			image->nr_segments -= 1;
    87			kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz;
    88			kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
    89		}
    90	
    91		if (ret) {
    92			pr_err("Could not find any suitable kernel location!");
    93			return ERR_PTR(ret);
    94		}
    95	
    96		kernel_segment = &image->segment[kernel_segment_number];
    97	
    98		/* Make sure the second kernel jumps to the correct "kernel_entry". */
    99		image->start = kernel_segment->mem + h->kernel_entry - text_offset;
   100	
   101		kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
   102			      kernel_segment->mem, kbuf.bufsz,
   103			      kernel_segment->memsz);
   104	
   105		return NULL;
   106	}
   107	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Huacai Chen 1 month, 1 week ago
Hi, Youling,

On Wed, Aug 20, 2025 at 1:57 PM Youling Tang <youling.tang@linux.dev> wrote:
>
> From: Youling Tang <tangyouling@kylinos.cn>
>
> This patch adds support for kexec_file on LoongArch.
>
> The efi_kexec_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)
>
> This initrd will be passed to the second kernel via the command line
> 'initrd=start,size'.
>
> Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are supported,
> but ELF format is not supported.
>
> Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
> ---
>  arch/loongarch/Kconfig                     |   9 ++
>  arch/loongarch/include/asm/image.h         |  17 +++
>  arch/loongarch/include/asm/kexec.h         |  12 +++
>  arch/loongarch/kernel/Makefile             |   1 +
>  arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
>  arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
>  arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
>  7 files changed, 289 insertions(+), 11 deletions(-)
>  create mode 100644 arch/loongarch/kernel/kexec_efi.c
>  create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
>
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index f0abc38c40ac..490dc6eed749 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -625,6 +625,15 @@ 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
> +       select RELOCATABLE
> +
>  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..655d5836c4e8 100644
> --- a/arch/loongarch/include/asm/image.h
> +++ b/arch/loongarch/include/asm/image.h
> @@ -36,5 +36,22 @@ struct loongarch_image_header {
>         uint32_t pe_header;
>  };
>
> +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
> +
> +/**
> + * 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..34799db933fb 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_efi_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);
> +#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..dd6183f353e6 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_efi.o
>  obj-$(CONFIG_CRASH_DUMP)       += crash_dump.o
>
>  obj-$(CONFIG_UNWINDER_GUESS)   += unwind_guess.o
> diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
> new file mode 100644
> index 000000000000..7741f1139a12
> --- /dev/null
> +++ b/arch/loongarch/kernel/kexec_efi.c
> @@ -0,0 +1,111 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Load EFI vmlinux file for the kexec_file_load syscall.
> + *
> + * Author: Youling Tang <tangyouling@kylinos.cn>
> + * Copyright (C) 2025 KylinSoft Corporation.
> + */
> +
> +#define pr_fmt(fmt)    "kexec_file(EFI): " 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 efi_kexec_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_warn("No loongarch PE image header.\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static void *efi_kexec_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_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_min = 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_efi_ops = {
> +       .probe = efi_kexec_probe,
> +       .load = efi_kexec_load,
> +};
> diff --git a/arch/loongarch/kernel/machine_kexec.c b/arch/loongarch/kernel/machine_kexec.c
> index f9381800e291..ba8d64bc6549 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: %s\n", (char *)cmdline_ptr);
Change to be like this is better:
pr_notice("EFI boot flag: 0x%lx\n", efi_boot);
pr_notice("Command line addr: 0x%lx\n", cmdline_ptr);
pr_notice("Command line string: %s\n", (char *)cmdline_ptr);

Huacai


>         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..a713acf32db8
> --- /dev/null
> +++ b/arch/loongarch/kernel/machine_kexec_file.c
> @@ -0,0 +1,117 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * kexec_file for LoongArch
> + *
> + * Author: Youling Tang <tangyouling@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_efi_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);
> +}
> +
> +/* Adds the "initrd=start,size" command line parameter to command line. */
> +static void cmdline_add_initrd(struct kimage *image, unsigned long *cmdline_tmplen,
> +                               char *modified_cmdline, unsigned long initrd)
> +{
> +       int initrd_strlen;
> +
> +       initrd_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "initrd=0x%lx,0x%lx ",
> +               initrd, image->initrd_buf_len);
> +       *cmdline_tmplen += initrd_strlen;
> +}
> +
> +/*
> + * Tries to add the initrd to the image. If it is not possible to find
> + * valid locations, this function will undo changes to the image and return non
> + * zero.
> + */
> +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)
> +{
> +       struct kexec_buf kbuf;
> +       unsigned long orig_segments = image->nr_segments;
> +       char *modified_cmdline = NULL;
> +       unsigned long cmdline_tmplen = 0;
> +       unsigned long initrd_load_addr = 0;
> +       int ret = 0;
> +
> +
> +       kbuf.image = image;
> +       /* not allocate anything below the kernel */
> +       kbuf.buf_min = kernel_load_addr + kernel_size;
> +
> +       modified_cmdline = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
> +       if (!modified_cmdline)
> +               return -EINVAL;
> +
> +       /* Ensure it's nul terminated */
> +       modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
> +
> +       /* load initrd */
> +       if (initrd) {
> +               kbuf.buffer = initrd;
> +               kbuf.bufsz = initrd_len;
> +               kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
> +               kbuf.memsz = initrd_len;
> +               kbuf.buf_align = 0;
> +               /* within 1GB-aligned window of up to 32GB in size */
> +               kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
> +                                               + (unsigned long)SZ_1G * 32;
> +               kbuf.top_down = false;
> +
> +               ret = kexec_add_buffer(&kbuf);
> +               if (ret)
> +                       goto out_err;
> +               initrd_load_addr = kbuf.mem;
> +
> +               kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
> +                             initrd_load_addr, kbuf.bufsz, kbuf.memsz);
> +
> +               /* Add the initrd=start,size parameter to the command line */
> +               cmdline_add_initrd(image, &cmdline_tmplen, modified_cmdline, initrd_load_addr);
> +       }
> +
> +       if (cmdline_len + cmdline_tmplen > COMMAND_LINE_SIZE) {
> +               pr_err("Appending command line exceeds cmdline size\n");
> +               ret = -EINVAL;
> +               goto out_err;
> +       }
> +       memcpy(modified_cmdline + cmdline_tmplen, cmdline, cmdline_len);
> +       cmdline = modified_cmdline;
> +       image->arch.cmdline_ptr = (unsigned long)cmdline;
> +
> +       return 0;
> +
> +out_err:
> +       image->nr_segments = orig_segments;
> +       kfree(modified_cmdline);
> +       return ret;
> +}
> --
> 2.43.0
>
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Yao Zi 1 month, 2 weeks ago
On Wed, Aug 20, 2025 at 01:56:57PM +0800, Youling Tang wrote:
> From: Youling Tang <tangyouling@kylinos.cn>
> 
> This patch adds support for kexec_file on LoongArch.
> 
> The efi_kexec_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)
> 
> This initrd will be passed to the second kernel via the command line
> 'initrd=start,size'.
> 
> Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are supported,
> but ELF format is not supported.
> 
> Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
> ---
>  arch/loongarch/Kconfig                     |   9 ++
>  arch/loongarch/include/asm/image.h         |  17 +++
>  arch/loongarch/include/asm/kexec.h         |  12 +++
>  arch/loongarch/kernel/Makefile             |   1 +
>  arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
>  arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
>  arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
>  7 files changed, 289 insertions(+), 11 deletions(-)
>  create mode 100644 arch/loongarch/kernel/kexec_efi.c
>  create mode 100644 arch/loongarch/kernel/machine_kexec_file.c

...

> diff --git a/arch/loongarch/include/asm/image.h b/arch/loongarch/include/asm/image.h
> index 1f090736e71d..655d5836c4e8 100644
> --- a/arch/loongarch/include/asm/image.h
> +++ b/arch/loongarch/include/asm/image.h
> @@ -36,5 +36,22 @@ struct loongarch_image_header {
>  	uint32_t pe_header;
>  };
>  
> +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
> +
> +/**
> + * 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]);
> +}

This check is still too weak and doesn't improve comparing to v1.

> This could be simplified with a memcmp(). Also, this check isn't
> strict enough: PE files for any architectures, and even legacy MS-DOS
> COM executables all start with "MZ".

I've pointed this out in my previous reply[1].

>  #endif /* __ASSEMBLY__ */
>  #endif /* __ASM_IMAGE_H */

...

> diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
> new file mode 100644
> index 000000000000..7741f1139a12
> --- /dev/null
> +++ b/arch/loongarch/kernel/kexec_efi.c

...

> +static void *efi_kexec_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_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_min = 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;

And this still assumes the loaded, secondary kernel is relocatable,
with neither extra check nor comment explaining its limitation.

Please see my previous reply[2] that explains why loading a
non-relocatble kernel with kexec_file API is reasonable.

> +	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_efi_ops = {
> +	.probe = efi_kexec_probe,
> +	.load = efi_kexec_load,
> +};

Thanks,
Yao Zi

[1]: https://lore.kernel.org/all/aJojDiHWi8cgvA2W@pie/
[2]: https://lore.kernel.org/all/aJwFa8x5BQMouB1y@pie/
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Youling Tang 1 month, 2 weeks ago
Hi, Yao

On 2025/8/20 14:50, Yao Zi wrote:

> On Wed, Aug 20, 2025 at 01:56:57PM +0800, Youling Tang wrote:
>> From: Youling Tang <tangyouling@kylinos.cn>
>>
>> This patch adds support for kexec_file on LoongArch.
>>
>> The efi_kexec_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)
>>
>> This initrd will be passed to the second kernel via the command line
>> 'initrd=start,size'.
>>
>> Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are supported,
>> but ELF format is not supported.
>>
>> Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
>> ---
>>   arch/loongarch/Kconfig                     |   9 ++
>>   arch/loongarch/include/asm/image.h         |  17 +++
>>   arch/loongarch/include/asm/kexec.h         |  12 +++
>>   arch/loongarch/kernel/Makefile             |   1 +
>>   arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
>>   arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
>>   arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
>>   7 files changed, 289 insertions(+), 11 deletions(-)
>>   create mode 100644 arch/loongarch/kernel/kexec_efi.c
>>   create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
> ...
>
>> diff --git a/arch/loongarch/include/asm/image.h b/arch/loongarch/include/asm/image.h
>> index 1f090736e71d..655d5836c4e8 100644
>> --- a/arch/loongarch/include/asm/image.h
>> +++ b/arch/loongarch/include/asm/image.h
>> @@ -36,5 +36,22 @@ struct loongarch_image_header {
>>   	uint32_t pe_header;
>>   };
>>   
>> +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
>> +
>> +/**
>> + * 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]);
>> +}
> This check is still too weak and doesn't improve comparing to v1.
>
>> This could be simplified with a memcmp(). Also, this check isn't
>> strict enough: PE files for any architectures, and even legacy MS-DOS
>> COM executables all start with "MZ".
> I've pointed this out in my previous reply[1].
Previously, I had considered adding a specific LoongArch magic
number (such as "Loongson") in the loongarch_image_header, but
this is incompatible with older versions of the kernel, so it
remains the same without further checks.
>
>>   #endif /* __ASSEMBLY__ */
>>   #endif /* __ASM_IMAGE_H */
> ...
>
>> diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
>> new file mode 100644
>> index 000000000000..7741f1139a12
>> --- /dev/null
>> +++ b/arch/loongarch/kernel/kexec_efi.c
> ...
>
>> +static void *efi_kexec_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_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_min = 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;
> And this still assumes the loaded, secondary kernel is relocatable,
> with neither extra check nor comment explaining its limitation.
>
> Please see my previous reply[2] that explains why loading a
> non-relocatble kernel with kexec_file API is reasonable.
LoongArch is a non-position independent (non-PIE) kernel when
the RELOCATABLE option is not enabled, the kernel contains certain
instructions such as la.abs, which prevent it from being relocated to
arbitrary memory addresses for execution. As a result, limitations
exist that make features like kdump or kexec_file dependent on
the RELOCATABLE option.

Strictly speaking, we need to add additional checks: if the kernel is
non-relocatable, the loading operation should fail directly. For a
running kernel, we can easily determine this by calling
kallsyms_lookup_name("relocate_kernel"). However, for a kernel
that is being loaded but has not yet started execution, it is difficult
to easily determine whether the currently loaded kernel has the
RELOCATABLE configuration option enabled.

For ELF format images, we can determine whether the loaded image
contains the ".la_abs" section in the following way:
static struct mem_shdr *laabs_section(const struct mem_ehdr *ehdr)
{
         struct mem_shdr *shdr, *shdr_end;
         unsigned char *strtab;

         strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
         shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
         for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
                 if (shdr->sh_size &&
                         strcmp((char *)&strtab[shdr->sh_name], 
".la_abs") == 0) {
                         return shdr;
                 }
         }

         return NULL;
}

Thanks,
Youling.
>
>> +	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_efi_ops = {
>> +	.probe = efi_kexec_probe,
>> +	.load = efi_kexec_load,
>> +};
> Thanks,
> Yao Zi
>
> [1]: https://lore.kernel.org/all/aJojDiHWi8cgvA2W@pie/
> [2]: https://lore.kernel.org/all/aJwFa8x5BQMouB1y@pie/
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Youling Tang 1 month, 1 week ago
On 2025/8/20 17:13, Youling Tang wrote:

> Hi, Yao
>
> On 2025/8/20 14:50, Yao Zi wrote:
>
>> On Wed, Aug 20, 2025 at 01:56:57PM +0800, Youling Tang wrote:
>>> From: Youling Tang <tangyouling@kylinos.cn>
>>>
>>> This patch adds support for kexec_file on LoongArch.
>>>
>>> The efi_kexec_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)
>>>
>>> This initrd will be passed to the second kernel via the command line
>>> 'initrd=start,size'.
>>>
>>> Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are 
>>> supported,
>>> but ELF format is not supported.
>>>
>>> Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
>>> ---
>>>   arch/loongarch/Kconfig                     |   9 ++
>>>   arch/loongarch/include/asm/image.h         |  17 +++
>>>   arch/loongarch/include/asm/kexec.h         |  12 +++
>>>   arch/loongarch/kernel/Makefile             |   1 +
>>>   arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
>>>   arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
>>>   arch/loongarch/kernel/machine_kexec_file.c | 117 
>>> +++++++++++++++++++++
>>>   7 files changed, 289 insertions(+), 11 deletions(-)
>>>   create mode 100644 arch/loongarch/kernel/kexec_efi.c
>>>   create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
>> ...
>>
>>> diff --git a/arch/loongarch/include/asm/image.h 
>>> b/arch/loongarch/include/asm/image.h
>>> index 1f090736e71d..655d5836c4e8 100644
>>> --- a/arch/loongarch/include/asm/image.h
>>> +++ b/arch/loongarch/include/asm/image.h
>>> @@ -36,5 +36,22 @@ struct loongarch_image_header {
>>>       uint32_t pe_header;
>>>   };
>>>   +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
>>> +
>>> +/**
>>> + * 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]);
>>> +}
>> This check is still too weak and doesn't improve comparing to v1.
>>
>>> This could be simplified with a memcmp(). Also, this check isn't
>>> strict enough: PE files for any architectures, and even legacy MS-DOS
>>> COM executables all start with "MZ".
>> I've pointed this out in my previous reply[1].
> Previously, I had considered adding a specific LoongArch magic
> number (such as "Loongson") in the loongarch_image_header, but
> this is incompatible with older versions of the kernel, so it
> remains the same without further checks.
>>
>>>   #endif /* __ASSEMBLY__ */
>>>   #endif /* __ASM_IMAGE_H */
>> ...
>>
>>> diff --git a/arch/loongarch/kernel/kexec_efi.c 
>>> b/arch/loongarch/kernel/kexec_efi.c
>>> new file mode 100644
>>> index 000000000000..7741f1139a12
>>> --- /dev/null
>>> +++ b/arch/loongarch/kernel/kexec_efi.c
>> ...
>>
>>> +static void *efi_kexec_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_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_min = 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;
>> And this still assumes the loaded, secondary kernel is relocatable,
>> with neither extra check nor comment explaining its limitation.
>>
>> Please see my previous reply[2] that explains why loading a
>> non-relocatble kernel with kexec_file API is reasonable.
> LoongArch is a non-position independent (non-PIE) kernel when
> the RELOCATABLE option is not enabled, the kernel contains certain
> instructions such as la.abs, which prevent it from being relocated to
> arbitrary memory addresses for execution. As a result, limitations
> exist that make features like kdump or kexec_file dependent on
> the RELOCATABLE option.
>
> Strictly speaking, we need to add additional checks: if the kernel is
> non-relocatable, the loading operation should fail directly. For a
> running kernel, we can easily determine this by calling
> kallsyms_lookup_name("relocate_kernel"). However, for a kernel
> that is being loaded but has not yet started execution, it is difficult
> to easily determine whether the currently loaded kernel has the
> RELOCATABLE configuration option enabled.
>
> For ELF format images, we can determine whether the loaded image
> contains the ".la_abs" section in the following way:
> static struct mem_shdr *laabs_section(const struct mem_ehdr *ehdr)
> {
>         struct mem_shdr *shdr, *shdr_end;
>         unsigned char *strtab;
>
>         strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
>         shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
>         for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
>                 if (shdr->sh_size &&
>                         strcmp((char *)&strtab[shdr->sh_name], 
> ".la_abs") == 0) {
>                         return shdr;
>                 }
>         }
>
>         return NULL;
> }
I attempted to parse the pe header to obtain the sections information
and found that there were only two sections, '.text' and '.data'. We
cannot parse whether there is a '.la_abs' section like in the ELF format.

The reason is that when generating vmlinux.efi, when the ELF vmlinux
is converted to the original binary file through the 'objdump -O binary'
operation (arch/loongarch/boot/Makefile), the remaining sections are
merged into the '.text' and '.data' sections.

Youling.
>
> Thanks,
> Youling.
>>
>>> +    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_efi_ops = {
>>> +    .probe = efi_kexec_probe,
>>> +    .load = efi_kexec_load,
>>> +};
>> Thanks,
>> Yao Zi
>>
>> [1]: https://lore.kernel.org/all/aJojDiHWi8cgvA2W@pie/
>> [2]: https://lore.kernel.org/all/aJwFa8x5BQMouB1y@pie/
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Yao Zi 1 month, 1 week ago
On Fri, Aug 22, 2025 at 10:56:18AM +0800, Youling Tang wrote:
> On 2025/8/20 17:13, Youling Tang wrote:
> 
> > Hi, Yao
> > 
> > On 2025/8/20 14:50, Yao Zi wrote:
> > 
> > > On Wed, Aug 20, 2025 at 01:56:57PM +0800, Youling Tang wrote:
> > > > From: Youling Tang <tangyouling@kylinos.cn>
> > > > 
> > > > This patch adds support for kexec_file on LoongArch.
> > > > 
> > > > The efi_kexec_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)
> > > > 
> > > > This initrd will be passed to the second kernel via the command line
> > > > 'initrd=start,size'.
> > > > 
> > > > Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images
> > > > are supported,
> > > > but ELF format is not supported.
> > > > 
> > > > Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
> > > > ---
> > > >   arch/loongarch/Kconfig                     |   9 ++
> > > >   arch/loongarch/include/asm/image.h         |  17 +++
> > > >   arch/loongarch/include/asm/kexec.h         |  12 +++
> > > >   arch/loongarch/kernel/Makefile             |   1 +
> > > >   arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
> > > >   arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
> > > >   arch/loongarch/kernel/machine_kexec_file.c | 117
> > > > +++++++++++++++++++++
> > > >   7 files changed, 289 insertions(+), 11 deletions(-)
> > > >   create mode 100644 arch/loongarch/kernel/kexec_efi.c
> > > >   create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
> > > ...
> > > 
> > > > diff --git a/arch/loongarch/include/asm/image.h
> > > > b/arch/loongarch/include/asm/image.h
> > > > index 1f090736e71d..655d5836c4e8 100644
> > > > --- a/arch/loongarch/include/asm/image.h
> > > > +++ b/arch/loongarch/include/asm/image.h
> > > > @@ -36,5 +36,22 @@ struct loongarch_image_header {
> > > >       uint32_t pe_header;
> > > >   };
> > > >   +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
> > > > +
> > > > +/**
> > > > + * 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]);
> > > > +}
> > > This check is still too weak and doesn't improve comparing to v1.
> > > 
> > > > This could be simplified with a memcmp(). Also, this check isn't
> > > > strict enough: PE files for any architectures, and even legacy MS-DOS
> > > > COM executables all start with "MZ".
> > > I've pointed this out in my previous reply[1].
> > Previously, I had considered adding a specific LoongArch magic
> > number (such as "Loongson") in the loongarch_image_header, but
> > this is incompatible with older versions of the kernel, so it
> > remains the same without further checks.
> > > 
> > > >   #endif /* __ASSEMBLY__ */
> > > >   #endif /* __ASM_IMAGE_H */
> > > ...
> > > 
> > > > diff --git a/arch/loongarch/kernel/kexec_efi.c
> > > > b/arch/loongarch/kernel/kexec_efi.c
> > > > new file mode 100644
> > > > index 000000000000..7741f1139a12
> > > > --- /dev/null
> > > > +++ b/arch/loongarch/kernel/kexec_efi.c
> > > ...
> > > 
> > > > +static void *efi_kexec_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_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_min = 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;
> > > And this still assumes the loaded, secondary kernel is relocatable,
> > > with neither extra check nor comment explaining its limitation.
> > > 
> > > Please see my previous reply[2] that explains why loading a
> > > non-relocatble kernel with kexec_file API is reasonable.
> > LoongArch is a non-position independent (non-PIE) kernel when
> > the RELOCATABLE option is not enabled, the kernel contains certain
> > instructions such as la.abs, which prevent it from being relocated to
> > arbitrary memory addresses for execution. As a result, limitations
> > exist that make features like kdump or kexec_file dependent on
> > the RELOCATABLE option.
> > 
> > Strictly speaking, we need to add additional checks: if the kernel is
> > non-relocatable, the loading operation should fail directly. For a
> > running kernel, we can easily determine this by calling
> > kallsyms_lookup_name("relocate_kernel"). However, for a kernel
> > that is being loaded but has not yet started execution, it is difficult
> > to easily determine whether the currently loaded kernel has the
> > RELOCATABLE configuration option enabled.
> > 
> > For ELF format images, we can determine whether the loaded image
> > contains the ".la_abs" section in the following way:
> > static struct mem_shdr *laabs_section(const struct mem_ehdr *ehdr)
> > {
> >         struct mem_shdr *shdr, *shdr_end;
> >         unsigned char *strtab;
> > 
> >         strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
> >         shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
> >         for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
> >                 if (shdr->sh_size &&
> >                         strcmp((char *)&strtab[shdr->sh_name],
> > ".la_abs") == 0) {
> >                         return shdr;
> >                 }
> >         }
> > 
> >         return NULL;
> > }
> I attempted to parse the pe header to obtain the sections information
> and found that there were only two sections, '.text' and '.data'. We
> cannot parse whether there is a '.la_abs' section like in the ELF format.

I think it's fine to just leave a comment indicating this doesn't work
with non-relocatable kernels for now.

Best regards,
Yao Zi

> The reason is that when generating vmlinux.efi, when the ELF vmlinux
> is converted to the original binary file through the 'objdump -O binary'
> operation (arch/loongarch/boot/Makefile), the remaining sections are
> merged into the '.text' and '.data' sections.
> 
> Youling.
> > 
> > Thanks,
> > Youling.
> > > 
> > > > +    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_efi_ops = {
> > > > +    .probe = efi_kexec_probe,
> > > > +    .load = efi_kexec_load,
> > > > +};
> > > Thanks,
> > > Yao Zi
> > > 
> > > [1]: https://lore.kernel.org/all/aJojDiHWi8cgvA2W@pie/
> > > [2]: https://lore.kernel.org/all/aJwFa8x5BQMouB1y@pie/
> 
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Yao Zi 1 month, 2 weeks ago
On Wed, Aug 20, 2025 at 05:13:27PM +0800, Youling Tang wrote:
> Hi, Yao
> 
> On 2025/8/20 14:50, Yao Zi wrote:
> 
> > On Wed, Aug 20, 2025 at 01:56:57PM +0800, Youling Tang wrote:
> > > From: Youling Tang <tangyouling@kylinos.cn>
> > > 
> > > This patch adds support for kexec_file on LoongArch.
> > > 
> > > The efi_kexec_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)
> > > 
> > > This initrd will be passed to the second kernel via the command line
> > > 'initrd=start,size'.
> > > 
> > > Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are supported,
> > > but ELF format is not supported.
> > > 
> > > Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
> > > ---
> > >   arch/loongarch/Kconfig                     |   9 ++
> > >   arch/loongarch/include/asm/image.h         |  17 +++
> > >   arch/loongarch/include/asm/kexec.h         |  12 +++
> > >   arch/loongarch/kernel/Makefile             |   1 +
> > >   arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
> > >   arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
> > >   arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
> > >   7 files changed, 289 insertions(+), 11 deletions(-)
> > >   create mode 100644 arch/loongarch/kernel/kexec_efi.c
> > >   create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
> > ...
> > 
> > > diff --git a/arch/loongarch/include/asm/image.h b/arch/loongarch/include/asm/image.h
> > > index 1f090736e71d..655d5836c4e8 100644
> > > --- a/arch/loongarch/include/asm/image.h
> > > +++ b/arch/loongarch/include/asm/image.h
> > > @@ -36,5 +36,22 @@ struct loongarch_image_header {
> > >   	uint32_t pe_header;
> > >   };
> > > +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
> > > +
> > > +/**
> > > + * 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]);
> > > +}
> > This check is still too weak and doesn't improve comparing to v1.
> > 
> > > This could be simplified with a memcmp(). Also, this check isn't
> > > strict enough: PE files for any architectures, and even legacy MS-DOS
> > > COM executables all start with "MZ".
> > I've pointed this out in my previous reply[1].
> Previously, I had considered adding a specific LoongArch magic
> number (such as "Loongson") in the loongarch_image_header, but
> this is incompatible with older versions of the kernel, so it
> remains the same without further checks.

All PE images ship a PE signature with offset 0x3c[1] and you could
locate the PE header[2] by the offset provided by
loongarch_image_header.pe_header and check its machine field[3], which
should be 0x6264 for 64-bit LoongArch executables and 0x6232 for 32-bit
ones. At least this ensures it's a LoongArch kernel image.

> > >   #endif /* __ASSEMBLY__ */
> > >   #endif /* __ASM_IMAGE_H */
> > ...
> > 
> > > diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
> > > new file mode 100644
> > > index 000000000000..7741f1139a12
> > > --- /dev/null
> > > +++ b/arch/loongarch/kernel/kexec_efi.c
> > ...
> > 
> > > +static void *efi_kexec_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_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_min = 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;
> > And this still assumes the loaded, secondary kernel is relocatable,
> > with neither extra check nor comment explaining its limitation.
> > 
> > Please see my previous reply[2] that explains why loading a
> > non-relocatble kernel with kexec_file API is reasonable.
> LoongArch is a non-position independent (non-PIE) kernel when
> the RELOCATABLE option is not enabled, the kernel contains certain
> instructions such as la.abs, which prevent it from being relocated to
> arbitrary memory addresses for execution. As a result, limitations
> exist that make features like kdump or kexec_file dependent on
> the RELOCATABLE option.
> 
> Strictly speaking, we need to add additional checks: if the kernel is
> non-relocatable, the loading operation should fail directly.

Agree.

> For a
> running kernel, we can easily determine this by calling
> kallsyms_lookup_name("relocate_kernel"). However, for a kernel
> that is being loaded but has not yet started execution, it is difficult
> to easily determine whether the currently loaded kernel has the
> RELOCATABLE configuration option enabled.

I understand the difficulty, thus IMHO a comment is helpful here to
remind the reader of lack of relocatability checks.

Best regards,
Yao Zi

> For ELF format images, we can determine whether the loaded image
> contains the ".la_abs" section in the following way:
> static struct mem_shdr *laabs_section(const struct mem_ehdr *ehdr)
> {
>         struct mem_shdr *shdr, *shdr_end;
>         unsigned char *strtab;
> 
>         strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
>         shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
>         for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
>                 if (shdr->sh_size &&
>                         strcmp((char *)&strtab[shdr->sh_name], ".la_abs") ==
> 0) {
>                         return shdr;
>                 }
>         }
> 
>         return NULL;
> }
> 
> Thanks,
> Youling.
> > 
> > > +	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_efi_ops = {
> > > +	.probe = efi_kexec_probe,
> > > +	.load = efi_kexec_load,
> > > +};
> > Thanks,
> > Yao Zi
> > 
> > [1]: https://lore.kernel.org/all/aJojDiHWi8cgvA2W@pie/
> > [2]: https://lore.kernel.org/all/aJwFa8x5BQMouB1y@pie/
> 

[1]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#signature-image-only
[2]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
[3]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Youling Tang 1 month, 2 weeks ago
On 2025/8/20 19:13, Yao Zi wrote:

> On Wed, Aug 20, 2025 at 05:13:27PM +0800, Youling Tang wrote:
>> Hi, Yao
>>
>> On 2025/8/20 14:50, Yao Zi wrote:
>>
>>> On Wed, Aug 20, 2025 at 01:56:57PM +0800, Youling Tang wrote:
>>>> From: Youling Tang <tangyouling@kylinos.cn>
>>>>
>>>> This patch adds support for kexec_file on LoongArch.
>>>>
>>>> The efi_kexec_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)
>>>>
>>>> This initrd will be passed to the second kernel via the command line
>>>> 'initrd=start,size'.
>>>>
>>>> Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are supported,
>>>> but ELF format is not supported.
>>>>
>>>> Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
>>>> ---
>>>>    arch/loongarch/Kconfig                     |   9 ++
>>>>    arch/loongarch/include/asm/image.h         |  17 +++
>>>>    arch/loongarch/include/asm/kexec.h         |  12 +++
>>>>    arch/loongarch/kernel/Makefile             |   1 +
>>>>    arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
>>>>    arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
>>>>    arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
>>>>    7 files changed, 289 insertions(+), 11 deletions(-)
>>>>    create mode 100644 arch/loongarch/kernel/kexec_efi.c
>>>>    create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
>>> ...
>>>
>>>> diff --git a/arch/loongarch/include/asm/image.h b/arch/loongarch/include/asm/image.h
>>>> index 1f090736e71d..655d5836c4e8 100644
>>>> --- a/arch/loongarch/include/asm/image.h
>>>> +++ b/arch/loongarch/include/asm/image.h
>>>> @@ -36,5 +36,22 @@ struct loongarch_image_header {
>>>>    	uint32_t pe_header;
>>>>    };
>>>> +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
>>>> +
>>>> +/**
>>>> + * 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]);
>>>> +}
>>> This check is still too weak and doesn't improve comparing to v1.
>>>
>>>> This could be simplified with a memcmp(). Also, this check isn't
>>>> strict enough: PE files for any architectures, and even legacy MS-DOS
>>>> COM executables all start with "MZ".
>>> I've pointed this out in my previous reply[1].
>> Previously, I had considered adding a specific LoongArch magic
>> number (such as "Loongson") in the loongarch_image_header, but
>> this is incompatible with older versions of the kernel, so it
>> remains the same without further checks.
> All PE images ship a PE signature with offset 0x3c[1] and you could
> locate the PE header[2] by the offset provided by
> loongarch_image_header.pe_header and check its machine field[3], which
> should be 0x6264 for 64-bit LoongArch executables and 0x6232 for 32-bit
> ones. At least this ensures it's a LoongArch kernel image.
I mentioned this judgment to Huacai before. Huacai, what do you think?
If necessary, I will add this judgment.

Thanks,
Youling.
>
>>>>    #endif /* __ASSEMBLY__ */
>>>>    #endif /* __ASM_IMAGE_H */
>>> ...
>>>
>>>> diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
>>>> new file mode 100644
>>>> index 000000000000..7741f1139a12
>>>> --- /dev/null
>>>> +++ b/arch/loongarch/kernel/kexec_efi.c
>>> ...
>>>
>>>> +static void *efi_kexec_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_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_min = 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;
>>> And this still assumes the loaded, secondary kernel is relocatable,
>>> with neither extra check nor comment explaining its limitation.
>>>
>>> Please see my previous reply[2] that explains why loading a
>>> non-relocatble kernel with kexec_file API is reasonable.
>> LoongArch is a non-position independent (non-PIE) kernel when
>> the RELOCATABLE option is not enabled, the kernel contains certain
>> instructions such as la.abs, which prevent it from being relocated to
>> arbitrary memory addresses for execution. As a result, limitations
>> exist that make features like kdump or kexec_file dependent on
>> the RELOCATABLE option.
>>
>> Strictly speaking, we need to add additional checks: if the kernel is
>> non-relocatable, the loading operation should fail directly.
> Agree.
>
>> For a
>> running kernel, we can easily determine this by calling
>> kallsyms_lookup_name("relocate_kernel"). However, for a kernel
>> that is being loaded but has not yet started execution, it is difficult
>> to easily determine whether the currently loaded kernel has the
>> RELOCATABLE configuration option enabled.
> I understand the difficulty, thus IMHO a comment is helpful here to
> remind the reader of lack of relocatability checks.
>
> Best regards,
> Yao Zi
>
>> For ELF format images, we can determine whether the loaded image
>> contains the ".la_abs" section in the following way:
>> static struct mem_shdr *laabs_section(const struct mem_ehdr *ehdr)
>> {
>>          struct mem_shdr *shdr, *shdr_end;
>>          unsigned char *strtab;
>>
>>          strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
>>          shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
>>          for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
>>                  if (shdr->sh_size &&
>>                          strcmp((char *)&strtab[shdr->sh_name], ".la_abs") ==
>> 0) {
>>                          return shdr;
>>                  }
>>          }
>>
>>          return NULL;
>> }
>>
>> Thanks,
>> Youling.
>>>> +	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_efi_ops = {
>>>> +	.probe = efi_kexec_probe,
>>>> +	.load = efi_kexec_load,
>>>> +};
>>> Thanks,
>>> Yao Zi
>>>
>>> [1]: https://lore.kernel.org/all/aJojDiHWi8cgvA2W@pie/
>>> [2]: https://lore.kernel.org/all/aJwFa8x5BQMouB1y@pie/
> [1]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#signature-image-only
> [2]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
> [3]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
Re: [PATCH v2 2/5] LoongArch: Add kexec_file support
Posted by Huacai Chen 1 month, 1 week ago
On Thu, Aug 21, 2025 at 9:20 AM Youling Tang <youling.tang@linux.dev> wrote:
>
> On 2025/8/20 19:13, Yao Zi wrote:
>
> > On Wed, Aug 20, 2025 at 05:13:27PM +0800, Youling Tang wrote:
> >> Hi, Yao
> >>
> >> On 2025/8/20 14:50, Yao Zi wrote:
> >>
> >>> On Wed, Aug 20, 2025 at 01:56:57PM +0800, Youling Tang wrote:
> >>>> From: Youling Tang <tangyouling@kylinos.cn>
> >>>>
> >>>> This patch adds support for kexec_file on LoongArch.
> >>>>
> >>>> The efi_kexec_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)
> >>>>
> >>>> This initrd will be passed to the second kernel via the command line
> >>>> 'initrd=start,size'.
> >>>>
> >>>> Currently, pez(vmlinuz.efi) and pei(vmlinux.efi) format images are supported,
> >>>> but ELF format is not supported.
> >>>>
> >>>> Signed-off-by: Youling Tang <tangyouling@kylinos.cn>
> >>>> ---
> >>>>    arch/loongarch/Kconfig                     |   9 ++
> >>>>    arch/loongarch/include/asm/image.h         |  17 +++
> >>>>    arch/loongarch/include/asm/kexec.h         |  12 +++
> >>>>    arch/loongarch/kernel/Makefile             |   1 +
> >>>>    arch/loongarch/kernel/kexec_efi.c          | 111 +++++++++++++++++++
> >>>>    arch/loongarch/kernel/machine_kexec.c      |  33 ++++--
> >>>>    arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
> >>>>    7 files changed, 289 insertions(+), 11 deletions(-)
> >>>>    create mode 100644 arch/loongarch/kernel/kexec_efi.c
> >>>>    create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
> >>> ...
> >>>
> >>>> diff --git a/arch/loongarch/include/asm/image.h b/arch/loongarch/include/asm/image.h
> >>>> index 1f090736e71d..655d5836c4e8 100644
> >>>> --- a/arch/loongarch/include/asm/image.h
> >>>> +++ b/arch/loongarch/include/asm/image.h
> >>>> @@ -36,5 +36,22 @@ struct loongarch_image_header {
> >>>>            uint32_t pe_header;
> >>>>    };
> >>>> +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'};
> >>>> +
> >>>> +/**
> >>>> + * 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]);
> >>>> +}
> >>> This check is still too weak and doesn't improve comparing to v1.
> >>>
> >>>> This could be simplified with a memcmp(). Also, this check isn't
> >>>> strict enough: PE files for any architectures, and even legacy MS-DOS
> >>>> COM executables all start with "MZ".
> >>> I've pointed this out in my previous reply[1].
> >> Previously, I had considered adding a specific LoongArch magic
> >> number (such as "Loongson") in the loongarch_image_header, but
> >> this is incompatible with older versions of the kernel, so it
> >> remains the same without further checks.
> > All PE images ship a PE signature with offset 0x3c[1] and you could
> > locate the PE header[2] by the offset provided by
> > loongarch_image_header.pe_header and check its machine field[3], which
> > should be 0x6264 for 64-bit LoongArch executables and 0x6232 for 32-bit
> > ones. At least this ensures it's a LoongArch kernel image.
> I mentioned this judgment to Huacai before. Huacai, what do you think?
> If necessary, I will add this judgment.
I think we can assume that users know what they are doing, so we can
just distinguish an EFI kernel and ELF kernel, no more checking is
necessary.

Huacai

>
> Thanks,
> Youling.
> >
> >>>>    #endif /* __ASSEMBLY__ */
> >>>>    #endif /* __ASM_IMAGE_H */
> >>> ...
> >>>
> >>>> diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
> >>>> new file mode 100644
> >>>> index 000000000000..7741f1139a12
> >>>> --- /dev/null
> >>>> +++ b/arch/loongarch/kernel/kexec_efi.c
> >>> ...
> >>>
> >>>> +static void *efi_kexec_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_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_min = 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;
> >>> And this still assumes the loaded, secondary kernel is relocatable,
> >>> with neither extra check nor comment explaining its limitation.
> >>>
> >>> Please see my previous reply[2] that explains why loading a
> >>> non-relocatble kernel with kexec_file API is reasonable.
> >> LoongArch is a non-position independent (non-PIE) kernel when
> >> the RELOCATABLE option is not enabled, the kernel contains certain
> >> instructions such as la.abs, which prevent it from being relocated to
> >> arbitrary memory addresses for execution. As a result, limitations
> >> exist that make features like kdump or kexec_file dependent on
> >> the RELOCATABLE option.
> >>
> >> Strictly speaking, we need to add additional checks: if the kernel is
> >> non-relocatable, the loading operation should fail directly.
> > Agree.
> >
> >> For a
> >> running kernel, we can easily determine this by calling
> >> kallsyms_lookup_name("relocate_kernel"). However, for a kernel
> >> that is being loaded but has not yet started execution, it is difficult
> >> to easily determine whether the currently loaded kernel has the
> >> RELOCATABLE configuration option enabled.
> > I understand the difficulty, thus IMHO a comment is helpful here to
> > remind the reader of lack of relocatability checks.
> >
> > Best regards,
> > Yao Zi
> >
> >> For ELF format images, we can determine whether the loaded image
> >> contains the ".la_abs" section in the following way:
> >> static struct mem_shdr *laabs_section(const struct mem_ehdr *ehdr)
> >> {
> >>          struct mem_shdr *shdr, *shdr_end;
> >>          unsigned char *strtab;
> >>
> >>          strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
> >>          shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
> >>          for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
> >>                  if (shdr->sh_size &&
> >>                          strcmp((char *)&strtab[shdr->sh_name], ".la_abs") ==
> >> 0) {
> >>                          return shdr;
> >>                  }
> >>          }
> >>
> >>          return NULL;
> >> }
> >>
> >> Thanks,
> >> Youling.
> >>>> +  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_efi_ops = {
> >>>> +  .probe = efi_kexec_probe,
> >>>> +  .load = efi_kexec_load,
> >>>> +};
> >>> Thanks,
> >>> Yao Zi
> >>>
> >>> [1]: https://lore.kernel.org/all/aJojDiHWi8cgvA2W@pie/
> >>> [2]: https://lore.kernel.org/all/aJwFa8x5BQMouB1y@pie/
> > [1]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#signature-image-only
> > [2]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
> > [3]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types