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