xref: /openbmc/qemu/hw/i386/nitro_enclave.c (revision 7d87775f)
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 
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 
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 
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 
93 static void nitro_enclave_devices_init(NitroEnclaveMachineState *nems)
94 {
95     vhost_user_vsock_init(nems);
96     virtio_nsm_init(nems);
97 }
98 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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