xref: /qemu/hw/i386/nitro_enclave.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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 "system/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_hash,
121                                QCRYPTO_HASH_DIGEST_LEN_SHA384);
122     /* PCR1 */
123     ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_hash,
124                                QCRYPTO_HASH_DIGEST_LEN_SHA384);
125     /* PCR2 */
126     ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_hash,
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_hash,
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_hash, nems->bootstrap_hash,
177                        nems->app_hash, nems->fingerprint_hash,
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 }
207 
create_memfd_backend(MachineState * ms,const char * path,Error ** errp)208 static bool create_memfd_backend(MachineState *ms, const char *path,
209                                  Error **errp)
210 {
211     Object *obj;
212     MachineClass *mc = MACHINE_GET_CLASS(ms);
213     bool r = false;
214 
215     obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
216     if (!object_property_set_int(obj, "size", ms->ram_size, errp)) {
217         goto out;
218     }
219     object_property_add_child(object_get_objects_root(), mc->default_ram_id,
220                               obj);
221 
222     if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
223         goto out;
224     }
225     r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
226 
227 out:
228     object_unref(obj);
229     return r;
230 }
231 
nitro_enclave_get_vsock_chardev_id(Object * obj,Error ** errp)232 static char *nitro_enclave_get_vsock_chardev_id(Object *obj, Error **errp)
233 {
234     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
235 
236     return g_strdup(nems->vsock);
237 }
238 
nitro_enclave_set_vsock_chardev_id(Object * obj,const char * value,Error ** errp)239 static void nitro_enclave_set_vsock_chardev_id(Object *obj, const char *value,
240                                                Error **errp)
241 {
242     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
243 
244     g_free(nems->vsock);
245     nems->vsock = g_strdup(value);
246 }
247 
nitro_enclave_get_id(Object * obj,Error ** errp)248 static char *nitro_enclave_get_id(Object *obj, Error **errp)
249 {
250     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
251 
252     return g_strdup(nems->id);
253 }
254 
nitro_enclave_set_id(Object * obj,const char * value,Error ** errp)255 static void nitro_enclave_set_id(Object *obj, const char *value,
256                                             Error **errp)
257 {
258     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
259 
260     g_free(nems->id);
261     nems->id = g_strdup(value);
262 }
263 
nitro_enclave_get_parent_role(Object * obj,Error ** errp)264 static char *nitro_enclave_get_parent_role(Object *obj, Error **errp)
265 {
266     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
267 
268     return g_strdup(nems->parent_role);
269 }
270 
nitro_enclave_set_parent_role(Object * obj,const char * value,Error ** errp)271 static void nitro_enclave_set_parent_role(Object *obj, const char *value,
272                                           Error **errp)
273 {
274     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
275 
276     g_free(nems->parent_role);
277     nems->parent_role = g_strdup(value);
278 }
279 
nitro_enclave_get_parent_id(Object * obj,Error ** errp)280 static char *nitro_enclave_get_parent_id(Object *obj, Error **errp)
281 {
282     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
283 
284     return g_strdup(nems->parent_id);
285 }
286 
nitro_enclave_set_parent_id(Object * obj,const char * value,Error ** errp)287 static void nitro_enclave_set_parent_id(Object *obj, const char *value,
288                                         Error **errp)
289 {
290     NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
291 
292     g_free(nems->parent_id);
293     nems->parent_id = g_strdup(value);
294 }
295 
nitro_enclave_class_init(ObjectClass * oc,const void * data)296 static void nitro_enclave_class_init(ObjectClass *oc, const void *data)
297 {
298     MachineClass *mc = MACHINE_CLASS(oc);
299     MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc);
300     NitroEnclaveMachineClass *nemc = NITRO_ENCLAVE_MACHINE_CLASS(oc);
301 
302     mmc->x86_load_linux = x86_load_eif;
303 
304     mc->family = "nitro_enclave_i386";
305     mc->desc = "AWS Nitro Enclave";
306 
307     nemc->parent_init = mc->init;
308     mc->init = nitro_enclave_machine_state_init;
309 
310     nemc->parent_reset = mc->reset;
311     mc->reset = nitro_enclave_machine_reset;
312 
313     mc->create_default_memdev = create_memfd_backend;
314 
315     object_class_property_add_str(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
316                                   nitro_enclave_get_vsock_chardev_id,
317                                   nitro_enclave_set_vsock_chardev_id);
318     object_class_property_set_description(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
319                                           "Set chardev id for vhost-user-vsock "
320                                           "device");
321 
322     object_class_property_add_str(oc, NITRO_ENCLAVE_ID, nitro_enclave_get_id,
323                                   nitro_enclave_set_id);
324     object_class_property_set_description(oc, NITRO_ENCLAVE_ID,
325                                           "Set enclave identifier");
326 
327     object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ROLE,
328                                   nitro_enclave_get_parent_role,
329                                   nitro_enclave_set_parent_role);
330     object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ROLE,
331                                           "Set parent instance IAM role ARN");
332 
333     object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ID,
334                                   nitro_enclave_get_parent_id,
335                                   nitro_enclave_set_parent_id);
336     object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ID,
337                                           "Set parent instance identifier");
338 }
339 
340 static const TypeInfo nitro_enclave_machine_info = {
341     .name          = TYPE_NITRO_ENCLAVE_MACHINE,
342     .parent        = TYPE_MICROVM_MACHINE,
343     .instance_size = sizeof(NitroEnclaveMachineState),
344     .instance_init = nitro_enclave_machine_initfn,
345     .class_size    = sizeof(NitroEnclaveMachineClass),
346     .class_init    = nitro_enclave_class_init,
347 };
348 
nitro_enclave_machine_init(void)349 static void nitro_enclave_machine_init(void)
350 {
351     type_register_static(&nitro_enclave_machine_info);
352 }
353 type_init(nitro_enclave_machine_init);
354