From f41c4182b0c403ea93cf47dcdc96437f5d6c53da Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Thu, 3 Aug 2023 10:41:50 +0800 Subject: 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 [Modified by Pingfan to export kernel fd] Signed-off-by: Pingfan Liu [Corrected indentation] Signed-off-by: Simon Horman --- include/Makefile | 1 + include/kexec-pe-zboot.h | 15 ++++++ kexec/Makefile | 1 + kexec/kexec-pe-zboot.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 include/kexec-pe-zboot.h create mode 100644 kexec/kexec-pe-zboot.c 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 +#include +#include +#include +#include +#include "kexec.h" +#include + +#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; +} -- cgit 1.2.3-korg