[lkrn] Add support for EFI zboot compressed kernel images

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>
This commit is contained in:
Michael Brown
2025-05-20 14:14:26 +01:00
parent ecac4a34c7
commit e2f4dba2b7
2 changed files with 136 additions and 0 deletions

View 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,
};

View 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>
/**