[PATCH 3/5] LoongArch: PE format image loading support

Youling Tang tangyouling at loongson.cn
Tue Sep 27 19:28:24 PDT 2022


The LoongArch kernel will mainly use the vmlinux.efi image in PE format,
so add it support.

I tested this on LoongArch 3A5000 machine and works as expected,

kexec:
  $ sudo kexec -l /boot/vmlinux.efi --reuse-cmdline
  $ sudo kexec -e

kdump:
  $ sudo kexec -p /boot/vmlinux-kdump.efi --reuse-cmdline --append="nr_cpus=1"
  # echo c > /proc/sysrq_trigger

Signed-off-by: Youling Tang <tangyouling at loongson.cn>
---
 kexec/arch/loongarch/Makefile              |   2 +
 kexec/arch/loongarch/image-header.h        |  79 ++++++++++++++
 kexec/arch/loongarch/kexec-elf-loongarch.c |   3 +
 kexec/arch/loongarch/kexec-loongarch.c     |  19 ++++
 kexec/arch/loongarch/kexec-loongarch.h     |   9 ++
 kexec/arch/loongarch/kexec-pei-loongarch.c | 117 +++++++++++++++++++++
 6 files changed, 229 insertions(+)
 create mode 100644 kexec/arch/loongarch/image-header.h
 create mode 100644 kexec/arch/loongarch/kexec-pei-loongarch.c

diff --git a/kexec/arch/loongarch/Makefile b/kexec/arch/loongarch/Makefile
index e5e190a..3b33b96 100644
--- a/kexec/arch/loongarch/Makefile
+++ b/kexec/arch/loongarch/Makefile
@@ -3,6 +3,7 @@
 #
 loongarch_KEXEC_SRCS =  kexec/arch/loongarch/kexec-loongarch.c
 loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-loongarch.c
+loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-pei-loongarch.c
 loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-rel-loongarch.c
 loongarch_KEXEC_SRCS += kexec/arch/loongarch/crashdump-loongarch.c
 
@@ -16,5 +17,6 @@ loongarch_VIRT_TO_PHYS =
 
 dist += kexec/arch/loongarch/Makefile $(loongarch_KEXEC_SRCS)			\
 	kexec/arch/loongarch/kexec-loongarch.h					\
+	kexec/arch/loongarch/image-header.h					\
 	kexec/arch/loongarch/crashdump-loongarch.h				\
 	kexec/arch/loongarch/include/arch/options.h
diff --git a/kexec/arch/loongarch/image-header.h b/kexec/arch/loongarch/image-header.h
new file mode 100644
index 0000000..3b75765
--- /dev/null
+++ b/kexec/arch/loongarch/image-header.h
@@ -0,0 +1,79 @@
+/*
+ * LoongArch binary image header.
+ */
+
+#if !defined(__LOONGARCH_IMAGE_HEADER_H)
+#define __LOONGARCH_IMAGE_HEADER_H
+
+#include <endian.h>
+#include <stdint.h>
+
+/**
+ * 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;
+};
+
+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]);
+}
+
+static inline uint64_t loongarch_header_text_offset(
+	const struct loongarch_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return le64toh(h->text_offset);
+}
+
+static inline uint64_t loongarch_header_image_size(
+	const struct loongarch_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return le64toh(h->image_size);
+}
+
+static inline uint64_t loongarch_header_kernel_entry(
+	const struct loongarch_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return le64toh(h->kernel_entry);
+}
+
+#endif
diff --git a/kexec/arch/loongarch/kexec-elf-loongarch.c b/kexec/arch/loongarch/kexec-elf-loongarch.c
index a5ec356..2bf128f 100644
--- a/kexec/arch/loongarch/kexec-elf-loongarch.c
+++ b/kexec/arch/loongarch/kexec-elf-loongarch.c
@@ -50,6 +50,7 @@ out:
 int elf_loongarch_load(int argc, char **argv, const char *kernel_buf,
 	off_t kernel_size, struct kexec_info *info)
 {
+	const struct loongarch_image_header *header = NULL;
 	unsigned long kernel_segment;
 	struct mem_ehdr ehdr;
 	int result;
@@ -76,6 +77,8 @@ int elf_loongarch_load(int argc, char **argv, const char *kernel_buf,
 		loongarch_mem.text_offset);
 	dbgprintf("%s: phys_offset:    %016lx\n", __func__,
 		loongarch_mem.phys_offset);
+	dbgprintf("%s: PE format:      %s\n", __func__,
+		(loongarch_header_check_pe_sig(header) ? "yes" : "no"));
 
 	/* create and initialize elf core header segment */
 	if (info->kexec_flags & KEXEC_ON_CRASH) {
diff --git a/kexec/arch/loongarch/kexec-loongarch.c b/kexec/arch/loongarch/kexec-loongarch.c
index ce7db2c..3fdba01 100644
--- a/kexec/arch/loongarch/kexec-loongarch.c
+++ b/kexec/arch/loongarch/kexec-loongarch.c
@@ -165,6 +165,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
 
 struct file_type file_type[] = {
 	{"elf-loongarch", elf_loongarch_probe, elf_loongarch_load, elf_loongarch_usage},
+	{"pei-loongarch", pei_loongarch_probe, pei_loongarch_load, pei_loongarch_usage},
 };
 int file_types = sizeof(file_type) / sizeof(file_type[0]);
 
@@ -172,6 +173,24 @@ int file_types = sizeof(file_type) / sizeof(file_type[0]);
 
 struct loongarch_mem loongarch_mem;
 
+/**
+ * loongarch_process_image_header - Process the loongarch image header.
+ */
+
+int loongarch_process_image_header(const struct loongarch_image_header *h)
+{
+
+	if (!loongarch_header_check_pe_sig(h))
+		return EFAILED;
+
+	if (h->image_size) {
+		loongarch_mem.text_offset = loongarch_header_text_offset(h);
+		loongarch_mem.image_size = loongarch_header_image_size(h);
+	}
+
+	return 0;
+}
+
 void arch_usage(void)
 {
 	printf(loongarch_opts_usage);
diff --git a/kexec/arch/loongarch/kexec-loongarch.h b/kexec/arch/loongarch/kexec-loongarch.h
index cb9b79a..5120a26 100644
--- a/kexec/arch/loongarch/kexec-loongarch.h
+++ b/kexec/arch/loongarch/kexec-loongarch.h
@@ -3,6 +3,8 @@
 
 #include <sys/types.h>
 
+#include "image-header.h"
+
 #define BOOT_BLOCK_VERSION 17
 #define BOOT_BLOCK_LAST_COMP_VERSION 16
 
@@ -21,6 +23,13 @@ int elf_loongarch_load(int argc, char **argv, const char *buf, off_t len,
 	struct kexec_info *info);
 void elf_loongarch_usage(void);
 
+int pei_loongarch_probe(const char *buf, off_t len);
+int pei_loongarch_load(int argc, char **argv, const char *buf, off_t len,
+	struct kexec_info *info);
+void pei_loongarch_usage(void);
+
+int loongarch_process_image_header(const struct loongarch_image_header *h);
+
 unsigned long loongarch_locate_kernel_segment(struct kexec_info *info);
 int loongarch_load_other_segments(struct kexec_info *info,
 	unsigned long hole_min);
diff --git a/kexec/arch/loongarch/kexec-pei-loongarch.c b/kexec/arch/loongarch/kexec-pei-loongarch.c
new file mode 100644
index 0000000..f86ac61
--- /dev/null
+++ b/kexec/arch/loongarch/kexec-pei-loongarch.c
@@ -0,0 +1,117 @@
+/*
+ * LoongArch kexec PE format binary image support.
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited.
+ *   Youling Tang <tangyouling at loongson.cn>
+ *
+ * derived from kexec-image-arm64.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+
+#include <limits.h>
+#include <errno.h>
+#include <elf.h>
+
+#include "kexec.h"
+#include "kexec-elf.h"
+#include "image-header.h"
+#include "kexec-syscall.h"
+#include "crashdump-loongarch.h"
+#include "kexec-loongarch.h"
+#include "arch/options.h"
+
+int pei_loongarch_probe(const char *kernel_buf, off_t kernel_size)
+{
+	const struct loongarch_image_header *h;
+
+	if (kernel_size < sizeof(struct loongarch_image_header)) {
+		dbgprintf("%s: No loongarch image header.\n", __func__);
+		return -1;
+	}
+
+	h = (const struct loongarch_image_header *)(kernel_buf);
+
+	if (!loongarch_header_check_pe_sig(h)) {
+		dbgprintf("%s: Bad loongarch PE image header.\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
+int pei_loongarch_load(int argc, char **argv, const char *buf,
+	off_t len, struct kexec_info *info)
+{
+	int result;
+	unsigned long hole_min = 0;
+	unsigned long kernel_segment, kernel_entry;
+	const struct loongarch_image_header *header;
+
+	header = (const struct loongarch_image_header *)(buf);
+
+	if (loongarch_process_image_header(header))
+		return EFAILED;
+
+	kernel_segment = loongarch_locate_kernel_segment(info);
+
+	if (kernel_segment == ULONG_MAX) {
+		dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+		result = EFAILED;
+		goto exit;
+	}
+
+	kernel_entry = virt_to_phys(loongarch_header_kernel_entry(header));
+
+	dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+	dbgprintf("%s: kernel_entry:   %016lx\n", __func__, kernel_entry);
+	dbgprintf("%s: image_size:     %016lx\n", __func__,
+		loongarch_mem.image_size);
+	dbgprintf("%s: text_offset:    %016lx\n", __func__,
+		loongarch_mem.text_offset);
+	dbgprintf("%s: phys_offset:    %016lx\n", __func__,
+		loongarch_mem.phys_offset);
+	dbgprintf("%s: PE format:      %s\n", __func__,
+		(loongarch_header_check_pe_sig(header) ? "yes" : "no"));
+
+	/* Get kernel entry point */
+	info->entry = (void *)kernel_entry;
+
+	hole_min = kernel_segment + loongarch_mem.image_size;
+
+	/* Create and initialize elf core header segment */
+	if (info->kexec_flags & KEXEC_ON_CRASH) {
+		result = load_crashdump_segments(info);
+		if (result) {
+			dbgprintf("%s: Creating eflcorehdr failed.\n",
+								__func__);
+			goto exit;
+		}
+	}
+
+	/* Load the kernel */
+	add_segment(info, buf, len, kernel_segment, loongarch_mem.image_size);
+
+	/* Prepare and load dtb and initrd data */
+	result = loongarch_load_other_segments(info, hole_min);
+	if (result) {
+		fprintf(stderr, "kexec: Load dtb and initrd segments failed.\n");
+		goto exit;
+	}
+
+exit:
+	if (result)
+		fprintf(stderr, "kexec: load failed.\n");
+
+	return result;
+}
+
+void pei_loongarch_usage(void)
+{
+	printf(
+"     An LoongArch PE format binary image, uncompressed, little endian.\n"
+"     Typically a vmlinux.efi file.\n\n");
+}
-- 
2.36.0




More information about the kexec mailing list