]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[lkrn] Add support for EFI zboot compressed kernel images
authorMichael Brown <mcb30@ipxe.org>
Tue, 20 May 2025 13:14:26 +0000 (14:14 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 20 May 2025 13:29:57 +0000 (14:29 +0100)
Current RISC-V and AArch64 kernels found in the wild tend not to be in
the documented kernel format, but are instead "EFI zboot" kernels
comprising a small EFI executable that decompresses and executes the
inner payload (which is a kernel in the expected format).

The EFI zboot header includes a recognisable magic value "zimg" along
with two fields describing the offset and length of the compressed
payload.  We can therefore treat this as an archive image format,
extracting the payload as-is and then relying on our existing ability
to execute compressed images.

This is sufficient to allow iPXE to execute the Fedora 42 RISC-V
kernel binary as currently published.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/image/lkrn.c
src/include/ipxe/lkrn.h

index 37e1485d0ac2582d8fd24fa7246859e66b556b47..f8929f3b393b088a835fb34cc30e2e1a58037da8 100644 (file)
@@ -252,3 +252,105 @@ struct image_type lkrn_image_type __image_type ( PROBE_NORMAL ) = {
        .probe = lkrn_probe,
        .exec = lkrn_exec,
 };
+
+/**
+ * Parse compressed kernel image
+ *
+ * @v image            Compressed kernel image
+ * @v zctx             Compressed kernel image context
+ * @ret rc             Return status code
+ */
+static int zimg_parse ( struct image *image, struct zimg_context *zctx ) {
+       const struct zimg_header *zhdr;
+
+       /* Initialise context */
+       memset ( zctx, 0, sizeof ( *zctx ) );
+
+       /* Parse header */
+       if ( image->len < sizeof ( *zhdr ) ) {
+               DBGC ( image, "ZIMG %s too short for header\n",
+                      image->name );
+               return -ENOEXEC;
+       }
+       zhdr = image->data;
+
+       /* Check magic value */
+       if ( zhdr->magic != cpu_to_le32 ( ZIMG_MAGIC ) ) {
+               DBGC ( image, "ZIMG %s bad magic value %#08x\n",
+                      image->name, le32_to_cpu ( zhdr->magic ) );
+               return -ENOEXEC;
+       }
+
+       /* Record and check offset and length */
+       zctx->offset = le32_to_cpu ( zhdr->offset );
+       zctx->len = le32_to_cpu ( zhdr->len );
+       if ( ( zctx->offset > image->len ) ||
+            ( zctx->len > ( image->len - zctx->offset ) ) ) {
+               DBGC ( image, "ZIMG %s bad range [+%#zx,+%#zx)/%#zx\n",
+                      image->name, zctx->offset,
+                      (zctx->offset + zctx->len ), image->len );
+               return -ENOEXEC;
+       }
+
+       /* Record compression type */
+       zctx->type.raw = zhdr->type;
+
+       return 0;
+}
+
+/**
+ * Extract compresed kernel image
+ *
+ * @v image            Compressed kernel image
+ * @v extracted                Extracted image
+ * @ret rc             Return status code
+ */
+static int zimg_extract ( struct image *image, struct image *extracted ) {
+       struct zimg_context zctx;
+       const void *payload;
+       int rc;
+
+       /* Parse header */
+       if ( ( rc = zimg_parse ( image, &zctx ) ) != 0 )
+               return rc;
+       DBGC ( image, "ZIMG %s has %s-compressed payload at [+%#zx,+%#zx)\n",
+              image->name, zctx.type.string, zctx.offset,
+              ( zctx.offset + zctx.len ) );
+
+       /* Extract compressed payload */
+       payload = ( image->data + zctx.offset );
+       if ( ( rc = image_set_data ( extracted, payload, zctx.len ) ) != 0 ) {
+               DBGC ( image, "ZIMG %s could not extract: %s\n",
+                      image->name, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Probe compressed kernel image
+ *
+ * @v image            Compressed kernel image
+ * @ret rc             Return status code
+ */
+static int zimg_probe ( struct image *image ) {
+       struct zimg_context zctx;
+       int rc;
+
+       /* Parse header */
+       if ( ( rc = zimg_parse ( image, &zctx ) ) != 0 )
+               return rc;
+
+       DBGC ( image, "ZIMG %s is a %s-compressed Linux kernel\n",
+              image->name, zctx.type.string );
+       return 0;
+}
+
+/** Linux kernel compressed image type */
+struct image_type zimg_image_type __image_type ( PROBE_NORMAL ) = {
+       .name = "zimg",
+       .probe = zimg_probe,
+       .extract = zimg_extract,
+       .exec = image_extract_exec,
+};
index c3a781b44282f0c4b1adeaa0779fe3d986207a94..59aabdd4ba7f314ddc2bf32863b7005f4abb611a 100644 (file)
@@ -56,6 +56,40 @@ struct lkrn_context {
        physaddr_t fdt;
 };
 
+/** Compressed kernel image header */
+struct zimg_header {
+       /** Reserved */
+       uint8_t reserved_a[4];
+       /** Magic */
+       uint32_t magic;
+       /** Offset to payload */
+       uint32_t offset;
+       /** Length of payload */
+       uint32_t len;
+       /** Reserved */
+       uint8_t reserved_b[8];
+       /** Compression type */
+       uint32_t type;
+} __attribute__ (( packed ));
+
+/** Compressed kernel image magic value */
+#define ZIMG_MAGIC LKRN_MAGIC ( 'z', 'i', 'm', 'g' )
+
+/** Compressed kernel image context */
+struct zimg_context {
+       /** Offset to compressed data */
+       size_t offset;
+       /** Length of compressed data */
+       size_t len;
+       /** Compression type */
+       union {
+               /** Raw type */
+               uint32_t raw;
+               /** Printable string */
+               char string[5];
+       } type;
+};
+
 #include <bits/lkrn.h>
 
 /**