[PATCH v2 10/11] hw/nitro: Enable direct kernel boot

Alexander Graf posted 11 patches 1 month, 2 weeks ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Alexander Graf <graf@amazon.com>, Pierrick Bouvier <pierrick.bouvier@linaro.org>, Dorjoy Chowdhury <dorjoychy111@gmail.com>, "Michael S. Tsirkin" <mst@redhat.com>, Cornelia Huck <cohuck@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, "Alex Bennée" <alex.bennee@linaro.org>, Thomas Huth <thuth@redhat.com>, Peter Maydell <peter.maydell@linaro.org>
There is a newer version of this series
[PATCH v2 10/11] hw/nitro: Enable direct kernel boot
Posted by Alexander Graf 1 month, 2 weeks ago
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 <graf@amazon.com>
---
 hw/core/eif.h        |   3 ++
 hw/nitro/machine.c   | 116 +++++++++++++++++++++++++++++++++++++++++++
 hw/nitro/meson.build |   2 +-
 3 files changed, 120 insertions(+), 1 deletion(-)

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
Re: [PATCH v2 10/11] hw/nitro: Enable direct kernel boot
Posted by Dorjoy Chowdhury 1 month, 2 weeks ago
On Wed, Feb 25, 2026 at 8:47 PM Alexander Graf <graf@amazon.com> 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 <graf@amazon.com>
> ---
>  hw/core/eif.h        |   3 ++
>  hw/nitro/machine.c   | 116 +++++++++++++++++++++++++++++++++++++++++++
>  hw/nitro/meson.build |   2 +-
>  3 files changed, 120 insertions(+), 1 deletion(-)
>
> 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);
> +}
> +

Maybe these eif building functions make sense in the hw/core/eif.c
file. There we already have the parsing code, so maybe these functions
complement the code there. That way we wouldn't need to move the EIF
structures etc into the header file in the previous commit.

Regards,
Dorjoy
Re: [PATCH v2 10/11] hw/nitro: Enable direct kernel boot
Posted by Alexander Graf 1 month, 2 weeks ago
On 25.02.26 17:28, Dorjoy Chowdhury wrote:
> On Wed, Feb 25, 2026 at 8:47 PM Alexander Graf <graf@amazon.com> 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 <graf@amazon.com>
>> ---
>>   hw/core/eif.h        |   3 ++
>>   hw/nitro/machine.c   | 116 +++++++++++++++++++++++++++++++++++++++++++
>>   hw/nitro/meson.build |   2 +-
>>   3 files changed, 120 insertions(+), 1 deletion(-)
>>
>> 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);
>> +}
>> +
> Maybe these eif building functions make sense in the hw/core/eif.c
> file. There we already have the parsing code, so maybe these functions
> complement the code there. That way we wouldn't need to move the EIF
> structures etc into the header file in the previous commit.


These functions started off in eif.c even. But then I realized that 
eif.c is fairly tied to the nitro enclave emulated machine which is x86 
only atm. And I want to generate EIFs also on aarch64.

IMHO the definitions are better fit in the header anyway. And I'd rather 
like to leave the EIF generation self-contained here for now until we 
decide to add support for live EIF assembly in the emulated machine as 
well. At that point we can think of merging them :)


Alex




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