master
c 277 lines 8.3 KB
Raw
1 /*
2 * Nitro Enclaves (accel) machine
3 *
4 * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 *
6 * Authors:
7 * Alexander Graf <graf@amazon.com>
8 *
9 * Nitro Enclaves machine model for -accel nitro. This machine behaves
10 * like the nitro-enclave machine, but uses the real Nitro Enclaves
11 * backend to launch the virtual machine. It requires use of the -accel
12 * nitro.
13 *
14 * SPDX-License-Identifier: GPL-2.0-or-later
15 */
16
17 #include "qemu/osdep.h"
18 #include "qemu/error-report.h"
19 #include "qapi/error.h"
20 #include "qom/object_interfaces.h"
21 #include "chardev/char.h"
22 #include "hw/core/boards.h"
23 #include "hw/core/cpu.h"
24 #include "hw/core/qdev-properties-system.h"
25 #include "hw/nitro/heartbeat.h"
26 #include "hw/nitro/machine.h"
27 #include "hw/nitro/nitro-vsock-bus.h"
28 #include "hw/nitro/serial-vsock.h"
29 #include "system/address-spaces.h"
30 #include "system/hostmem.h"
31 #include "system/system.h"
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;
136 const char *cpu_type = machine->cpu_type;
137 g_autofree char *eif_data = NULL;
138 gsize eif_size;
139
140 if (!nitro_enabled()) {
141 error_report("The 'nitro' machine requires -accel nitro");
142 exit(1);
143 }
144
145 if (!cpu_type) {
146 ObjectClass *oc = cpu_class_by_name(target_cpu_type(), "host");
147
148 if (!oc) {
149 error_report("nitro: no 'host' CPU available");
150 exit(1);
151 }
152 cpu_type = object_class_get_name(oc);
153 }
154
155 if (!eif_path) {
156 error_report("nitro: -kernel <eif-file> is required");
157 exit(1);
158 }
159
160 /* Expose memory as normal QEMU RAM. Needs to be huge page backed. */
161 memory_region_add_subregion(get_system_memory(), 0, machine->ram);
162
163 /*
164 * Load EIF (-kernel) as raw blob at the EIF_LOAD_ADDR into guest RAM.
165 * The Nitro Hypervisor will extract its contents and bootstrap the
166 * Enclave from it.
167 */
168 if (!g_file_get_contents(eif_path, &eif_data, &eif_size, NULL)) {
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
196 if (defaults_enabled()) {
197 NitroVsockBridge *bridge = nitro_vsock_bridge_create();
198
199 /* Nitro Enclaves require a heartbeat device. Provide one. */
200 qdev_realize(qdev_new(TYPE_NITRO_HEARTBEAT),
201 BUS(&bridge->bus), &error_fatal);
202
203 /*
204 * In debug mode, Nitro Enclaves expose the guest's serial output via
205 * vsock. When the accel is in debug mode, wire the vsock serial to
206 * the machine's serial port so that -nographic automatically works
207 */
208 if (object_property_get_bool(OBJECT(current_accel()), "debug-mode", NULL)) {
209 Chardev *chr = serial_hd(0);
210
211 if (chr) {
212 DeviceState *dev = qdev_new(TYPE_NITRO_SERIAL_VSOCK);
213
214 qdev_prop_set_chr(dev, "chardev", chr);
215 qdev_realize(dev, BUS(&bridge->bus), &error_fatal);
216 }
217 }
218 }
219 }
220
221 static bool nitro_create_memfd_backend(MachineState *ms, const char *path,
222 Error **errp)
223 {
224 MachineClass *mc = MACHINE_GET_CLASS(ms);
225 Object *root = object_get_objects_root();
226 Object *obj;
227 bool r = false;
228
229 obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
230
231 /* Nitro Enclaves require huge page backing */
232 if (!object_property_set_int(obj, "size", ms->ram_size, errp) ||
233 !object_property_set_bool(obj, "hugetlb", true, errp)) {
234 goto out;
235 }
236
237 object_property_add_child(root, mc->default_ram_id, obj);
238
239 if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
240 goto out;
241 }
242 r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
243
244 out:
245 object_unref(obj);
246 return r;
247 }
248
249 static void nitro_machine_class_init(ObjectClass *oc, const void *data)
250 {
251 MachineClass *mc = MACHINE_CLASS(oc);
252
253 mc->desc = "Nitro Enclave";
254 mc->init = nitro_machine_init;
255 mc->create_default_memdev = nitro_create_memfd_backend;
256 mc->default_ram_id = "ram";
257 mc->max_cpus = 4096;
258 }
259
260 static const TypeInfo nitro_machine_info = {
261 .name = TYPE_NITRO_MACHINE,
262 .parent = TYPE_MACHINE,
263 .instance_size = sizeof(NitroMachineState),
264 .class_init = nitro_machine_class_init,
265 .interfaces = (const InterfaceInfo[]) {
266 /* x86_64 and aarch64 only */
267 { TYPE_TARGET_AARCH64_MACHINE },
268 { }
269 },
270 };
271
272 static void nitro_machine_register(void)
273 {
274 type_register_static(&nitro_machine_info);
275 }
276
277 type_init(nitro_machine_register);