[PATCH 5/5] arm64: add support for zboot image

Pingfan Liu piliu at redhat.com
Fri May 5 20:05:21 PDT 2023


zboot image consists of zboot_header and Image.gz. And the compressed
payload should be located and parsed with extra effort. Most of
important, the kernel can only work with Image, so the final fd should
point to a temporary file, which contains Image.

Signed-off-by: Pingfan Liu <piliu at redhat.com>
To: kexec at lists.infradead.org
Cc: horms at verge.net.au
Cc: ardb at kernel.org
Cc: jeremy.linton at arm.com

---
 kexec/arch/arm64/Makefile            |   3 +-
 kexec/arch/arm64/kexec-arm64.c       |   1 +
 kexec/arch/arm64/kexec-arm64.h       |   5 +
 kexec/arch/arm64/kexec-zboot-arm64.c | 261 +++++++++++++++++++++++++++
 kexec/arch/arm64/zboot.h             |  26 +++
 5 files changed, 295 insertions(+), 1 deletion(-)
 create mode 100644 kexec/arch/arm64/kexec-zboot-arm64.c
 create mode 100644 kexec/arch/arm64/zboot.h

diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
index d27c8ee..7826a36 100644
--- a/kexec/arch/arm64/Makefile
+++ b/kexec/arch/arm64/Makefile
@@ -16,7 +16,8 @@ arm64_KEXEC_SRCS += \
 	kexec/arch/arm64/kexec-elf-arm64.c \
 	kexec/arch/arm64/kexec-uImage-arm64.c \
 	kexec/arch/arm64/kexec-image-arm64.c \
-	kexec/arch/arm64/kexec-zImage-arm64.c
+	kexec/arch/arm64/kexec-zImage-arm64.c \
+	kexec/arch/arm64/kexec-zboot-arm64.c
 
 arm64_UIMAGE = kexec/kexec-uImage.c
 
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
index ec6df4b..88b5f57 100644
--- a/kexec/arch/arm64/kexec-arm64.c
+++ b/kexec/arch/arm64/kexec-arm64.c
@@ -72,6 +72,7 @@ const struct arch_map_entry arches[] = {
 
 struct file_type file_type[] = {
 	{"vmlinux", elf_arm64_probe, elf_arm64_load, elf_arm64_usage},
+	{"zboot", zboot_arm64_probe, zboot_arm64_load, zboot_arm64_usage},
 	{"Image", image_arm64_probe, image_arm64_load, image_arm64_usage},
 	{"uImage", uImage_arm64_probe, uImage_arm64_load, uImage_arm64_usage},
 	{"zImage", zImage_arm64_probe, zImage_arm64_load, zImage_arm64_usage},
diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h
index 88bb508..98e1be9 100644
--- a/kexec/arch/arm64/kexec-arm64.h
+++ b/kexec/arch/arm64/kexec-arm64.h
@@ -50,6 +50,11 @@ int zImage_arm64_load(int argc, char **argv, const char *kernel_buf,
 void zImage_arm64_usage(void);
 
 
+int zboot_arm64_probe(const char *kernel_buf, off_t kernel_size, struct kexec_info *info);
+int zboot_arm64_load(int argc, char **argv, const char *kernel_buf,
+	off_t kernel_size, struct kexec_info *info);
+void zboot_arm64_usage(void);
+
 extern off_t initrd_base;
 extern off_t initrd_size;
 
diff --git a/kexec/arch/arm64/kexec-zboot-arm64.c b/kexec/arch/arm64/kexec-zboot-arm64.c
new file mode 100644
index 0000000..b6c4fe3
--- /dev/null
+++ b/kexec/arch/arm64/kexec-zboot-arm64.c
@@ -0,0 +1,261 @@
+/*
+ * ARM64 kexec zboot Image support.
+ *
+ * Based on kexec-zImage-arm64.c
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "crashdump-arm64.h"
+#include "image-header.h"
+#include "kexec.h"
+#include "kexec-arm64.h"
+#include "kexec-syscall.h"
+#include "kexec-zlib.h"
+#include "zboot.h"
+#include "arch/options.h"
+
+#define MZ_MAGIC	0x5a4d	/* "MZ" */
+#define FILENAME_IMAGE_GZ		"/tmp/zboot_Image_gzXXXXXX"
+#define FILENAME_IMAGE		"/tmp/zboot_ImageXXXXXX"
+
+
+static struct zboot_image_header *check_zboot_header(char *buf, off_t size)
+{
+
+	uint32_t magic = MZ_MAGIC;
+	struct zboot_image_header *zh =
+		(struct zboot_image_header *)(buf);
+
+	if (!zh || (size < sizeof(*zh)))
+		return NULL;
+
+	if (memcmp(&zh->magic, &magic, sizeof(zh->magic))) {
+		dbgprintf("%s: Not an zboot Image\n", __func__);
+		return NULL;
+	}
+	if (strncmp(zh->zimg, "zimg", 4)) {
+		dbgprintf("%s: Not an zboot Image\n", __func__);
+		return NULL;
+	}
+	return zh;
+}
+
+/* Returns:
+ * -1 : in case of error/invalid format.
+ */
+int zboot_arm64_probe(const char *kern_fname, off_t kernel_size, struct kexec_info *info)
+{
+	int ret = 0;
+	int fd = 0, gz_fd = 0;
+	int kernel_fd = 0;
+	char *fname = NULL, *gz_fname;
+	char *kernel_buf, *uncompressed_buf = NULL;
+	char *compressed_kernel;
+	const struct arm64_image_header *h;
+	struct zboot_image_header *zh;
+
+	kernel_buf = slurp_file(kern_fname, &kernel_size);
+	zh = check_zboot_header(kernel_buf, kernel_size);
+	if (!zh)
+		return -1;
+
+	if (!(gz_fname = strdup(FILENAME_IMAGE_GZ))) {
+		dbgprintf("%s: Can't duplicate strings %s\n", __func__,
+				gz_fname);
+		return -1;
+	}
+
+	if ((gz_fd = mkstemp(gz_fname)) < 0) {
+		dbgprintf("%s: Can't open file %s\n", __func__,
+				gz_fname);
+		ret = -1;
+		goto fail_mkstemp;
+	}
+
+	/* locate the Image.gz and copy it to a temp file */
+
+	/*
+	 * zboot has SizeOfCode, SizeOfImage, SizeOfHeaders appended at the end. And
+	 * each occupies 4 bytes.
+	 */
+	compressed_kernel = (char *)kernel_buf + zh->gzdata_offset;
+	if (write(gz_fd, compressed_kernel, zh->gzdata_size) != zh->gzdata_size) {
+		dbgprintf("%s: Can't write the Image.gz %s\n",
+				__func__, gz_fname);
+		ret = -1;
+		goto fail_bad_header;
+	}
+	free(kernel_buf);
+	close(gz_fd);
+
+	/* slurp in the input kernel */
+	dbgprintf("%s: ", __func__);
+	/*
+	 * If the kernel enables integrity check, the innermost Image should
+	 * be signed.
+	 */
+	uncompressed_buf = slurp_decompress_file(gz_fname,
+							&kernel_size);
+
+
+	/* double checking against internal Image */
+
+	if (kernel_size < sizeof(struct arm64_image_header)) {
+		dbgprintf("%s: No arm64 image header.\n", __func__);
+		ret = -1;
+		goto fail_bad_header;
+	}
+
+	h = (const struct arm64_image_header *)(uncompressed_buf);
+
+	if (!arm64_header_check_magic(h)) {
+		dbgprintf("%s: Bad arm64 image header.\n", __func__);
+		ret = -1;
+		goto fail_bad_header;
+	}
+
+	if (!(fname = strdup(FILENAME_IMAGE))) {
+		dbgprintf("%s: Can't duplicate strings %s\n", __func__,
+				fname);
+		return -1;
+	}
+
+	if ((fd = mkstemp(fname)) < 0) {
+		dbgprintf("%s: Can't open file %s\n", __func__,
+				fname);
+		ret = -1;
+		goto fail_mkstemp;
+	}
+	if (write(fd, uncompressed_buf,	kernel_size) != kernel_size) {
+		dbgprintf("%s: Can't write the uncompressed file %s\n",
+				__func__, fname);
+		ret = -1;
+		goto fail_bad_header;
+	}
+
+	close(fd);
+
+	/* Open the tmp file again, this time in O_RDONLY mode, as
+	 * opening the file in O_RDWR and calling kexec_file_load()
+	 * causes the kernel to return -ETXTBSY
+	 */
+	kernel_fd = open(fname, O_RDONLY);
+	if (kernel_fd == -1) {
+		dbgprintf("%s: Failed to open file %s\n",
+				__func__, fname);
+		ret = -1;
+		goto fail_bad_header;
+	}
+	info->kernel_fd = kernel_fd;
+	info->kernel_buf = uncompressed_buf;
+
+	unlink(gz_fname);
+	free(gz_fname);
+	unlink(fname);
+	free(fname);
+
+	return ret;
+
+fail_bad_header:
+	free(uncompressed_buf);
+
+	if (fd >= 0)
+		close(fd);
+
+	unlink(fname);
+	unlink(gz_fname);
+
+fail_mkstemp:
+	free(fname);
+	free(gz_fname);
+
+	return ret;
+}
+
+int zboot_arm64_load(int argc, char **argv, const char *kernel_buf,
+	off_t kernel_size, struct kexec_info *info)
+{
+	const struct arm64_image_header *header;
+	unsigned long kernel_segment;
+	int result;
+
+	if (info->file_mode) {
+		if (arm64_opts.initrd) {
+			info->initrd_fd = open(arm64_opts.initrd, O_RDONLY);
+			if (info->initrd_fd == -1) {
+				fprintf(stderr,
+					"Could not open initrd file %s:%s\n",
+					arm64_opts.initrd, strerror(errno));
+				result = EFAILED;
+				goto exit;
+			}
+		}
+
+		if (arm64_opts.command_line) {
+			info->command_line = (char *)arm64_opts.command_line;
+			info->command_line_len =
+					strlen(arm64_opts.command_line) + 1;
+		}
+
+		return 0;
+	}
+
+	header = (const struct arm64_image_header *)(kernel_buf);
+
+	if (arm64_process_image_header(header))
+		return EFAILED;
+
+	kernel_segment = arm64_locate_kernel_segment(info);
+
+	if (kernel_segment == ULONG_MAX) {
+		dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+		result = EFAILED;
+		goto exit;
+	}
+
+	dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+	dbgprintf("%s: text_offset:    %016lx\n", __func__,
+		arm64_mem.text_offset);
+	dbgprintf("%s: image_size:     %016lx\n", __func__,
+		arm64_mem.image_size);
+	dbgprintf("%s: phys_offset:    %016lx\n", __func__,
+		arm64_mem.phys_offset);
+	dbgprintf("%s: vp_offset:      %016lx\n", __func__,
+		arm64_mem.vp_offset);
+	dbgprintf("%s: PE format:      %s\n", __func__,
+		(arm64_header_check_pe_sig(header) ? "yes" : "no"));
+
+	/* 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_phys_virt(info, kernel_buf, kernel_size,
+			kernel_segment + arm64_mem.text_offset,
+			arm64_mem.image_size, 0);
+
+	/* load additional data */
+	result = arm64_load_other_segments(info, kernel_segment
+		+ arm64_mem.text_offset);
+
+exit:
+	if (result)
+		fprintf(stderr, "kexec: load failed.\n");
+	return result;
+}
+
+void zboot_arm64_usage(void)
+{
+	printf("An ARM64 zboot Image, compressed, big or little endian.\n");
+}
diff --git a/kexec/arch/arm64/zboot.h b/kexec/arch/arm64/zboot.h
new file mode 100644
index 0000000..fa97aa3
--- /dev/null
+++ b/kexec/arch/arm64/zboot.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef ZBOOT_H
+#define ZBOOT_H
+
+struct zboot_image_header {
+	union {
+		struct {
+			uint32_t magic;
+			/* image type, .ascii "zimg" */
+			char zimg[4];
+			int32_t gzdata_offset;
+			int32_t gzdata_size;
+			int32_t reserved[2];
+			/* compression type, .asciz */
+			char comp_type[];
+		};
+		struct {
+			char pad[56];
+		};
+	};
+	/* 0x818223cd */
+	uint32_t linux_pe_magic;
+	int32_t pe_header_offset;
+} __packed;
+
+#endif
-- 
2.31.1




More information about the kexec mailing list