[PATCH 2/4] arm64: Add ZBOOT PE containing compressed image support
Pingfan Liu
piliu at redhat.com
Thu May 4 20:17:04 PDT 2023
On Fri, May 5, 2023 at 12:44 AM Jeremy Linton <jeremy.linton at arm.com> 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 at arm.com>
> ---
> 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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
>
More information about the kexec
mailing list