|
@@ -857,6 +857,97 @@ ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz)
|
|
return bytes;
|
|
return bytes;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* The PE/COFF MS-DOS stub magic number */
|
|
|
|
+#define EFI_PE_MSDOS_MAGIC "MZ"
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * The Linux header magic number for a EFI PE/COFF
|
|
|
|
+ * image targetting an unspecified architecture.
|
|
|
|
+ */
|
|
|
|
+#define EFI_PE_LINUX_MAGIC "\xcd\x23\x82\x81"
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Bootable Linux kernel images may be packaged as EFI zboot images, which are
|
|
|
|
+ * self-decompressing executables when loaded via EFI. The compressed payload
|
|
|
|
+ * can also be extracted from the image and decompressed by a non-EFI loader.
|
|
|
|
+ *
|
|
|
|
+ * The de facto specification for this format is at the following URL:
|
|
|
|
+ *
|
|
|
|
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/efi/libstub/zboot-header.S
|
|
|
|
+ *
|
|
|
|
+ * This definition is based on Linux upstream commit 29636a5ce87beba.
|
|
|
|
+ */
|
|
|
|
+struct linux_efi_zboot_header {
|
|
|
|
+ uint8_t msdos_magic[2]; /* PE/COFF 'MZ' magic number */
|
|
|
|
+ uint8_t reserved0[2];
|
|
|
|
+ uint8_t zimg[4]; /* "zimg" for Linux EFI zboot images */
|
|
|
|
+ uint32_t payload_offset; /* LE offset to compressed payload */
|
|
|
|
+ uint32_t payload_size; /* LE size of the compressed payload */
|
|
|
|
+ uint8_t reserved1[8];
|
|
|
|
+ char compression_type[32]; /* Compression type, NUL terminated */
|
|
|
|
+ uint8_t linux_magic[4]; /* Linux header magic */
|
|
|
|
+ uint32_t pe_header_offset; /* LE offset to the PE header */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Check whether *buffer points to a Linux EFI zboot image in memory.
|
|
|
|
+ *
|
|
|
|
+ * If it does, attempt to decompress it to a new buffer, and free the old one.
|
|
|
|
+ * If any of this fails, return an error to the caller.
|
|
|
|
+ *
|
|
|
|
+ * If the image is not a Linux EFI zboot image, do nothing and return success.
|
|
|
|
+ */
|
|
|
|
+ssize_t unpack_efi_zboot_image(uint8_t **buffer, int *size)
|
|
|
|
+{
|
|
|
|
+ const struct linux_efi_zboot_header *header;
|
|
|
|
+ uint8_t *data = NULL;
|
|
|
|
+ int ploff, plsize;
|
|
|
|
+ ssize_t bytes;
|
|
|
|
+
|
|
|
|
+ /* ignore if this is too small to be a EFI zboot image */
|
|
|
|
+ if (*size < sizeof(*header)) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ header = (struct linux_efi_zboot_header *)*buffer;
|
|
|
|
+
|
|
|
|
+ /* ignore if this is not a Linux EFI zboot image */
|
|
|
|
+ if (memcmp(&header->msdos_magic, EFI_PE_MSDOS_MAGIC, 2) != 0 ||
|
|
|
|
+ memcmp(&header->zimg, "zimg", 4) != 0 ||
|
|
|
|
+ memcmp(&header->linux_magic, EFI_PE_LINUX_MAGIC, 4) != 0) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (strcmp(header->compression_type, "gzip") != 0) {
|
|
|
|
+ fprintf(stderr,
|
|
|
|
+ "unable to handle EFI zboot image with \"%.*s\" compression\n",
|
|
|
|
+ (int)sizeof(header->compression_type) - 1,
|
|
|
|
+ header->compression_type);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ploff = ldl_le_p(&header->payload_offset);
|
|
|
|
+ plsize = ldl_le_p(&header->payload_size);
|
|
|
|
+
|
|
|
|
+ if (ploff < 0 || plsize < 0 || ploff + plsize > *size) {
|
|
|
|
+ fprintf(stderr, "unable to handle corrupt EFI zboot image\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ data = g_malloc(LOAD_IMAGE_MAX_GUNZIP_BYTES);
|
|
|
|
+ bytes = gunzip(data, LOAD_IMAGE_MAX_GUNZIP_BYTES, *buffer + ploff, plsize);
|
|
|
|
+ if (bytes < 0) {
|
|
|
|
+ fprintf(stderr, "failed to decompress EFI zboot image\n");
|
|
|
|
+ g_free(data);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_free(*buffer);
|
|
|
|
+ *buffer = g_realloc(data, bytes);
|
|
|
|
+ *size = bytes;
|
|
|
|
+ return bytes;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Functions for reboot-persistent memory regions.
|
|
* Functions for reboot-persistent memory regions.
|
|
* - used for vga bios and option roms.
|
|
* - used for vga bios and option roms.
|