@samitouri / QOSamiQemu / commits / f75e7baeaf

hw/nitro: Enable direct kernel boot

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> Reviewed-by: Dorjoy Chowdhury <dorjoychy111@gmail.com> Link: https://lore.kernel.org/r/20260225220807.33092-11-graf@amazon.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Alexander Graf committed Feb 25, 2026 at 22:08 UTC f75e7baeafc93c715f469366ddc1de0790337548
3 files changed +120 -1
hw/core/eif.h
+3
@@ -12,6 +12,7 @@
12 #define HW_CORE_EIF_H
13
14 #define MAX_SECTIONS 32
15 +#define EIF_HDR_ARCH_ARM64 0x1
16
17 /* members are ordered according to field order in .eif file */
18 typedef struct EifHeader {
@@ -49,6 +50,8 @@ enum EifSectionTypes {
50 EIF_SECTION_MAX = 6,
51 };
52
53 +#define EIF_MAGIC { '.', 'e', 'i', 'f' }
54 +
55 bool read_eif_file(const char *eif_path, const char *machine_initrd,
56 char **kernel_path, char **initrd_path,
57 char **kernel_cmdline, uint8_t *image_sha384,
hw/nitro/machine.c
+116
@@ -32,9 +32,104 @@
32 #include "system/nitro-accel.h"
33 #include "qemu/accel.h"
34 #include "hw/arm/machines-qom.h"
35 +#include "hw/core/eif.h"
36 +#include <zlib.h> /* for crc32 */
37
38 #define EIF_LOAD_ADDR (8 * 1024 * 1024)
39
40 +static bool is_eif(char *eif, gsize len)
41 +{
42 + const char eif_magic[] = EIF_MAGIC;
43 +
44 + return len >= sizeof(eif_magic) &&
45 + !memcmp(eif, eif_magic, sizeof(eif_magic));
46 +}
47 +
48 +static void build_eif_section(EifHeader *hdr, GByteArray *buf, uint16_t type,
49 + const char *data, uint64_t size)
50 +{
51 + uint16_t section = be16_to_cpu(hdr->section_cnt);
52 + EifSectionHeader shdr = {
53 + .section_type = cpu_to_be16(type),
54 + .flags = 0,
55 + .section_size = cpu_to_be64(size),
56 + };
57 +
58 + hdr->section_offsets[section] = cpu_to_be64(buf->len);
59 + hdr->section_sizes[section] = cpu_to_be64(size);
60 +
61 + g_byte_array_append(buf, (const uint8_t *)&shdr, sizeof(shdr));
62 + if (size) {
63 + g_byte_array_append(buf, (const uint8_t *)data, size);
64 + }
65 +
66 + hdr->section_cnt = cpu_to_be16(section + 1);
67 +}
68 +
69 +/*
70 + * Nitro Enclaves only support loading EIF files. When the user provides
71 + * a Linux kernel, initrd and cmdline, convert them into EIF format.
72 + */
73 +static char *build_eif(const char *kernel_data, gsize kernel_size,
74 + const char *initrd_path, const char *cmdline,
75 + gsize *out_size, Error **errp)
76 +{
77 + g_autofree char *initrd_data = NULL;
78 + static const char metadata[] = "{}";
79 + size_t metadata_len = sizeof(metadata) - 1;
80 + gsize initrd_size = 0;
81 + GByteArray *buf;
82 + EifHeader hdr;
83 + uint32_t crc = 0;
84 + size_t cmdline_len;
85 +
86 + if (initrd_path) {
87 + if (!g_file_get_contents(initrd_path, &initrd_data,
88 + &initrd_size, NULL)) {
89 + error_setg(errp, "Failed to read initrd '%s'", initrd_path);
90 + return NULL;
91 + }
92 + }
93 +
94 + buf = g_byte_array_new();
95 +
96 + cmdline_len = cmdline ? strlen(cmdline) : 0;
97 +
98 + hdr = (EifHeader) {
99 + .magic = EIF_MAGIC,
100 + .version = cpu_to_be16(4),
101 + .flags = cpu_to_be16(target_aarch64() ? EIF_HDR_ARCH_ARM64 : 0),
102 + };
103 +
104 + g_byte_array_append(buf, (const uint8_t *)&hdr, sizeof(hdr));
105 +
106 + /* Kernel */
107 + build_eif_section(&hdr, buf, EIF_SECTION_KERNEL, kernel_data, kernel_size);
108 +
109 + /* Command line */
110 + build_eif_section(&hdr, buf, EIF_SECTION_CMDLINE, cmdline, cmdline_len);
111 +
112 + /* Initramfs */
113 + build_eif_section(&hdr, buf, EIF_SECTION_RAMDISK, initrd_data, initrd_size);
114 +
115 + /* Metadata */
116 + build_eif_section(&hdr, buf, EIF_SECTION_METADATA, metadata, metadata_len);
117 +
118 + /*
119 + * Patch the header into the buffer first (with real section offsets
120 + * and sizes), then compute CRC over everything except the CRC field.
121 + */
122 + memcpy(buf->data, &hdr, sizeof(hdr));
123 + crc = crc32(crc, buf->data, offsetof(EifHeader, eif_crc32));
124 + crc = crc32(crc, &buf->data[sizeof(hdr)], buf->len - sizeof(hdr));
125 +
126 + /* Finally write the CRC into the in-buffer header */
127 + ((EifHeader *)buf->data)->eif_crc32 = cpu_to_be32(crc);
128 +
129 + *out_size = buf->len;
130 + return (char *)g_byte_array_free(buf, false);
131 +}
132 +
133 static void nitro_machine_init(MachineState *machine)
134 {
135 const char *eif_path = machine->kernel_filename;
@@ -74,6 +169,27 @@ static void nitro_machine_init(MachineState *machine)
169 error_report("nitro: failed to read EIF '%s'", eif_path);
170 exit(1);
171 }
172 +
173 + if (!is_eif(eif_data, eif_size)) {
174 + char *kernel_data = eif_data;
175 + gsize kernel_size = eif_size;
176 + Error *err = NULL;
177 +
178 + /*
179 + * The user gave us a non-EIF kernel, likely a Linux kernel image.
180 + * Assemble an EIF file from it, the -initrd and the -append arguments,
181 + * so that users can perform a natural direct kernel boot.
182 + */
183 + eif_data = build_eif(kernel_data, kernel_size, machine->initrd_filename,
184 + machine->kernel_cmdline, &eif_size, &err);
185 + if (!eif_data) {
186 + error_report_err(err);
187 + exit(1);
188 + }
189 +
190 + g_free(kernel_data);
191 + }
192 +
193 address_space_write(&address_space_memory, EIF_LOAD_ADDR,
194 MEMTXATTRS_UNSPECIFIED, eif_data, eif_size);
195
hw/nitro/meson.build
+1 -1
@@ -1,4 +1,4 @@
1 system_ss.add(when: 'CONFIG_NITRO_VSOCK_BUS', if_true: files('nitro-vsock-bus.c'))
2 system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: files('serial-vsock.c'))
3 system_ss.add(when: 'CONFIG_NITRO_HEARTBEAT', if_true: files('heartbeat.c'))
4 -system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: files('machine.c'))
4 +system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: [files('machine.c'), zlib])