On Fri, May 5, 2023 at 12:44 AM Jeremy Linton <jeremy.linton@xxxxxxx> wrote: > > The kernel EFI stub ZBOOT feature creates a PE that > contains a compressed linux kernel image. The stub > when run in a valid UEFI environment then decompresses > the resulting image and executes it. > > Support these image formats with kexec as well to avoid > having to keep an alternate kernel image around. > > This patch adds a the _probe() and usage() routines needed > for kexec to understand this format. > > Signed-off-by: Jeremy Linton <jeremy.linton@xxxxxxx> > --- > kexec/arch/arm64/image-header.h | 11 ++ > kexec/arch/arm64/kexec-vmlinuz-arm64.c | 172 +++++++++++++++++++++++++ > 2 files changed, 183 insertions(+) > create mode 100644 kexec/arch/arm64/kexec-vmlinuz-arm64.c > > diff --git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h > index 158d411..5106b67 100644 > --- a/kexec/arch/arm64/image-header.h > +++ b/kexec/arch/arm64/image-header.h > @@ -35,8 +35,19 @@ struct arm64_image_header { > uint32_t pe_header; > }; > > +/* see drivers/firmware/efi/libstub/zboot-header.S */ > +struct arm64_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; > +}; > + > static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U}; > static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'}; > +static const uint8_t arm64_pe_machtype[6] = {'P','E', 0x0, 0x0, 0x64, 0xAA}; > static const uint64_t arm64_image_flag_be = (1UL << 0); > static const uint64_t arm64_image_flag_page_size = (3UL << 1); > static const uint64_t arm64_image_flag_placement = (1UL << 3); > diff --git a/kexec/arch/arm64/kexec-vmlinuz-arm64.c b/kexec/arch/arm64/kexec-vmlinuz-arm64.c > new file mode 100644 > index 0000000..7033e2e > --- /dev/null > +++ b/kexec/arch/arm64/kexec-vmlinuz-arm64.c > @@ -0,0 +1,172 @@ > +/* > + * ARM64 PE compressed Image (vmlinuz, ZBOOT) support. > + * > + * Several distros use 'make zinstall' rule inside > + * 'arch/arm64/boot/Makefile' to install the arm64 > + * ZBOOT compressed file inside the boot destination > + * directory (for e.g. /boot). > + * > + * 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. > + * > + * Note this, module doesn't provide a _load() function instead > + * relying on image_arm64_load() to load the resulting decompressed > + * image. > + * > + * So basically the kernel space still gets a decompressed > + * kernel image to load via kexec-tools. > + */ > + > +#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 "arch/options.h" > + > +#define FILENAME_IMAGE "/tmp/ImageXXXXXX" > + > +/* Returns: > + * -1 : in case of error/invalid format (not a valid PE+compressed ZBOOT format. > + * fd : File descriptor of the temp file containing the decompressed > + * Image. > + */ > +int pez_arm64_probe(const char *kernel_buf, off_t kernel_size) > +{ > + int ret = -1; > + int fd = 0; > + int kernel_fd = 0; > + char *fname = NULL; > + char *kernel_uncompressed_buf = NULL; > + off_t decompressed_size = 0; > + const struct arm64_image_header *h; > + const struct arm64_zboot_header *z; > + h = (const struct arm64_image_header *)(kernel_buf); > + z = (const struct arm64_zboot_header *)(kernel_buf); > + > + dbgprintf("%s: PROBE.\n", __func__); > + if (kernel_size < sizeof(struct arm64_image_header)) { > + dbgprintf("%s: Not large enough to be a PE image.\n", __func__); > + return -1; > + } > + if (!arm64_header_check_pe_sig(h)) { > + dbgprintf("%s: Not an PE image.\n", __func__); > + return -1; > + } > + > + if (kernel_size < sizeof(struct arm64_image_header) + h->pe_header) { > + dbgprintf("%s: PE image offset larger than image.\n", __func__); > + return -1; > + } > + > + if (memcmp(&kernel_buf[h->pe_header], > + arm64_pe_machtype, sizeof(arm64_pe_machtype))) { > + dbgprintf("%s: PE header doesn't match machine type.\n", __func__); > + return -1; > + } > + > + if (memcmp(&z->image_type, "zimg", sizeof(z->image_type))) { > + dbgprintf("%s: PE doesn't contain a compressed kernel.\n", __func__); > + return -1; > + } > + > + 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 (kernel_size < 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, &kernel_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); > + Later, the image load should check against kernel_uncompressed_buf. But the original interface design can not return it. So I proposed a new probe interface, which can return such info. Thanks, Pingfan > + dbgprintf("%s: decompressed size %ld\n", __func__, decompressed_size); > + > + h = (const struct arm64_image_header *)(kernel_uncompressed_buf); > + if (!arm64_header_check_magic(h)) { > + dbgprintf("%s: Bad arm64 image header.\n", __func__); > + ret = -1; > + goto fail_bad_header; > + } > + > + 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: ", __func__); > + > + ret = kernel_fd; > + > +fail_bad_header: > + free(kernel_uncompressed_buf); > + > +fail_write: > + if (fd >= 0) > + close(fd); > + > + unlink(fname); > + > +fail_mkstemp: > + free(fname); > + > + return ret; > +} > + > +void pez_arm64_usage(void) > +{ > + printf( > +" An ARM64 vmlinuz, PE image of a compressed, little endian.\n" > +" kernel, built with ZBOOT enabled.\n\n"); > +} > -- > 2.40.0 > > > _______________________________________________ > kexec mailing list > kexec@xxxxxxxxxxxxxxxxxxx > http://lists.infradead.org/mailman/listinfo/kexec > _______________________________________________ kexec mailing list kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec