summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYouling Tang <tangyouling@loongson.cn>2022-09-28 10:28:24 +0800
committerSimon Horman <horms@kernel.org>2022-10-10 13:35:13 +0200
commit615b6757dcab7bda01d9cb9945738b6548f97af1 (patch)
treefe9dd0a93e408192b78ca32232d460f3a243f445
parent1c8bf2dc0127b06f97a7973488c7f8cf9a5f7c19 (diff)
downloadkexec-tools-615b6757dcab7bda01d9cb9945738b6548f97af1.tar.gz
LoongArch: PE format image loading support
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@loongson.cn> Signed-off-by: Simon Horman <horms@kernel.org>
-rw-r--r--kexec/arch/loongarch/Makefile2
-rw-r--r--kexec/arch/loongarch/image-header.h79
-rw-r--r--kexec/arch/loongarch/kexec-elf-loongarch.c3
-rw-r--r--kexec/arch/loongarch/kexec-loongarch.c19
-rw-r--r--kexec/arch/loongarch/kexec-loongarch.h9
-rw-r--r--kexec/arch/loongarch/kexec-pei-loongarch.c117
6 files changed, 229 insertions, 0 deletions
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@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");
+}