On Thu, Feb 26, 2026 at 4:09 AM Alexander Graf <[email protected]> wrote: > > Nitro Enclaves can only boot EIF files which are a combination of > kernel, initramfs and cmdline in a single file. When the kernel image is > not an EIF, treat it like a kernel image and assemble an EIF image on > the fly. This way, users can call QEMU with a direct > kernel/initrd/cmdline combination and everything "just works". > > Signed-off-by: Alexander Graf <[email protected]> > --- > hw/core/eif.h | 3 ++ > hw/nitro/machine.c | 116 +++++++++++++++++++++++++++++++++++++++++++ > hw/nitro/meson.build | 2 +- > 3 files changed, 120 insertions(+), 1 deletion(-) >
Reviewed-by: Dorjoy Chowdhury <[email protected]> > diff --git a/hw/core/eif.h b/hw/core/eif.h > index a3412377a9..0c432dbc2d 100644 > --- a/hw/core/eif.h > +++ b/hw/core/eif.h > @@ -12,6 +12,7 @@ > #define HW_CORE_EIF_H > > #define MAX_SECTIONS 32 > +#define EIF_HDR_ARCH_ARM64 0x1 > > /* members are ordered according to field order in .eif file */ > typedef struct EifHeader { > @@ -49,6 +50,8 @@ enum EifSectionTypes { > EIF_SECTION_MAX = 6, > }; > > +#define EIF_MAGIC { '.', 'e', 'i', 'f' } > + > bool read_eif_file(const char *eif_path, const char *machine_initrd, > char **kernel_path, char **initrd_path, > char **kernel_cmdline, uint8_t *image_sha384, > diff --git a/hw/nitro/machine.c b/hw/nitro/machine.c > index e28c8e9bf5..8849959359 100644 > --- a/hw/nitro/machine.c > +++ b/hw/nitro/machine.c > @@ -32,9 +32,104 @@ > #include "system/nitro-accel.h" > #include "qemu/accel.h" > #include "hw/arm/machines-qom.h" > +#include "hw/core/eif.h" > +#include <zlib.h> /* for crc32 */ > > #define EIF_LOAD_ADDR (8 * 1024 * 1024) > > +static bool is_eif(char *eif, gsize len) > +{ > + const char eif_magic[] = EIF_MAGIC; > + > + return len >= sizeof(eif_magic) && > + !memcmp(eif, eif_magic, sizeof(eif_magic)); > +} > + > +static void build_eif_section(EifHeader *hdr, GByteArray *buf, uint16_t type, > + const char *data, uint64_t size) > +{ > + uint16_t section = be16_to_cpu(hdr->section_cnt); > + EifSectionHeader shdr = { > + .section_type = cpu_to_be16(type), > + .flags = 0, > + .section_size = cpu_to_be64(size), > + }; > + > + hdr->section_offsets[section] = cpu_to_be64(buf->len); > + hdr->section_sizes[section] = cpu_to_be64(size); > + > + g_byte_array_append(buf, (const uint8_t *)&shdr, sizeof(shdr)); > + if (size) { > + g_byte_array_append(buf, (const uint8_t *)data, size); > + } > + > + hdr->section_cnt = cpu_to_be16(section + 1); > +} > + > +/* > + * Nitro Enclaves only support loading EIF files. When the user provides > + * a Linux kernel, initrd and cmdline, convert them into EIF format. > + */ > +static char *build_eif(const char *kernel_data, gsize kernel_size, > + const char *initrd_path, const char *cmdline, > + gsize *out_size, Error **errp) > +{ > + g_autofree char *initrd_data = NULL; > + static const char metadata[] = "{}"; > + size_t metadata_len = sizeof(metadata) - 1; > + gsize initrd_size = 0; > + GByteArray *buf; > + EifHeader hdr; > + uint32_t crc = 0; > + size_t cmdline_len; > + > + if (initrd_path) { > + if (!g_file_get_contents(initrd_path, &initrd_data, > + &initrd_size, NULL)) { > + error_setg(errp, "Failed to read initrd '%s'", initrd_path); > + return NULL; > + } > + } > + > + buf = g_byte_array_new(); > + > + cmdline_len = cmdline ? strlen(cmdline) : 0; > + > + hdr = (EifHeader) { > + .magic = EIF_MAGIC, > + .version = cpu_to_be16(4), > + .flags = cpu_to_be16(target_aarch64() ? EIF_HDR_ARCH_ARM64 : 0), > + }; > + > + g_byte_array_append(buf, (const uint8_t *)&hdr, sizeof(hdr)); > + > + /* Kernel */ > + build_eif_section(&hdr, buf, EIF_SECTION_KERNEL, kernel_data, > kernel_size); > + > + /* Command line */ > + build_eif_section(&hdr, buf, EIF_SECTION_CMDLINE, cmdline, cmdline_len); > + > + /* Initramfs */ > + build_eif_section(&hdr, buf, EIF_SECTION_RAMDISK, initrd_data, > initrd_size); > + > + /* Metadata */ > + build_eif_section(&hdr, buf, EIF_SECTION_METADATA, metadata, > metadata_len); > + > + /* > + * Patch the header into the buffer first (with real section offsets > + * and sizes), then compute CRC over everything except the CRC field. > + */ > + memcpy(buf->data, &hdr, sizeof(hdr)); > + crc = crc32(crc, buf->data, offsetof(EifHeader, eif_crc32)); > + crc = crc32(crc, &buf->data[sizeof(hdr)], buf->len - sizeof(hdr)); > + > + /* Finally write the CRC into the in-buffer header */ > + ((EifHeader *)buf->data)->eif_crc32 = cpu_to_be32(crc); > + > + *out_size = buf->len; > + return (char *)g_byte_array_free(buf, false); > +} > + > static void nitro_machine_init(MachineState *machine) > { > const char *eif_path = machine->kernel_filename; > @@ -74,6 +169,27 @@ static void nitro_machine_init(MachineState *machine) > error_report("nitro: failed to read EIF '%s'", eif_path); > exit(1); > } > + > + if (!is_eif(eif_data, eif_size)) { > + char *kernel_data = eif_data; > + gsize kernel_size = eif_size; > + Error *err = NULL; > + > + /* > + * The user gave us a non-EIF kernel, likely a Linux kernel image. > + * Assemble an EIF file from it, the -initrd and the -append > arguments, > + * so that users can perform a natural direct kernel boot. > + */ > + eif_data = build_eif(kernel_data, kernel_size, > machine->initrd_filename, > + machine->kernel_cmdline, &eif_size, &err); > + if (!eif_data) { > + error_report_err(err); > + exit(1); > + } > + > + g_free(kernel_data); > + } > + > address_space_write(&address_space_memory, EIF_LOAD_ADDR, > MEMTXATTRS_UNSPECIFIED, eif_data, eif_size); > > diff --git a/hw/nitro/meson.build b/hw/nitro/meson.build > index e3f1895890..b9bd0d4300 100644 > --- a/hw/nitro/meson.build > +++ b/hw/nitro/meson.build > @@ -1,4 +1,4 @@ > system_ss.add(when: 'CONFIG_NITRO_VSOCK_BUS', if_true: > files('nitro-vsock-bus.c')) > system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: > files('serial-vsock.c')) > system_ss.add(when: 'CONFIG_NITRO_HEARTBEAT', if_true: files('heartbeat.c')) > -system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: files('machine.c')) > +system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: [files('machine.c'), > zlib]) > -- > 2.47.1 > > > > > Amazon Web Services Development Center Germany GmbH > Tamara-Danz-Str. 13 > 10243 Berlin > Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger > Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B > Sitz: Berlin > Ust-ID: DE 365 538 597 >
