mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 20:33:54 -06:00

We were storing the pointers to buffers in a GList due to lack of stateful crypto apis and instead doing the final hash computation at the end after we had all the necessary buffers. Now that we have the stateful qcrypto apis available, we can instead update the hashes inline in the read_eif_* functions which makes the code much simpler. Signed-off-by: Dorjoy Chowdhury <dorjoychy111@gmail.com> Reviewed-by: Alexander Graf <graf@amazon.com> Message-ID: <20241109123039.24180-1-dorjoychy111@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
354 lines
11 KiB
C
354 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_hash,
|
|
QCRYPTO_HASH_DIGEST_LEN_SHA384);
|
|
/* PCR1 */
|
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_hash,
|
|
QCRYPTO_HASH_DIGEST_LEN_SHA384);
|
|
/* PCR2 */
|
|
ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_hash,
|
|
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_hash,
|
|
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_hash, nems->bootstrap_hash,
|
|
nems->app_hash, nems->fingerprint_hash,
|
|
&(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);
|