aboutsummaryrefslogtreecommitdiff
path: root/hw/core/loader.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/core/loader.c')
-rw-r--r--hw/core/loader.c91
1 files changed, 91 insertions, 0 deletions
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 173f8f67f6..cd53235fed 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -857,6 +857,97 @@ ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz)
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.
* - used for vga bios and option roms.