Skip to contentSkip to conversation

Add kexec_file support for LoongArch

PATCH Superseded Youling Tang 4 participants 17 messages Aug 20, 2025 – Aug 26, 2025

Subsystems (3)

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v2 cover Add kexec_file support for LoongArch

From: Youling Tang <[email protected]>

This patchset implement kexec_file_load() support on LoongArch.

This patch series enables us to load the LoongArch vmlinuz.efi(pez) or vmlinux.efi(pei) or vmlinux(elf) by specifying its file decriptor, instead of user-filled buffer via kexec_load() syscall.

To use kexec_file_load() system call, instead of kexec_load(), at kexec command, '-s' options must be specified. kexec-tools needs to apply the corresponding patches. These patches can be found in repository [1] and will be submitted to the kexec-tools community later.

The basic usage of kexec_file is:
1) Load second kernel image:

 # kexec -s -l vmlinuz.efi --initrd=initrd.img --reuse-cmdline

2) Startup second kernel:

 # kexec -e

For kdump:
1) Load capture kernel image:

 # kexec -s -p vmlinuz.efi --initrd=initrd.img --reuse-cmdline

2) Do something to crash, like:

 # echo c > /proc/sysrq-trigger

Link:
[1] https://github.com/tangyouling/kexec-tools/commits/main/

Changelog:
v2:
* Merge some patches.
* Add support for ELF format images.
* Rename kexec_image.c to kexec_efi.c .
* When KEXEC_FILE is enabled, RELOCATABLE is selected by default.
* Some minor modifications.

Youling Tang (5):
LoongArch: Add struct loongarch_image_header for kernel image
LoongArch: Add kexec_file support LoongArch/kexec_file: Support loading ELF binary file LoongArch/kexec_file: Add crash dump support
LoongArch: Enable CONFIG_KEXEC_FILE

 arch/loongarch/Kconfig                     |  10 +
 arch/loongarch/configs/loongson3_defconfig |   1 +
 arch/loongarch/include/asm/image.h         |  57 +++++
 arch/loongarch/include/asm/kexec.h         |  13 ++
 arch/loongarch/kernel/Makefile             |   1 +
 arch/loongarch/kernel/kexec_efi.c          | 111 ++++++++++
 arch/loongarch/kernel/kexec_elf.c          | 105 +++++++++
 arch/loongarch/kernel/machine_kexec.c      |  33 ++-
 arch/loongarch/kernel/machine_kexec_file.c | 235 +++++++++++++++++++++
 9 files changed, 555 insertions(+), 11 deletions(-)
 create mode 100644 arch/loongarch/include/asm/image.h
 create mode 100644 arch/loongarch/kernel/kexec_efi.c
 create mode 100644 arch/loongarch/kernel/kexec_elf.c
 create mode 100644 arch/loongarch/kernel/machine_kexec_file.c
Signature · 1 line
2.43.0
#1 lore raw

PATCH v2 1/5 LoongArch: Add struct loongarch_image_header for kernel image

From: Youling Tang <[email protected]>

Define a dedicated image header structure for LoongArch architecture to standardize kernel loading in bootloaders (primarily for kexec_file).

This header includes critical metadata such as PE signature, entry points, image size and load address offset, etc.

---
 arch/loongarch/include/asm/image.h | 40 ++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 arch/loongarch/include/asm/image.h
arch/loongarch/include/asm/image.h new file +40 -0
new file mode 100644
index 000000000000..1f090736e71d
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * LoongArch binary image header.
+ *
+ * Author: Youling Tang <[email protected]>
+ * Copyright (C) 2025 KylinSoft Corporation.
+ *
+ * Most code is derived from LoongArch port of kexec-tools
+ */
+
+#ifndef __ASM_IMAGE_H
+#define __ASM_IMAGE_H
+#ifndef __ASSEMBLY__
+
+/**
+ * struct loongarch_image_header
+ *
+ * @pe_sig: Optional PE format 'MZ' signature.
+ * @reserved_1: Reserved.
+ * @kernel_entry: Kernel image entry pointer.
+ * @image_size: An estimated size of the memory image size in LSB byte order.
+ * @text_offset: The image load offset in LSB byte order.
+ * @reserved_2: Reserved.
+ * @reserved_3: Reserved.
+ * @pe_header: Optional offset to a PE format header.
+ **/
+
+struct loongarch_image_header {
+	uint8_t pe_sig[2];
+	uint16_t reserved_1[3];
+	uint64_t kernel_entry;
+	uint64_t image_size;
+	uint64_t text_offset;
+	uint64_t reserved_2[3];
+	uint32_t reserved_3;
+	uint32_t pe_header;
+};
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_IMAGE_H */
Signature · 1 line
2.43.0
#2 lore raw

PATCH v2 2/5 LoongArch: Add kexec_file support

In reply to Youling Tang

From: Youling Tang <[email protected]>

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.

---
 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
arch/loongarch/Kconfig +9 -0
index f0abc38c40ac..490dc6eed749 100644
@@ -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
 
arch/loongarch/include/asm/image.h +17 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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 */
arch/loongarch/include/asm/kexec.h +12 -0
index cf95cd3eb2de..34799db933fb 100644
@@ -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,
arch/loongarch/kernel/Makefile +1 -0
index 6f5a4574a911..dd6183f353e6 100644
@@ -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
arch/loongarch/kernel/kexec_efi.c new file +111 -0

111 lines not on this page · raw message

arch/loongarch/kernel/machine_kexec.c +22 -11
index f9381800e291..ba8d64bc6549 100644
@@ -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");
arch/loongarch/kernel/machine_kexec_file.c new file +117 -0

117 lines not on this page · raw message

Signature · 1 line
2.43.0
#3 lore raw

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

Quoted 26 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
 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

...

Quoted 14 lines
arch/loongarch/include/asm/image.h +6 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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.

arch/loongarch/include/asm/image.h struct loongarch_image_header { · line 54

Quoted 10 lines
arch/loongarch/include/asm/image.h +10 -0
+ */
+
+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.

Quoted 3 lines

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].

Quoted 2 lines

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

...

Quoted 5 lines
arch/loongarch/kernel/kexec_efi.c new file +0 -0
new file mode 100644
index 000000000000..7741f1139a12

...

Quoted 52 lines
arch/loongarch/kernel/kexec_efi.c new file +52 -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;
+	}

arch/loongarch/kernel/kexec_efi.c line 99

Quoted 10 lines
arch/loongarch/kernel/kexec_efi.c new file +10 -0
+
+	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.

arch/loongarch/kernel/kexec_efi.c line 111

Quoted 11 lines
arch/loongarch/kernel/kexec_efi.c new file +11 -0
+	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/

#4 lore raw

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

Hi, Yao

Quoted 55 lines · Yao Zi
Quoted 26 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
  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

...

Quoted 14 lines
arch/loongarch/include/asm/image.h +6 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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.

arch/loongarch/include/asm/image.h struct loongarch_image_header { · line 54

Quoted 10 lines
arch/loongarch/include/asm/image.h +10 -0
+ */
+
+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.

Quoted 4 lines
Quoted 3 lines

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.

Quoted 77 lines
Quoted 3 lines
  #endif /* __ASSEMBLY__ */
  #endif /* __ASM_IMAGE_H */

...

Quoted 5 lines
arch/loongarch/kernel/kexec_efi.c new file +0 -0
new file mode 100644
index 000000000000..7741f1139a12

...

Quoted 52 lines
arch/loongarch/kernel/kexec_efi.c new file +52 -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;
+	}

arch/loongarch/kernel/kexec_efi.c line 99

Quoted 10 lines
arch/loongarch/kernel/kexec_efi.c new file +10 -0
+
+	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.

Quoted 2 lines

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.

Quoted 17 lines

arch/loongarch/kernel/kexec_efi.c line 111

Quoted 12 lines
arch/loongarch/kernel/kexec_efi.c new file +11 -0
+	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/

#5 lore raw

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

Quoted 58 lines · Youling Tang

Hi, Yao

Quoted 54 lines · Yao Zi
Quoted 26 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
  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

...

Quoted 13 lines
arch/loongarch/include/asm/image.h +6 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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.

arch/loongarch/include/asm/image.h struct loongarch_image_header { · line 53

Quoted 10 lines
arch/loongarch/include/asm/image.h +10 -0
+ */
+
+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.

Quoted 8 lines
Quoted 4 lines
Quoted 3 lines

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.

Quoted 85 lines
Quoted 76 lines
Quoted 2 lines
  #endif /* __ASSEMBLY__ */
  #endif /* __ASM_IMAGE_H */

...

Quoted 5 lines
arch/loongarch/kernel/kexec_efi.c new file +0 -0
new file mode 100644
index 000000000000..7741f1139a12

...

Quoted 52 lines
arch/loongarch/kernel/kexec_efi.c new file +52 -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;
+	}

arch/loongarch/kernel/kexec_efi.c line 99

Quoted 10 lines
arch/loongarch/kernel/kexec_efi.c new file +10 -0
+
+	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.

Quoted 2 lines

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.

Quoted 2 lines

Strictly speaking, we need to add additional checks: if the kernel is non-relocatable, the loading operation should fail directly.

Agree.

Quoted 6 lines

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

Quoted 17 lines

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;
                }
        }

Quoted 23 lines

        return NULL;
}

Thanks,
Youling.

Quoted 17 lines

arch/loongarch/kernel/kexec_efi.c line 111

Quoted 12 lines
arch/loongarch/kernel/kexec_efi.c new file +11 -0
+	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

#6 lore raw

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

Quoted 67 lines · Yao Zi
Quoted 58 lines · Youling Tang

Hi, Yao

Quoted 54 lines · Yao Zi
Quoted 26 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
   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

...

Quoted 13 lines
arch/loongarch/include/asm/image.h +6 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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.

arch/loongarch/include/asm/image.h struct loongarch_image_header { · line 53

Quoted 10 lines
arch/loongarch/include/asm/image.h +10 -0
+ */
+
+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.

Quoted 8 lines
Quoted 4 lines
Quoted 3 lines

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.

Quoted 5 lines

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.

Quoted 143 lines
Quoted 86 lines
Quoted 77 lines
Quoted 3 lines
   #endif /* __ASSEMBLY__ */
   #endif /* __ASM_IMAGE_H */

...

Quoted 5 lines
arch/loongarch/kernel/kexec_efi.c new file +0 -0
new file mode 100644
index 000000000000..7741f1139a12

...

Quoted 52 lines
arch/loongarch/kernel/kexec_efi.c new file +52 -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;
+	}

arch/loongarch/kernel/kexec_efi.c line 99

Quoted 10 lines
arch/loongarch/kernel/kexec_efi.c new file +10 -0
+
+	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.

Quoted 2 lines

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.

Quoted 2 lines

Strictly speaking, we need to add additional checks: if the kernel is non-relocatable, the loading operation should fail directly.

Agree.

Quoted 6 lines

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

Quoted 17 lines

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;
                }
        }

Quoted 21 lines

        return NULL;
}

Thanks,
Youling.

Quoted 16 lines

arch/loongarch/kernel/kexec_efi.c line 111

Quoted 11 lines
arch/loongarch/kernel/kexec_efi.c new file +11 -0
+	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

maintainer
#7 lore raw

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

Quoted 70 lines · Youling Tang
Quoted 67 lines · Yao Zi
Quoted 66 lines · Youling Tang

Hi, Yao

Quoted 54 lines · Yao Zi
Quoted 26 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
   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

...

Quoted 13 lines
arch/loongarch/include/asm/image.h +6 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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.

arch/loongarch/include/asm/image.h struct loongarch_image_header { · line 53

Quoted 10 lines
arch/loongarch/include/asm/image.h +10 -0
+ */
+
+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.

Quoted 4 lines
Quoted 3 lines

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.

Quoted 7 lines
Quoted 5 lines

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

Quoted 146 lines

Thanks,
Youling.

Quoted 143 lines
Quoted 86 lines
Quoted 77 lines
Quoted 3 lines
   #endif /* __ASSEMBLY__ */
   #endif /* __ASM_IMAGE_H */

...

Quoted 5 lines
arch/loongarch/kernel/kexec_efi.c new file +0 -0
new file mode 100644
index 000000000000..7741f1139a12

...

Quoted 52 lines
arch/loongarch/kernel/kexec_efi.c new file +52 -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;
+  }

arch/loongarch/kernel/kexec_efi.c line 99

Quoted 10 lines
arch/loongarch/kernel/kexec_efi.c new file +10 -0
+
+  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.

Quoted 2 lines

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.

Quoted 2 lines

Strictly speaking, we need to add additional checks: if the kernel is non-relocatable, the loading operation should fail directly.

Agree.

Quoted 6 lines

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

Quoted 20 lines

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;
}
Quoted 18 lines

Thanks,
Youling.

Quoted 16 lines

arch/loongarch/kernel/kexec_efi.c line 111

Quoted 11 lines
arch/loongarch/kernel/kexec_efi.c new file +11 -0
+  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

#8 lore raw

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

In reply to Youling Tang
Quoted 182 lines · Youling Tang

Hi, Yao

Quoted 57 lines · Yao Zi
Quoted 27 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
  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

...

Quoted 18 lines
arch/loongarch/include/asm/image.h +3 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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.
+ */

arch/loongarch/include/asm/image.h +2 -0
+
+static inline int loongarch_header_check_pe_sig(const struct 

loongarch_image_header *h)

arch/loongarch/include/asm/image.h struct loongarch_image_header { · line 54

Quoted 7 lines
arch/loongarch/include/asm/image.h +7 -0
+{
+    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.

Quoted 4 lines
Quoted 3 lines

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.

Quoted 80 lines
Quoted 3 lines

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

...

Quoted 5 lines
arch/loongarch/kernel/kexec_efi.c new file +0 -0
new file mode 100644
index 000000000000..7741f1139a12

...

Quoted 62 lines
arch/loongarch/kernel/kexec_efi.c new file +32 -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;

arch/loongarch/kernel/kexec_efi.c new file +18 -0
+
+        /*
+         * 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 
Quoted 3 lines

"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.

Quoted 2 lines

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;
                }
        }

Quoted 2 lines

        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.

Quoted 20 lines

Thanks,
Youling.

Quoted 17 lines

arch/loongarch/kernel/kexec_efi.c line 111

Quoted 12 lines

+    kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n", +              kernel_segment->mem, kbuf.bufsz,
+              kernel_segment->memsz);

arch/loongarch/kernel/kexec_efi.c new file +8 -0
+
+    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/

#9 lore raw

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

Quoted 188 lines · Youling Tang
Quoted 186 lines · Youling Tang

Hi, Yao

Quoted 59 lines · Yao Zi
Quoted 28 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
  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

...

Quoted 19 lines
arch/loongarch/include/asm/image.h +3 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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.
+ */

arch/loongarch/include/asm/image.h +2 -0
+
+static inline int loongarch_header_check_pe_sig(const struct

loongarch_image_header *h)

arch/loongarch/include/asm/image.h struct loongarch_image_header { · line 54

Quoted 7 lines
arch/loongarch/include/asm/image.h +7 -0
+{
+    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.

Quoted 4 lines
Quoted 3 lines

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.

Quoted 81 lines
Quoted 3 lines

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

...

Quoted 6 lines
arch/loongarch/kernel/kexec_efi.c new file +0 -0
new file mode 100644
index 000000000000..7741f1139a12

...

Quoted 62 lines
arch/loongarch/kernel/kexec_efi.c new file +32 -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;

arch/loongarch/kernel/kexec_efi.c new file +18 -0
+
+        /*
+         * 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
Quoted 3 lines

"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.

Quoted 2 lines

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;
                }
        }

Quoted 5 lines
Quoted 2 lines

        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

Quoted 27 lines

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.

Quoted 20 lines

Thanks,
Youling.

Quoted 17 lines

arch/loongarch/kernel/kexec_efi.c line 111

Quoted 12 lines

+    kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n", +              kernel_segment->mem, kbuf.bufsz,
+              kernel_segment->memsz);

arch/loongarch/kernel/kexec_efi.c new file +8 -0
+
+    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/

maintainer
#10 lore raw

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

In reply to Youling Tang

Hi, Youling,

Quoted 271 lines · Youling Tang

From: Youling Tang <[email protected]>

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.

---
 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
arch/loongarch/Kconfig +9 -0
index f0abc38c40ac..490dc6eed749 100644
@@ -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
 
arch/loongarch/include/asm/image.h +17 -0
index 1f090736e71d..655d5836c4e8 100644
@@ -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 */
arch/loongarch/include/asm/kexec.h +12 -0
index cf95cd3eb2de..34799db933fb 100644
@@ -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,
arch/loongarch/kernel/Makefile +1 -0
index 6f5a4574a911..dd6183f353e6 100644
@@ -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
arch/loongarch/kernel/kexec_efi.c new file +111 -0
new file mode 100644
index 000000000000..7741f1139a12
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Load EFI vmlinux file for the kexec_file_load syscall.
+ *
+ * Author: Youling Tang <[email protected]>
+ * 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,
+};
arch/loongarch/kernel/machine_kexec.c +20 -10
index f9381800e291..ba8d64bc6549 100644
@@ -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 */

arch/loongarch/kernel/machine_kexec.c void machine_kexec(struct kimage *image) · line 302

Quoted 6 lines
arch/loongarch/kernel/machine_kexec.c +2 -1
@@ -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

Quoted 129 lines
arch/loongarch/kernel/machine_kexec.c +0 -0
        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");
arch/loongarch/kernel/machine_kexec_file.c new file +117 -1
new file mode 100644
index 000000000000..a713acf32db8
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * kexec_file for LoongArch
+ *
+ * Author: Youling Tang <[email protected]>
+ * 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

#11 lore raw

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

In reply to Youling Tang
kernel test robot noticed the following build warnings:

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/[email protected]/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce: (https://download.01.org/0day-ci/archive/20250826/[email protected]/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 <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

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

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#12 lore raw

PATCH v2 3/5 LoongArch/kexec_file: Support loading ELF binary file

In reply to Youling Tang

From: Youling Tang <[email protected]>

This patch creates kexec_elf_ops to load ELF binary file for kexec_file_load() syscall.

---
 arch/loongarch/Kconfig                     |   1 +
 arch/loongarch/include/asm/kexec.h         |   1 +
 arch/loongarch/kernel/Makefile             |   2 +-
 arch/loongarch/kernel/kexec_elf.c          | 105 +++++++++++++++++++++
 arch/loongarch/kernel/machine_kexec_file.c |   1 +
 5 files changed, 109 insertions(+), 1 deletion(-)
 create mode 100644 arch/loongarch/kernel/kexec_elf.c
arch/loongarch/Kconfig +1 -0
index 490dc6eed749..bf2664ed7ce3 100644
@@ -632,6 +632,7 @@ config ARCH_SELECTS_KEXEC_FILE
 	def_bool y
 	depends on KEXEC_FILE
 	select HAVE_IMA_KEXEC if IMA
+	select KEXEC_ELF
 	select RELOCATABLE
 
 config ARCH_SUPPORTS_CRASH_DUMP
arch/loongarch/include/asm/kexec.h +1 -0
index 34799db933fb..fecfb3015abc 100644
@@ -43,6 +43,7 @@ struct kimage_arch {
 
 #ifdef CONFIG_KEXEC_FILE
 extern const struct kexec_file_ops kexec_efi_ops;
+extern const struct kexec_file_ops kexec_elf_ops;
 
 int arch_kimage_file_post_load_cleanup(struct kimage *image);
 #define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup
arch/loongarch/kernel/Makefile +1 -1
index dd6183f353e6..001924877772 100644
@@ -62,7 +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_KEXEC_FILE)	+= machine_kexec_file.o kexec_efi.o kexec_elf.o
 obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 
 obj-$(CONFIG_UNWINDER_GUESS)	+= unwind_guess.o
arch/loongarch/kernel/kexec_elf.c new file +105 -0

105 lines not on this page · raw message

arch/loongarch/kernel/machine_kexec_file.c +1 -0
index a713acf32db8..dc6e37457559 100644
@@ -22,6 +22,7 @@
 
 const struct kexec_file_ops * const kexec_file_loaders[] = {
 	&kexec_efi_ops,
+	&kexec_elf_ops,
 	NULL
 };
 
Signature · 1 line
2.43.0
#13 lore raw

Re: [PATCH v2 3/5] LoongArch/kexec_file: Support loading ELF binary file

Quoted 95 lines · Youling Tang

From: Youling Tang <[email protected]>

This patch creates kexec_elf_ops to load ELF binary file for kexec_file_load() syscall.

---
  arch/loongarch/Kconfig                     |   1 +
  arch/loongarch/include/asm/kexec.h         |   1 +
  arch/loongarch/kernel/Makefile             |   2 +-
  arch/loongarch/kernel/kexec_elf.c          | 105 +++++++++++++++++++++
  arch/loongarch/kernel/machine_kexec_file.c |   1 +
  5 files changed, 109 insertions(+), 1 deletion(-)

create mode 100644 arch/loongarch/kernel/kexec_elf.c

arch/loongarch/Kconfig +1 -0
index 490dc6eed749..bf2664ed7ce3 100644
@@ -632,6 +632,7 @@ config ARCH_SELECTS_KEXEC_FILE
  	def_bool y
  	depends on KEXEC_FILE
  	select HAVE_IMA_KEXEC if IMA
+	select KEXEC_ELF
  	select RELOCATABLE
 
  config ARCH_SUPPORTS_CRASH_DUMP
arch/loongarch/include/asm/kexec.h +1 -0
index 34799db933fb..fecfb3015abc 100644
@@ -43,6 +43,7 @@ struct kimage_arch {
 
  #ifdef CONFIG_KEXEC_FILE
  extern const struct kexec_file_ops kexec_efi_ops;
+extern const struct kexec_file_ops kexec_elf_ops;
 
  int arch_kimage_file_post_load_cleanup(struct kimage *image);
  #define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup
arch/loongarch/kernel/Makefile +1 -1
index dd6183f353e6..001924877772 100644
@@ -62,7 +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_KEXEC_FILE)	+= machine_kexec_file.o kexec_efi.o kexec_elf.o
  obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 
  obj-$(CONFIG_UNWINDER_GUESS)	+= unwind_guess.o
arch/loongarch/kernel/kexec_elf.c new file +37 -0
new file mode 100644
index 000000000000..d41ee5fd7cef
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Load ELF vmlinux file for the kexec_file_load syscall.
+ *
+ * Author: Youling Tang <[email protected]>
+ * Copyright (C) 2025 KylinSoft Corporation.
+ */
+
+#define pr_fmt(fmt)	"kexec_file(ELF): " fmt
+
+#include <linux/elf.h>
+#include <linux/kexec.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/memblock.h>
+#include <asm/image.h>
+#include <asm/setup.h>
+
+static int loongarch_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+			 struct kexec_elf_info *elf_info,
+			 struct kexec_buf *kbuf,
+			 unsigned long *text_offset)
+{
+	int ret = -1;
+	size_t i;
+
+	/* Read in the PT_LOAD segments. */
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		size_t size;
+		const struct elf_phdr *phdr;
+		struct loongarch_image_header *header;
+
+		phdr = &elf_info->proghdrs[i];
+		if (phdr->p_type != PT_LOAD)
+			continue;
+
+		size = phdr->p_filesz;

arch/loongarch/kernel/kexec_elf.c line 47

Quoted 10 lines
arch/loongarch/kernel/kexec_elf.c new file +10 -0
+		if (size > phdr->p_memsz)
+			size = phdr->p_memsz;
+
+		kbuf->buffer = (void *)elf_info->buffer + phdr->p_offset;
+		kbuf->bufsz = size;
+		kbuf->buf_align = phdr->p_align;
+		header = (struct loongarch_image_header *)kbuf->buffer;
+		*text_offset = le64_to_cpu(header->text_offset);
+		kbuf->buf_min = *text_offset;
+		kbuf->memsz = le64_to_cpu(header->image_size);

Elf kernel resolution should not be used to struct loongarch_image_header, members of the content contained in the inner world of CONFIG_EFI_STUB in the arch/loongarch/kernel/head.S, should use the content inside the elf header information, The kexec-tools tool should also be fixed.

However, there is certain content in the elf header information, and the general kexec_elf_load() function cannot be used. Special processing of kbuf->memsz and kbuf->memsz is required.

$ readelf -l vmlinux
...
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags Align
  LOAD           0x0000000000010000 0x9000000000200000 0x9000000000200000
                 0x0000000002747a00 0x000000000287a0d8  RWE 0x10000
  NOTE           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  R      0x8

phdr->p_paddr should have been a physical address, but it is a virtual address on the current LoongArch. This will cause kexec_file to fail when loading the kernel and need to be converted to a physical address. Another patch is working to fix this issue[1].

From the above MemSiz, it can be seen that 0x287a0d8 has not been aligned. Although kexec_add_buffer() will perform PAGE_SIZE alignment on kbuf->memsz, there is still a stampeding in the loaded kernel space and initrd space. The initrd resolution failed when starting the second kernel.

It can be known from the link script vmlinux.lds.S that, BSS_SECTION(0, SZ_64K, 8)
   . = ALIGN(PECOFF_SEGMENT_ALIGN);

It needs to be aligned according to the SZ_64K size, so that after alignment, its size is consistent with _kernel_asize (loongarch_image_header->image_size).

The following modifications will be used in the next version to fix it,

arch/loongarch/kernel/kexec_elf.c +0 -0
@@ -28,7 +28,6 @@ static int loongarch_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,

        for (i = 0; i < ehdr->e_phnum; i++) { size_t size;
                const struct elf_phdr *phdr; -               struct loongarch_image_header *header;

                phdr = &elf_info->proghdrs[i];
                if (phdr->p_type != PT_LOAD)
@@ -41,10 +40,9 @@ static int loongarch_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, kbuf->buffer = (void *)elf_info->buffer + phdr->p_offset; kbuf->bufsz = size;
                kbuf->buf_align = phdr->p_align;
-               header = (struct loongarch_image_header *)kbuf->buffer;
-               *text_offset = le64_to_cpu(header->text_offset);
-               kbuf->buf_min = *text_offset;
-               kbuf->memsz = le64_to_cpu(header->image_size);
+               *text_offset = __pa(phdr->p_paddr);
+               kbuf->buf_min = _*text_offset;
+               kbuf->memsz = ALIGN(phdr->p_memsz, SZ_64K);
                kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;

[1]: https://lore.kernel.org/loongarch/[email protected]/T/#t

Thanks,
Youling.

Quoted 70 lines
arch/loongarch/kernel/kexec_elf.c new file +58 -0
+		kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
+		ret = kexec_add_buffer(kbuf);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static void *elf_kexec_load(struct kimage *image, char *kernel_buf,
+			    unsigned long kernel_len, char *initrd,
+			    unsigned long initrd_len, char *cmdline,
+			    unsigned long cmdline_len)
+{
+	int ret;
+	unsigned long text_offset = 0, kernel_segment_number;
+	struct elfhdr ehdr;
+	struct kexec_elf_info elf_info;
+	struct kexec_segment *kernel_segment;
+	struct kexec_buf kbuf;
+
+	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/* Load the kernel */
+	kbuf.image = image;
+	kbuf.buf_max = ULONG_MAX;
+	kbuf.top_down = false;
+
+	kernel_segment_number = image->nr_segments;
+
+	ret = loongarch_kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &text_offset);
+	if (ret)
+		goto out;
+
+	/* 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)
+		goto out;
+
+	/* Make sure the second kernel jumps to the correct "kernel_entry". */
+	image->start = kernel_segment->mem + __pa(ehdr.e_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);
+
+out:
+	kexec_free_elf_info(&elf_info);
+	return ret ? ERR_PTR(ret) : NULL;
+}
+
+const struct kexec_file_ops kexec_elf_ops = {
+	.probe = kexec_elf_probe,
+	.load  = elf_kexec_load,
+};
arch/loongarch/kernel/machine_kexec_file.c +1 -0
index a713acf32db8..dc6e37457559 100644
@@ -22,6 +22,7 @@
 
  const struct kexec_file_ops * const kexec_file_loaders[] = {
  	&kexec_efi_ops,
+	&kexec_elf_ops,
  	NULL
  };
 
#14 lore raw

PATCH v2 4/5 LoongArch/kexec_file: Add crash dump support

In reply to Youling Tang

From: Youling Tang <[email protected]>

Enabling crash dump (kdump) includes:
- Prepare contents of ELF header of a core dump file, /proc/vmcore, using crash_prepare_elf64_headers().
- Add "elfcorehdr=size@start" to cmdline.
- Add the "mem=size@start" parameter to the command line and pass it to the capture kernel. Limit the runtime memory area of the captured kernel to avoid disrupting the production kernel's runtime state.

---
 arch/loongarch/kernel/machine_kexec_file.c | 117 +++++++++++++++++++++
 1 file changed, 117 insertions(+)
arch/loongarch/kernel/machine_kexec_file.c +117 -0
index dc6e37457559..1d47be06549e 100644
@@ -35,6 +35,83 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
 	return kexec_image_post_load_cleanup_default(image);
 }
 
+#ifdef CONFIG_CRASH_DUMP
+
+static int prepare_elf_headers(void **addr, unsigned long *sz)
+{
+	struct crash_mem *cmem;
+	unsigned int nr_ranges;
+	int ret;
+	u64 i;
+	phys_addr_t start, end;
+
+	nr_ranges = 2; /* for exclusion of crashkernel region */
+	for_each_mem_range(i, &start, &end)
+		nr_ranges++;
+
+	cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);
+	if (!cmem)
+		return -ENOMEM;
+
+	cmem->max_nr_ranges = nr_ranges;
+	cmem->nr_ranges = 0;
+	for_each_mem_range(i, &start, &end) {
+		cmem->ranges[cmem->nr_ranges].start = start;
+		cmem->ranges[cmem->nr_ranges].end = end - 1;
+		cmem->nr_ranges++;
+	}
+
+	/* Exclude crashkernel region */
+	ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
+	if (ret)
+		goto out;
+
+	if (crashk_low_res.end) {
+		ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
+		if (ret)
+			goto out;
+	}
+
+	ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
+
+out:
+	kfree(cmem);
+	return ret;
+}
+
+/* Adds the "elfcorehdr=size@start" command line parameter to command line. */
+static void cmdline_add_elfcorehdr(struct kimage *image, unsigned long *cmdline_tmplen,
+				char *modified_cmdline, unsigned long elfcorehdr_sz)
+{
+	int elfcorehdr_strlen = 0;
+
+	elfcorehdr_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "elfcorehdr=0x%lx@0x%lx ",
+		elfcorehdr_sz, image->elf_load_addr);
+	*cmdline_tmplen += elfcorehdr_strlen;
+}
+
+/*
+ * Adds the "mem=size@start" command line parameter to command line, indicating the
+ * memory region the new kernel can use to boot into.
+ */
+static void cmdline_add_mem(struct kimage *image, unsigned long *cmdline_tmplen,
+				char *modified_cmdline)
+{
+	int mem_strlen = 0;
+
+	mem_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "mem=0x%llx@0x%llx ",
+		crashk_res.end - crashk_res.start + 1, crashk_res.start);
+	*cmdline_tmplen += mem_strlen;
+
+	if (crashk_low_res.end) {
+		mem_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "mem=0x%llx@0x%llx ",
+			crashk_low_res.end - crashk_low_res.start + 1, crashk_low_res.start);
+		*cmdline_tmplen += mem_strlen;
+	}
+}
+
+#endif
+
 /* 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)
@@ -76,6 +153,46 @@ int load_other_segments(struct kimage *image,
 	/* Ensure it's nul terminated */
 	modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
 
+#ifdef CONFIG_CRASH_DUMP
+	/* load elf core header */
+	void *headers;
+	unsigned long headers_sz;
+
+	if (image->type == KEXEC_TYPE_CRASH) {
+		ret = prepare_elf_headers(&headers, &headers_sz);
+		if (ret) {
+			pr_err("Preparing elf core header failed\n");
+			goto out_err;
+		}
+
+		kbuf.buffer = headers;
+		kbuf.bufsz = headers_sz;
+		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
+		kbuf.memsz = headers_sz;
+		kbuf.buf_align = SZ_64K; /* largest supported page size */
+		kbuf.buf_max = ULONG_MAX;
+		kbuf.top_down = true;
+
+		ret = kexec_add_buffer(&kbuf);
+		if (ret) {
+			vfree(headers);
+			goto out_err;
+		}
+		image->elf_headers = headers;
+		image->elf_load_addr = kbuf.mem;
+		image->elf_headers_sz = headers_sz;
+
+		kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
+			      image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
+
+		/* Add the elfcorehdr=size@start parameter to the command line */
+		cmdline_add_elfcorehdr(image, &cmdline_tmplen, modified_cmdline, headers_sz);
+
+		/* Add the mem=size@start parameter to the command line */
+		cmdline_add_mem(image, &cmdline_tmplen, modified_cmdline);
+	}
+#endif
+
 	/* load initrd */
 	if (initrd) {
 		kbuf.buffer = initrd;
Signature · 1 line
2.43.0
#15 lore raw

PATCH v2 5/5 LoongArch: Enable CONFIG_KEXEC_FILE

In reply to Youling Tang

From: Youling Tang <[email protected]>

This patch enables config KEXEC_FILE by default in the loongson3_defconfig, so that user-space tools like kexec-tools can use the same as the default interface for kexec/kdump on LoongArch.

---
 arch/loongarch/configs/loongson3_defconfig | 1 +
 1 file changed, 1 insertion(+)
arch/loongarch/configs/loongson3_defconfig +1 -0
index 34eaee0384c9..6cfea2f37cc0 100644
@@ -45,6 +45,7 @@ CONFIG_EXPERT=y
 CONFIG_KALLSYMS_ALL=y
 CONFIG_PERF_EVENTS=y
 CONFIG_KEXEC=y
+CONFIG_KEXEC_FILE=y
 CONFIG_CRASH_DUMP=y
 CONFIG_LOONGARCH=y
 CONFIG_64BIT=y
Signature · 1 line
2.43.0
maintainer
#16 lore raw
In reply to Youling Tang

Hi, Youling,

Quoted 39 lines · Youling Tang

From: Youling Tang <[email protected]>

This patchset implement kexec_file_load() support on LoongArch.

This patch series enables us to load the LoongArch vmlinuz.efi(pez) or vmlinux.efi(pei) or vmlinux(elf) by specifying its file decriptor, instead of user-filled buffer via kexec_load() syscall.

To use kexec_file_load() system call, instead of kexec_load(), at kexec command, '-s' options must be specified. kexec-tools needs to apply the corresponding patches. These patches can be found in repository [1] and will be submitted to the kexec-tools community later.

The basic usage of kexec_file is:
1) Load second kernel image:

 # kexec -s -l vmlinuz.efi --initrd=initrd.img --reuse-cmdline

2) Startup second kernel:

 # kexec -e

For kdump:
1) Load capture kernel image:

 # kexec -s -p vmlinuz.efi --initrd=initrd.img --reuse-cmdline

2) Do something to crash, like:

 # echo c > /proc/sysrq-trigger

Link:
[1] https://github.com/tangyouling/kexec-tools/commits/main/

Changelog:
v2:
* Merge some patches.
* Add support for ELF format images.
* Rename kexec_image.c to kexec_efi.c .
* When KEXEC_FILE is enabled, RELOCATABLE is selected by default.
* Some minor modifications.

Quoted 4 lines

Youling Tang (5):
LoongArch: Add struct loongarch_image_header for kernel image
LoongArch: Add kexec_file support LoongArch/kexec_file: Support loading ELF binary file

I prefer to combine Patch-2 and Patch-3. :)

Huacai

Quoted 21 lines

LoongArch/kexec_file: Add crash dump support
LoongArch: Enable CONFIG_KEXEC_FILE

 arch/loongarch/Kconfig                     |  10 +
 arch/loongarch/configs/loongson3_defconfig |   1 +
 arch/loongarch/include/asm/image.h         |  57 +++++
 arch/loongarch/include/asm/kexec.h         |  13 ++
 arch/loongarch/kernel/Makefile             |   1 +
 arch/loongarch/kernel/kexec_efi.c          | 111 ++++++++++
 arch/loongarch/kernel/kexec_elf.c          | 105 +++++++++
 arch/loongarch/kernel/machine_kexec.c      |  33 ++-
 arch/loongarch/kernel/machine_kexec_file.c | 235 +++++++++++++++++++++
 9 files changed, 555 insertions(+), 11 deletions(-)
 create mode 100644 arch/loongarch/include/asm/image.h
 create mode 100644 arch/loongarch/kernel/kexec_efi.c
 create mode 100644 arch/loongarch/kernel/kexec_elf.c
 create mode 100644 arch/loongarch/kernel/machine_kexec_file.c

--
2.43.0