1 /*
2 * AWS nitro-enclave machine
3 *
4 * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or
7 * (at your option) any later version. See the COPYING file in the
8 * top-level directory.
9 */
10
11 #include "qemu/osdep.h"
12 #include "qemu/error-report.h"
13 #include "qapi/error.h"
14 #include "qom/object_interfaces.h"
15
16 #include "chardev/char.h"
17 #include "hw/sysbus.h"
18 #include "hw/core/eif.h"
19 #include "hw/i386/x86.h"
20 #include "hw/i386/microvm.h"
21 #include "hw/i386/nitro_enclave.h"
22 #include "hw/virtio/virtio-mmio.h"
23 #include "hw/virtio/virtio-nsm.h"
24 #include "hw/virtio/vhost-user-vsock.h"
25 #include "sysemu/hostmem.h"
26
find_free_virtio_mmio_bus(void)27 static BusState *find_free_virtio_mmio_bus(void)
28 {
29 BusChild *kid;
30 BusState *bus = sysbus_get_default();
31
32 QTAILQ_FOREACH(kid, &bus->children, sibling) {
33 DeviceState *dev = kid->child;
34 if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO)) {
35 VirtIOMMIOProxy *mmio = VIRTIO_MMIO(OBJECT(dev));
36 VirtioBusState *mmio_virtio_bus = &mmio->bus;
37 BusState *mmio_bus = &mmio_virtio_bus->parent_obj;
38 if (QTAILQ_EMPTY(&mmio_bus->children)) {
39 return mmio_bus;
40 }
41 }
42 }
43
44 return NULL;
45 }
46
vhost_user_vsock_init(NitroEnclaveMachineState * nems)47 static void vhost_user_vsock_init(NitroEnclaveMachineState *nems)
48 {
49 DeviceState *dev = qdev_new(TYPE_VHOST_USER_VSOCK);
50 VHostUserVSock *vsock = VHOST_USER_VSOCK(dev);
51 BusState *bus;
52
53 if (!nems->vsock) {
54 error_report("A valid chardev id for vhost-user-vsock device must be "
55 "provided using the 'vsock' machine option");
56 exit(1);
57 }
58
59 bus = find_free_virtio_mmio_bus();
60 if (!bus) {
61 error_report("Failed to find bus for vhost-user-vsock device");
62 exit(1);
63 }
64
65 Chardev *chardev = qemu_chr_find(nems->vsock);
66 if (!chardev) {
67 error_report("Failed to find chardev with id %s", nems->vsock);
68 exit(1);
69 }
70
71 vsock->conf.chardev.chr = chardev;
72
73 qdev_realize_and_unref(dev, bus, &error_fatal);
74 }
75
virtio_nsm_init(NitroEnclaveMachineState * nems)76 static void virtio_nsm_init(NitroEnclaveMachineState *nems)
77 {
78 DeviceState *dev = qdev_new(TYPE_VIRTIO_NSM);
79 VirtIONSM *vnsm = VIRTIO_NSM(dev);
80 BusState *bus = find_free_virtio_mmio_bus();
81
82 if (!bus) {
83 error_report("Failed to find bus for virtio-nsm device.");
84 exit(1);
85 }
86
87 qdev_prop_set_string(dev, "module-id", nems->id);
88
89 qdev_realize_and_unref(dev, bus, &error_fatal);
90 nems->vnsm = vnsm;
91 }
92
nitro_enclave_devices_init(NitroEnclaveMachineState * nems)93 static void nitro_enclave_devices_init(NitroEnclaveMachineState *nems)
94 {
95 vhost_user_vsock_init(nems);
96 virtio_nsm_init(nems);
97 }
98
nitro_enclave_machine_state_init(MachineState * machine)99 static void nitro_enclave_machine_state_init(MachineState *machine)
100 {
101 NitroEnclaveMachineClass *ne_class =
102 NITRO_ENCLAVE_MACHINE_GET_CLASS(machine);
103 NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine);
104
105 ne_class->parent_init(machine);
106 nitro_enclave_devices_init(ne_state);
107 }
108
nitro_enclave_machine_reset(MachineState * machine,ResetType type)109 static void nitro_enclave_machine_reset(MachineState *machine, ResetType type)
110 {
111 NitroEnclaveMachineClass *ne_class =
112 NITRO_ENCLAVE_MACHINE_GET_CLASS(machine);
113 NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine);
114
115 ne_class->parent_reset(machine, type);
116
117 memset(ne_state->vnsm->pcrs, 0, sizeof(ne_state->vnsm->pcrs));
118
119 /* PCR0 */
120 ne_state->vnsm->extend_pcr(ne_state->vnsm, 0, ne_state->image_sha384,
121 QCRYPTO_HASH_DIGEST_LEN_SHA384);
122 /* PCR1 */
123 ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_sha384,
124 QCRYPTO_HASH_DIGEST_LEN_SHA384);
125 /* PCR2 */
126 ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_sha384,
127 QCRYPTO_HASH_DIGEST_LEN_SHA384);
128 /* PCR3 */
129 if (ne_state->parent_role) {
130 ne_state->vnsm->extend_pcr(ne_state->vnsm, 3,
131 (uint8_t *) ne_state->parent_role,
132 strlen(ne_state->parent_role));
133 }
134 /* PCR4 */
135 if (ne_state->parent_id) {
136 ne_state->vnsm->extend_pcr(ne_state->vnsm, 4,
137 (uint8_t *) ne_state->parent_id,
138 strlen(ne_state->parent_id));
139 }
140 /* PCR8 */
141 if (ne_state->signature_found) {
142 ne_state->vnsm->extend_pcr(ne_state->vnsm, 8,
143 ne_state->fingerprint_sha384,
144 QCRYPTO_HASH_DIGEST_LEN_SHA384);
145 }
146
147 /* First 16 PCRs are locked from boot and reserved for nitro enclave */
148 for (int i = 0; i < 16; ++i) {
149 ne_state->vnsm->lock_pcr(ne_state->vnsm, i);
150 }
151 }
152
nitro_enclave_machine_initfn(Object * obj)153 static void nitro_enclave_machine_initfn(Object *obj)
154 {
155 MicrovmMachineState *mms = MICROVM_MACHINE(obj);
156 X86MachineState *x86ms = X86_MACHINE(obj);
157 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
158
159 nems->id = g_strdup("i-234-enc5678");
160
161 /* AWS nitro enclaves have PCIE and ACPI disabled */
162 mms->pcie = ON_OFF_AUTO_OFF;
163 x86ms->acpi = ON_OFF_AUTO_OFF;
164 }
165
x86_load_eif(X86MachineState * x86ms,FWCfgState * fw_cfg,int acpi_data_size,bool pvh_enabled)166 static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg,
167 int acpi_data_size, bool pvh_enabled)
168 {
169 Error *err = NULL;
170 char *eif_kernel, *eif_initrd, *eif_cmdline;
171 MachineState *machine = MACHINE(x86ms);
172 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(x86ms);
173
174 if (!read_eif_file(machine->kernel_filename, machine->initrd_filename,
175 &eif_kernel, &eif_initrd, &eif_cmdline,
176 nems->image_sha384, nems->bootstrap_sha384,
177 nems->app_sha384, nems->fingerprint_sha384,
178 &(nems->signature_found), &err)) {
179 error_report_err(err);
180 exit(1);
181 }
182
183 g_free(machine->kernel_filename);
184 machine->kernel_filename = eif_kernel;
185 g_free(machine->initrd_filename);
186 machine->initrd_filename = eif_initrd;
187
188 /*
189 * If kernel cmdline argument was provided, let's concatenate it to the
190 * extracted EIF kernel cmdline.
191 */
192 if (machine->kernel_cmdline != NULL) {
193 char *cmd = g_strdup_printf("%s %s", eif_cmdline,
194 machine->kernel_cmdline);
195 g_free(eif_cmdline);
196 g_free(machine->kernel_cmdline);
197 machine->kernel_cmdline = cmd;
198 } else {
199 machine->kernel_cmdline = eif_cmdline;
200 }
201
202 x86_load_linux(x86ms, fw_cfg, 0, true);
203
204 unlink(machine->kernel_filename);
205 unlink(machine->initrd_filename);
206 return;
207 }
208
create_memfd_backend(MachineState * ms,const char * path,Error ** errp)209 static bool create_memfd_backend(MachineState *ms, const char *path,
210 Error **errp)
211 {
212 Object *obj;
213 MachineClass *mc = MACHINE_GET_CLASS(ms);
214 bool r = false;
215
216 obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
217 if (!object_property_set_int(obj, "size", ms->ram_size, errp)) {
218 goto out;
219 }
220 object_property_add_child(object_get_objects_root(), mc->default_ram_id,
221 obj);
222
223 if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
224 goto out;
225 }
226 r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
227
228 out:
229 object_unref(obj);
230 return r;
231 }
232
nitro_enclave_get_vsock_chardev_id(Object * obj,Error ** errp)233 static char *nitro_enclave_get_vsock_chardev_id(Object *obj, Error **errp)
234 {
235 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
236
237 return g_strdup(nems->vsock);
238 }
239
nitro_enclave_set_vsock_chardev_id(Object * obj,const char * value,Error ** errp)240 static void nitro_enclave_set_vsock_chardev_id(Object *obj, const char *value,
241 Error **errp)
242 {
243 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
244
245 g_free(nems->vsock);
246 nems->vsock = g_strdup(value);
247 }
248
nitro_enclave_get_id(Object * obj,Error ** errp)249 static char *nitro_enclave_get_id(Object *obj, Error **errp)
250 {
251 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
252
253 return g_strdup(nems->id);
254 }
255
nitro_enclave_set_id(Object * obj,const char * value,Error ** errp)256 static void nitro_enclave_set_id(Object *obj, const char *value,
257 Error **errp)
258 {
259 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
260
261 g_free(nems->id);
262 nems->id = g_strdup(value);
263 }
264
nitro_enclave_get_parent_role(Object * obj,Error ** errp)265 static char *nitro_enclave_get_parent_role(Object *obj, Error **errp)
266 {
267 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
268
269 return g_strdup(nems->parent_role);
270 }
271
nitro_enclave_set_parent_role(Object * obj,const char * value,Error ** errp)272 static void nitro_enclave_set_parent_role(Object *obj, const char *value,
273 Error **errp)
274 {
275 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
276
277 g_free(nems->parent_role);
278 nems->parent_role = g_strdup(value);
279 }
280
nitro_enclave_get_parent_id(Object * obj,Error ** errp)281 static char *nitro_enclave_get_parent_id(Object *obj, Error **errp)
282 {
283 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
284
285 return g_strdup(nems->parent_id);
286 }
287
nitro_enclave_set_parent_id(Object * obj,const char * value,Error ** errp)288 static void nitro_enclave_set_parent_id(Object *obj, const char *value,
289 Error **errp)
290 {
291 NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
292
293 g_free(nems->parent_id);
294 nems->parent_id = g_strdup(value);
295 }
296
nitro_enclave_class_init(ObjectClass * oc,void * data)297 static void nitro_enclave_class_init(ObjectClass *oc, void *data)
298 {
299 MachineClass *mc = MACHINE_CLASS(oc);
300 MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc);
301 NitroEnclaveMachineClass *nemc = NITRO_ENCLAVE_MACHINE_CLASS(oc);
302
303 mmc->x86_load_linux = x86_load_eif;
304
305 mc->family = "nitro_enclave_i386";
306 mc->desc = "AWS Nitro Enclave";
307
308 nemc->parent_init = mc->init;
309 mc->init = nitro_enclave_machine_state_init;
310
311 nemc->parent_reset = mc->reset;
312 mc->reset = nitro_enclave_machine_reset;
313
314 mc->create_default_memdev = create_memfd_backend;
315
316 object_class_property_add_str(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
317 nitro_enclave_get_vsock_chardev_id,
318 nitro_enclave_set_vsock_chardev_id);
319 object_class_property_set_description(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
320 "Set chardev id for vhost-user-vsock "
321 "device");
322
323 object_class_property_add_str(oc, NITRO_ENCLAVE_ID, nitro_enclave_get_id,
324 nitro_enclave_set_id);
325 object_class_property_set_description(oc, NITRO_ENCLAVE_ID,
326 "Set enclave identifier");
327
328 object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ROLE,
329 nitro_enclave_get_parent_role,
330 nitro_enclave_set_parent_role);
331 object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ROLE,
332 "Set parent instance IAM role ARN");
333
334 object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ID,
335 nitro_enclave_get_parent_id,
336 nitro_enclave_set_parent_id);
337 object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ID,
338 "Set parent instance identifier");
339 }
340
341 static const TypeInfo nitro_enclave_machine_info = {
342 .name = TYPE_NITRO_ENCLAVE_MACHINE,
343 .parent = TYPE_MICROVM_MACHINE,
344 .instance_size = sizeof(NitroEnclaveMachineState),
345 .instance_init = nitro_enclave_machine_initfn,
346 .class_size = sizeof(NitroEnclaveMachineClass),
347 .class_init = nitro_enclave_class_init,
348 };
349
nitro_enclave_machine_init(void)350 static void nitro_enclave_machine_init(void)
351 {
352 type_register_static(&nitro_enclave_machine_info);
353 }
354 type_init(nitro_enclave_machine_init);
355