355 lines
11 KiB
C
355 lines
11 KiB
C
|
/*
|
||
|
* AWS nitro-enclave machine
|
||
|
*
|
||
|
* Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
|
||
|
*
|
||
|
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||
|
* (at your option) any later version. See the COPYING file in the
|
||
|
* top-level directory.
|
||
|
*/
|
||
|
|
||
|
#include "qemu/osdep.h"
|
||
|
#include "qemu/error-report.h"
|
||
|
#include "qapi/error.h"
|
||
|
#include "qom/object_interfaces.h"
|
||
|
|
||
|
#include "chardev/char.h"
|
||
|
#include "hw/sysbus.h"
|
||
|
#include "hw/core/eif.h"
|
||
|
#include "hw/i386/x86.h"
|
||
|
#include "hw/i386/microvm.h"
|
||
|
#include "hw/i386/nitro_enclave.h"
|
||
|
#include "hw/virtio/virtio-mmio.h"
|
||
|
#include "hw/virtio/virtio-nsm.h"
|
||
|
#include "hw/virtio/vhost-user-vsock.h"
|
||
|
#include "sysemu/hostmem.h"
|
||
|
|
||
|
static BusState *find_free_virtio_mmio_bus(void)
|
||
|
{
|
||
|
BusChild *kid;
|
||
|
BusState *bus = sysbus_get_default();
|
||
|
|
||
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||
|
DeviceState *dev = kid->child;
|
||
|
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO)) {
|
||
|
VirtIOMMIOProxy *mmio = VIRTIO_MMIO(OBJECT(dev));
|
||
|
VirtioBusState *mmio_virtio_bus = &mmio->bus;
|
||
|
BusState *mmio_bus = &mmio_virtio_bus->parent_obj;
|
||
|
if (QTAILQ_EMPTY(&mmio_bus->children)) {
|
||
|
return mmio_bus;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void vhost_user_vsock_init(NitroEnclaveMachineState *nems)
|
||
|
{
|
||
|
DeviceState *dev = qdev_new(TYPE_VHOST_USER_VSOCK);
|
||
|
VHostUserVSock *vsock = VHOST_USER_VSOCK(dev);
|
||
|
BusState *bus;
|
||
|
|
||
|
if (!nems->vsock) {
|
||
|
error_report("A valid chardev id for vhost-user-vsock device must be "
|
||
|
"provided using the 'vsock' machine option");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
bus = find_free_virtio_mmio_bus();
|
||
|
if (!bus) {
|
||
|
error_report("Failed to find bus for vhost-user-vsock device");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
Chardev *chardev = qemu_chr_find(nems->vsock);
|
||
|
if (!chardev) {
|
||
|
error_report("Failed to find chardev with id %s", nems->vsock);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
vsock->conf.chardev.chr = chardev;
|
||
|
|
||
|
qdev_realize_and_unref(dev, bus, &error_fatal);
|
||
|
}
|
||
|
|
||
|
static void virtio_nsm_init(NitroEnclaveMachineState *nems)
|
||
|
{
|
||
|
DeviceState *dev = qdev_new(TYPE_VIRTIO_NSM);
|
||
|
VirtIONSM *vnsm = VIRTIO_NSM(dev);
|
||
|
BusState *bus = find_free_virtio_mmio_bus();
|
||
|
|
||
|
if (!bus) {
|
||
|
error_report("Failed to find bus for virtio-nsm device.");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
qdev_prop_set_string(dev, "module-id", nems->id);
|
||
|
|
||
|
qdev_realize_and_unref(dev, bus, &error_fatal);
|
||
|
nems->vnsm = vnsm;
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_devices_init(NitroEnclaveMachineState *nems)
|
||
|
{
|
||
|
vhost_user_vsock_init(nems);
|
||
|
virtio_nsm_init(nems);
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_machine_state_init(MachineState *machine)
|
||
|
{
|
||
|
NitroEnclaveMachineClass *ne_class =
|
||
|
NITRO_ENCLAVE_MACHINE_GET_CLASS(machine);
|
||
|
NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine);
|
||
|
|
||
|
ne_class->parent_init(machine);
|
||
|
nitro_enclave_devices_init(ne_state);
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_machine_reset(MachineState *machine, ResetType type)
|
||
|
{
|
||
|
NitroEnclaveMachineClass *ne_class =
|
||
|
NITRO_ENCLAVE_MACHINE_GET_CLASS(machine);
|
||
|
NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine);
|
||
|
|
||
|
ne_class->parent_reset(machine, type);
|
||
|
|
||
|
memset(ne_state->vnsm->pcrs, 0, sizeof(ne_state->vnsm->pcrs));
|
||
|
|
||
|
/* PCR0 */
|
||
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 0, ne_state->image_sha384,
|
||
|
QCRYPTO_HASH_DIGEST_LEN_SHA384);
|
||
|
/* PCR1 */
|
||
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_sha384,
|
||
|
QCRYPTO_HASH_DIGEST_LEN_SHA384);
|
||
|
/* PCR2 */
|
||
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_sha384,
|
||
|
QCRYPTO_HASH_DIGEST_LEN_SHA384);
|
||
|
/* PCR3 */
|
||
|
if (ne_state->parent_role) {
|
||
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 3,
|
||
|
(uint8_t *) ne_state->parent_role,
|
||
|
strlen(ne_state->parent_role));
|
||
|
}
|
||
|
/* PCR4 */
|
||
|
if (ne_state->parent_id) {
|
||
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 4,
|
||
|
(uint8_t *) ne_state->parent_id,
|
||
|
strlen(ne_state->parent_id));
|
||
|
}
|
||
|
/* PCR8 */
|
||
|
if (ne_state->signature_found) {
|
||
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 8,
|
||
|
ne_state->fingerprint_sha384,
|
||
|
QCRYPTO_HASH_DIGEST_LEN_SHA384);
|
||
|
}
|
||
|
|
||
|
/* First 16 PCRs are locked from boot and reserved for nitro enclave */
|
||
|
for (int i = 0; i < 16; ++i) {
|
||
|
ne_state->vnsm->lock_pcr(ne_state->vnsm, i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_machine_initfn(Object *obj)
|
||
|
{
|
||
|
MicrovmMachineState *mms = MICROVM_MACHINE(obj);
|
||
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
nems->id = g_strdup("i-234-enc5678");
|
||
|
|
||
|
/* AWS nitro enclaves have PCIE and ACPI disabled */
|
||
|
mms->pcie = ON_OFF_AUTO_OFF;
|
||
|
x86ms->acpi = ON_OFF_AUTO_OFF;
|
||
|
}
|
||
|
|
||
|
static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg,
|
||
|
int acpi_data_size, bool pvh_enabled)
|
||
|
{
|
||
|
Error *err = NULL;
|
||
|
char *eif_kernel, *eif_initrd, *eif_cmdline;
|
||
|
MachineState *machine = MACHINE(x86ms);
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(x86ms);
|
||
|
|
||
|
if (!read_eif_file(machine->kernel_filename, machine->initrd_filename,
|
||
|
&eif_kernel, &eif_initrd, &eif_cmdline,
|
||
|
nems->image_sha384, nems->bootstrap_sha384,
|
||
|
nems->app_sha384, nems->fingerprint_sha384,
|
||
|
&(nems->signature_found), &err)) {
|
||
|
error_report_err(err);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
g_free(machine->kernel_filename);
|
||
|
machine->kernel_filename = eif_kernel;
|
||
|
g_free(machine->initrd_filename);
|
||
|
machine->initrd_filename = eif_initrd;
|
||
|
|
||
|
/*
|
||
|
* If kernel cmdline argument was provided, let's concatenate it to the
|
||
|
* extracted EIF kernel cmdline.
|
||
|
*/
|
||
|
if (machine->kernel_cmdline != NULL) {
|
||
|
char *cmd = g_strdup_printf("%s %s", eif_cmdline,
|
||
|
machine->kernel_cmdline);
|
||
|
g_free(eif_cmdline);
|
||
|
g_free(machine->kernel_cmdline);
|
||
|
machine->kernel_cmdline = cmd;
|
||
|
} else {
|
||
|
machine->kernel_cmdline = eif_cmdline;
|
||
|
}
|
||
|
|
||
|
x86_load_linux(x86ms, fw_cfg, 0, true);
|
||
|
|
||
|
unlink(machine->kernel_filename);
|
||
|
unlink(machine->initrd_filename);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static bool create_memfd_backend(MachineState *ms, const char *path,
|
||
|
Error **errp)
|
||
|
{
|
||
|
Object *obj;
|
||
|
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||
|
bool r = false;
|
||
|
|
||
|
obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
|
||
|
if (!object_property_set_int(obj, "size", ms->ram_size, errp)) {
|
||
|
goto out;
|
||
|
}
|
||
|
object_property_add_child(object_get_objects_root(), mc->default_ram_id,
|
||
|
obj);
|
||
|
|
||
|
if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
|
||
|
goto out;
|
||
|
}
|
||
|
r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
|
||
|
|
||
|
out:
|
||
|
object_unref(obj);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static char *nitro_enclave_get_vsock_chardev_id(Object *obj, Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
return g_strdup(nems->vsock);
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_set_vsock_chardev_id(Object *obj, const char *value,
|
||
|
Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
g_free(nems->vsock);
|
||
|
nems->vsock = g_strdup(value);
|
||
|
}
|
||
|
|
||
|
static char *nitro_enclave_get_id(Object *obj, Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
return g_strdup(nems->id);
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_set_id(Object *obj, const char *value,
|
||
|
Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
g_free(nems->id);
|
||
|
nems->id = g_strdup(value);
|
||
|
}
|
||
|
|
||
|
static char *nitro_enclave_get_parent_role(Object *obj, Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
return g_strdup(nems->parent_role);
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_set_parent_role(Object *obj, const char *value,
|
||
|
Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
g_free(nems->parent_role);
|
||
|
nems->parent_role = g_strdup(value);
|
||
|
}
|
||
|
|
||
|
static char *nitro_enclave_get_parent_id(Object *obj, Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
return g_strdup(nems->parent_id);
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_set_parent_id(Object *obj, const char *value,
|
||
|
Error **errp)
|
||
|
{
|
||
|
NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
|
||
|
|
||
|
g_free(nems->parent_id);
|
||
|
nems->parent_id = g_strdup(value);
|
||
|
}
|
||
|
|
||
|
static void nitro_enclave_class_init(ObjectClass *oc, void *data)
|
||
|
{
|
||
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||
|
MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc);
|
||
|
NitroEnclaveMachineClass *nemc = NITRO_ENCLAVE_MACHINE_CLASS(oc);
|
||
|
|
||
|
mmc->x86_load_linux = x86_load_eif;
|
||
|
|
||
|
mc->family = "nitro_enclave_i386";
|
||
|
mc->desc = "AWS Nitro Enclave";
|
||
|
|
||
|
nemc->parent_init = mc->init;
|
||
|
mc->init = nitro_enclave_machine_state_init;
|
||
|
|
||
|
nemc->parent_reset = mc->reset;
|
||
|
mc->reset = nitro_enclave_machine_reset;
|
||
|
|
||
|
mc->create_default_memdev = create_memfd_backend;
|
||
|
|
||
|
object_class_property_add_str(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
|
||
|
nitro_enclave_get_vsock_chardev_id,
|
||
|
nitro_enclave_set_vsock_chardev_id);
|
||
|
object_class_property_set_description(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
|
||
|
"Set chardev id for vhost-user-vsock "
|
||
|
"device");
|
||
|
|
||
|
object_class_property_add_str(oc, NITRO_ENCLAVE_ID, nitro_enclave_get_id,
|
||
|
nitro_enclave_set_id);
|
||
|
object_class_property_set_description(oc, NITRO_ENCLAVE_ID,
|
||
|
"Set enclave identifier");
|
||
|
|
||
|
object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ROLE,
|
||
|
nitro_enclave_get_parent_role,
|
||
|
nitro_enclave_set_parent_role);
|
||
|
object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ROLE,
|
||
|
"Set parent instance IAM role ARN");
|
||
|
|
||
|
object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ID,
|
||
|
nitro_enclave_get_parent_id,
|
||
|
nitro_enclave_set_parent_id);
|
||
|
object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ID,
|
||
|
"Set parent instance identifier");
|
||
|
}
|
||
|
|
||
|
static const TypeInfo nitro_enclave_machine_info = {
|
||
|
.name = TYPE_NITRO_ENCLAVE_MACHINE,
|
||
|
.parent = TYPE_MICROVM_MACHINE,
|
||
|
.instance_size = sizeof(NitroEnclaveMachineState),
|
||
|
.instance_init = nitro_enclave_machine_initfn,
|
||
|
.class_size = sizeof(NitroEnclaveMachineClass),
|
||
|
.class_init = nitro_enclave_class_init,
|
||
|
};
|
||
|
|
||
|
static void nitro_enclave_machine_init(void)
|
||
|
{
|
||
|
type_register_static(&nitro_enclave_machine_info);
|
||
|
}
|
||
|
type_init(nitro_enclave_machine_init);
|