summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Linton <jeremy.linton@arm.com>2023-08-03 10:41:50 +0800
committerSimon Horman <horms@kernel.org>2023-08-11 09:25:12 +0200
commitf41c4182b0c403ea93cf47dcdc96437f5d6c53da (patch)
tree3e60f781578bb7992ad2388c62d51172507cd841
parent1572b91da7c4512d111a340185914c34acb0a139 (diff)
downloadkexec-tools-f41c4182b0c403ea93cf47dcdc96437f5d6c53da.tar.gz
kexec/zboot: Add arch independent zboot support
The linux kernel CONFIG_ZBOOT option creates self decompressing PE kernel images. So this means that kexec should have a generic understanding of the format which may be used by multiple arches. So lets add an arch independent validation and decompression routine. Signed-off-by: Jeremy Linton <jeremy.linton@arm.com> [Modified by Pingfan to export kernel fd] Signed-off-by: Pingfan Liu <piliu@redhat.com> [Corrected indentation] Signed-off-by: Simon Horman <horms@kernel.org>
-rw-r--r--include/Makefile1
-rw-r--r--include/kexec-pe-zboot.h15
-rw-r--r--kexec/Makefile1
-rw-r--r--kexec/kexec-pe-zboot.c131
4 files changed, 148 insertions, 0 deletions
diff --git a/include/Makefile b/include/Makefile
index 621ce9f9..cd88a26b 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -1,6 +1,7 @@
dist += include/Makefile \
include/config.h \
include/config.h.in \
+ include/kexec-pe-zboot.h \
include/kexec-uImage.h \
include/x86/x86-linux.h \
include/x86/mb_info.h \
diff --git a/include/kexec-pe-zboot.h b/include/kexec-pe-zboot.h
new file mode 100644
index 00000000..e2e0448a
--- /dev/null
+++ b/include/kexec-pe-zboot.h
@@ -0,0 +1,15 @@
+#ifndef __KEXEC_PE_ZBOOT_H__
+#define __KEXEC_PE_ZBOOT_H__
+
+/* see drivers/firmware/efi/libstub/zboot-header.S */
+struct linux_pe_zboot_header {
+ uint32_t mz_magic;
+ uint32_t image_type;
+ uint32_t payload_offset;
+ uint32_t payload_size;
+ uint32_t reserved[2];
+ uint32_t compress_type;
+};
+
+int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd);
+#endif
diff --git a/kexec/Makefile b/kexec/Makefile
index 8a52e8de..11682bfa 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -17,6 +17,7 @@ KEXEC_SRCS_base += kexec/kexec-elf-exec.c
KEXEC_SRCS_base += kexec/kexec-elf-core.c
KEXEC_SRCS_base += kexec/kexec-elf-rel.c
KEXEC_SRCS_base += kexec/kexec-elf-boot.c
+KEXEC_SRCS_base += kexec/kexec-pe-zboot.c
KEXEC_SRCS_base += kexec/kexec-iomem.c
KEXEC_SRCS_base += kexec/firmware_memmap.c
KEXEC_SRCS_base += kexec/crashdump.c
diff --git a/kexec/kexec-pe-zboot.c b/kexec/kexec-pe-zboot.c
new file mode 100644
index 00000000..2f2e052b
--- /dev/null
+++ b/kexec/kexec-pe-zboot.c
@@ -0,0 +1,131 @@
+/*
+ * Generic PE compressed Image (vmlinuz, ZBOOT) support.
+ *
+ * Several distros use 'make zinstall' with CONFIG_ZBOOT
+ * enabled to create UEFI PE images that contain
+ * a decompressor and a compressed kernel image.
+ *
+ * Currently we cannot use kexec_file_load() to load vmlinuz
+ * PE images that self decompress.
+ *
+ * To support ZBOOT, we should:
+ * a). Copy the compressed contents of vmlinuz to a temporary file.
+ * b). Decompress (gunzip-decompress) the contents inside the
+ * temporary file.
+ * c). Validate the resulting image and write it back to the
+ * temporary file.
+ * d). Pass the 'fd' of the temporary file to the kernel space.
+ *
+ * This module contains the arch independent code for the above,
+ * arch specific PE and image checks should wrap calls
+ * to functions in this module.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "kexec.h"
+#include <kexec-pe-zboot.h>
+
+#define FILENAME_IMAGE "/tmp/ImageXXXXXX"
+
+/*
+ * Returns -1 : in case of error/invalid format (not a valid PE+compressed ZBOOT format.
+ *
+ * crude_buf: the content, which is read from the kernel file without any processing
+ */
+int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd)
+{
+ int ret = -1;
+ int fd = 0;
+ char *fname = NULL;
+ char *kernel_uncompressed_buf = NULL;
+ off_t decompressed_size = 0;
+ const struct linux_pe_zboot_header *z;
+
+ z = (const struct linux_pe_zboot_header *)(crude_buf);
+
+ if (memcmp(&z->image_type, "zimg", sizeof(z->image_type))) {
+ dbgprintf("%s: PE doesn't contain a compressed kernel.\n", __func__);
+ return -1;
+ }
+
+ /*
+ * At the moment its possible to create images with more compression
+ * algorithms than are supported here, error out if we detect that.
+ */
+ if (memcmp(&z->compress_type, "gzip", 4) &&
+ memcmp(&z->compress_type, "lzma", 4)) {
+ dbgprintf("%s: kexec can only decompress gziped and lzma images.\n", __func__);
+ return -1;
+ }
+
+ if (buf_sz < z->payload_offset + z->payload_size) {
+ dbgprintf("%s: PE too small to contain complete payload.\n", __func__);
+ return -1;
+ }
+
+ if (!(fname = strdup(FILENAME_IMAGE))) {
+ dbgprintf("%s: Can't duplicate strings\n", __func__);
+ 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, &crude_buf[z->payload_offset],
+ z->payload_size) != z->payload_size) {
+ dbgprintf("%s: Can't write the compressed file %s\n",
+ __func__, fname);
+ ret = -1;
+ goto fail_write;
+ }
+
+ kernel_uncompressed_buf = slurp_decompress_file(fname,
+ &decompressed_size);
+
+ dbgprintf("%s: decompressed size %ld\n", __func__, decompressed_size);
+
+ lseek(fd, 0, SEEK_SET);
+
+ if (write(fd, kernel_uncompressed_buf,
+ decompressed_size) != decompressed_size) {
+ dbgprintf("%s: Can't write the decompressed file %s\n",
+ __func__, fname);
+ ret = -1;
+ goto fail_bad_header;
+ }
+
+ *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;
+ }
+
+ dbgprintf("%s: done\n", __func__);
+
+ ret = 0;
+ goto fail_write;
+
+fail_bad_header:
+ free(kernel_uncompressed_buf);
+
+fail_write:
+ if (fd >= 0)
+ close(fd);
+
+ unlink(fname);
+
+fail_mkstemp:
+ free(fname);
+
+ return ret;
+}