On Mon, Sep 16, 2024, 1:26 AM Michael S. Tsirkin <m...@redhat.com> wrote:
> On Fri, Sep 06, 2024 at 01:57:32AM +0600, Dorjoy Chowdhury wrote: > > Nitro Secure Module (NSM)[1] device is used in AWS Nitro Enclaves[2] > > for stripped down TPM functionality like cryptographic attestation. > > The requests to and responses from NSM device are CBOR[3] encoded. > > > > This commit adds support for NSM device in QEMU. Although related to > > AWS Nitro Enclaves, the virito-nsm device is independent and can be > > used in other machine types as well. The libcbor[4] library has been > > used for the CBOR encoding and decoding functionalities. > > > > [1] > https://lists.oasis-open.org/archives/virtio-comment/202310/msg00387.html > > [2] https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave.html > > [3] http://cbor.io/ > > [4] https://libcbor.readthedocs.io/en/latest/ > > > > Signed-off-by: Dorjoy Chowdhury <dorjoychy...@gmail.com> > > --- > > MAINTAINERS | 10 + > > hw/virtio/Kconfig | 5 + > > hw/virtio/cbor-helpers.c | 326 ++++++ > > hw/virtio/meson.build | 6 + > > hw/virtio/virtio-nsm-pci.c | 73 ++ > > hw/virtio/virtio-nsm.c | 1665 ++++++++++++++++++++++++++++++ > > include/hw/virtio/cbor-helpers.h | 46 + > > include/hw/virtio/virtio-nsm.h | 59 ++ > > meson.build | 2 + > > 9 files changed, 2192 insertions(+) > > create mode 100644 hw/virtio/cbor-helpers.c > > create mode 100644 hw/virtio/virtio-nsm-pci.c > > create mode 100644 hw/virtio/virtio-nsm.c > > create mode 100644 include/hw/virtio/cbor-helpers.h > > create mode 100644 include/hw/virtio/virtio-nsm.h > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index c14ac014e2..b371c24747 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -2342,6 +2342,16 @@ F: include/sysemu/rng*.h > > F: backends/rng*.c > > F: tests/qtest/virtio-rng-test.c > > > > +virtio-nsm > > +M: Alexander Graf <g...@amazon.com> > > +M: Dorjoy Chowdhury <dorjoychy...@gmail.com> > > +S: Maintained > > +F: hw/virtio/cbor-helpers.c > > +F: hw/virtio/virtio-nsm.c > > +F: hw/virtio/virtio-nsm-pci.c > > +F: include/hw/virtio/cbor-helpers.h > > +F: include/hw/virtio/virtio-nsm.h > > + > > vhost-user-stubs > > M: Alex Bennée <alex.ben...@linaro.org> > > S: Maintained > > diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig > > index aa63ff7fd4..29fee32035 100644 > > --- a/hw/virtio/Kconfig > > +++ b/hw/virtio/Kconfig > > @@ -6,6 +6,11 @@ config VIRTIO_RNG > > default y > > depends on VIRTIO > > > > +config VIRTIO_NSM > > + bool > > + default y > > + depends on VIRTIO > > + > > config VIRTIO_IOMMU > > bool > > default y > > diff --git a/hw/virtio/cbor-helpers.c b/hw/virtio/cbor-helpers.c > > new file mode 100644 > > index 0000000000..a0e58d6862 > > --- /dev/null > > +++ b/hw/virtio/cbor-helpers.c > > @@ -0,0 +1,326 @@ > > +/* > > + * QEMU CBOR helpers > > + * > > + * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy...@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 "hw/virtio/cbor-helpers.h" > > + > > +bool qemu_cbor_map_add(cbor_item_t *map, cbor_item_t *key, cbor_item_t > *value) > > +{ > > + bool success = false; > > + struct cbor_pair pair = (struct cbor_pair) { > > + .key = cbor_move(key), > > + .value = cbor_move(value) > > + }; > > + > > + success = cbor_map_add(map, pair); > > + if (!success) { > > + cbor_incref(pair.key); > > + cbor_incref(pair.value); > > + } > > + > > + return success; > > +} > > + > > +bool qemu_cbor_array_push(cbor_item_t *array, cbor_item_t *value) > > +{ > > + bool success = false; > > + > > + success = cbor_array_push(array, cbor_move(value)); > > + if (!success) { > > + cbor_incref(value); > > + } > > + > > + return success; > > +} > > + > > +bool qemu_cbor_add_bool_to_map(cbor_item_t *map, const char *key, bool > value) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_build_bool(value); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_uint8_to_map(cbor_item_t *map, const char *key, > > + uint8_t value) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_build_uint8(value); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_map_to_map(cbor_item_t *map, const char *key, > > + size_t nested_map_size, > > + cbor_item_t **nested_map) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_new_definite_map(nested_map_size); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + *nested_map = value_cbor; > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_bytestring_to_map(cbor_item_t *map, const char *key, > > + uint8_t *arr, size_t len) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_build_bytestring(arr, len); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_bytestring_or_null_to_map(cbor_item_t *map, const > char *key, > > + uint8_t *arr, size_t len) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + if (len) { > > + value_cbor = cbor_build_bytestring(arr, len); > > + } else { > > + value_cbor = cbor_new_null(); > > + } > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_string_to_map(cbor_item_t *map, const char *key, > > + const char *value) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_build_string(value); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_uint8_array_to_map(cbor_item_t *map, const char *key, > > + uint8_t *arr, size_t len) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_new_definite_array(len); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + > > + for (int i = 0; i < len; ++i) { > > + cbor_item_t *tmp = cbor_build_uint8(arr[i]); > > + if (!tmp) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_array_push(value_cbor, tmp)) { > > + cbor_decref(&tmp); > > + goto cleanup; > > + } > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_uint8_key_bytestring_to_map(cbor_item_t *map, > uint8_t key, > > + uint8_t *buf, size_t len) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_uint8(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_build_bytestring(buf, len); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > + > > +bool qemu_cbor_add_uint64_to_map(cbor_item_t *map, const char *key, > > + uint64_t value) > > +{ > > + cbor_item_t *key_cbor = NULL; > > + cbor_item_t *value_cbor = NULL; > > + > > + key_cbor = cbor_build_string(key); > > + if (!key_cbor) { > > + goto cleanup; > > + } > > + value_cbor = cbor_build_uint64(value); > > + if (!value_cbor) { > > + goto cleanup; > > + } > > + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { > > + goto cleanup; > > + } > > + > > + return true; > > + > > + cleanup: > > + if (key_cbor) { > > + cbor_decref(&key_cbor); > > + } > > + if (value_cbor) { > > + cbor_decref(&value_cbor); > > + } > > + return false; > > +} > > diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build > > index 621fc65454..1fe7cb4d72 100644 > > --- a/hw/virtio/meson.build > > +++ b/hw/virtio/meson.build > > @@ -54,6 +54,9 @@ specific_virtio_ss.add(when: 'CONFIG_VIRTIO_PMEM', > if_true: files('virtio-pmem.c > > specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: > files('vhost-vsock.c')) > > specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: > files('vhost-user-vsock.c')) > > specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: > files('virtio-rng.c')) > > +if libcbor.found() > > + specific_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: > [files('virtio-nsm.c', 'cbor-helpers.c'), libcbor]) > > +endif > > specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: > files('virtio-mem.c')) > > specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: > files('vhost-user-scmi.c')) > > specific_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', > 'CONFIG_VHOST_USER_SCMI'], if_true: files('vhost-user-scmi-pci.c')) > > @@ -70,6 +73,9 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', > if_true: files('virtio-crypto-pc > > virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: > files('virtio-input-host-pci.c')) > > virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: > files('virtio-input-pci.c')) > > virtio_pci_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: > files('virtio-rng-pci.c')) > > +if libcbor.found() > > + virtio_pci_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: > [files('virtio-nsm-pci.c', 'cbor-helpers.c'), libcbor]) > > +endif > > virtio_pci_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: > files('virtio-balloon-pci.c')) > > virtio_pci_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: > files('virtio-9p-pci.c')) > > virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: > files('virtio-scsi-pci.c')) > > diff --git a/hw/virtio/virtio-nsm-pci.c b/hw/virtio/virtio-nsm-pci.c > > new file mode 100644 > > index 0000000000..dca797315a > > --- /dev/null > > +++ b/hw/virtio/virtio-nsm-pci.c > > @@ -0,0 +1,73 @@ > > +/* > > + * AWS Nitro Secure Module (NSM) device > > + * > > + * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy...@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 "hw/virtio/virtio-pci.h" > > +#include "hw/virtio/virtio-nsm.h" > > +#include "hw/qdev-properties.h" > > +#include "qapi/error.h" > > +#include "qemu/module.h" > > +#include "qom/object.h" > > + > > +typedef struct VirtIONsmPCI VirtIONsmPCI; > > + > > +#define TYPE_VIRTIO_NSM_PCI "virtio-nsm-pci-base" > > +DECLARE_INSTANCE_CHECKER(VirtIONsmPCI, VIRTIO_NSM_PCI, > > + TYPE_VIRTIO_NSM_PCI) > > + > > +struct VirtIONsmPCI { > > + VirtIOPCIProxy parent_obj; > > + VirtIONSM vdev; > > +}; > > + > > +static void virtio_nsm_pci_realize(VirtIOPCIProxy *vpci_dev, Error > **errp) > > +{ > > + VirtIONsmPCI *vnsm = VIRTIO_NSM_PCI(vpci_dev); > > + DeviceState *vdev = DEVICE(&vnsm->vdev); > > + > > + virtio_pci_force_virtio_1(vpci_dev); > > + > > + if (!qdev_realize(vdev, BUS(&vpci_dev->bus), errp)) { > > + return; > > + } > > +} > > + > > +static void virtio_nsm_pci_class_init(ObjectClass *klass, void *data) > > +{ > > + DeviceClass *dc = DEVICE_CLASS(klass); > > + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); > > + > > + k->realize = virtio_nsm_pci_realize; > > + set_bit(DEVICE_CATEGORY_MISC, dc->categories); > > +} > > + > > +static void virtio_nsm_initfn(Object *obj) > > +{ > > + VirtIONsmPCI *dev = VIRTIO_NSM_PCI(obj); > > + > > + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), > > + TYPE_VIRTIO_NSM); > > +} > > + > > +static const VirtioPCIDeviceTypeInfo virtio_nsm_pci_info = { > > + .base_name = TYPE_VIRTIO_NSM_PCI, > > + .generic_name = "virtio-nsm-pci", > > + .instance_size = sizeof(VirtIONsmPCI), > > + .instance_init = virtio_nsm_initfn, > > + .class_init = virtio_nsm_pci_class_init, > > +}; > > + > > +static void virtio_nsm_pci_register(void) > > +{ > > + virtio_pci_types_register(&virtio_nsm_pci_info); > > +} > > + > > +type_init(virtio_nsm_pci_register) > > diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c > > new file mode 100644 > > index 0000000000..0f4edd92ff > > --- /dev/null > > +++ b/hw/virtio/virtio-nsm.c > > @@ -0,0 +1,1665 @@ > > +/* > > + * AWS Nitro Secure Module (NSM) device > > + * > > + * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy...@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/iov.h" > > +#include "qemu/guest-random.h" > > +#include "qapi/error.h" > > + > > +#include "crypto/hash.h" > > +#include "hw/virtio/virtio.h" > > +#include "hw/virtio/virtio-nsm.h" > > +#include "hw/virtio/cbor-helpers.h" > > +#include "standard-headers/linux/virtio_ids.h" > > + > > +#define NSM_RESPONSE_BUF_SIZE 0x3000 > > +#define NSM_PCR_DATA_REQ_MAX_SIZE 512 > > + > > +enum NSMResponseTypes { > > + NSM_SUCCESS = 0, > > + NSM_INVALID_ARGUMENT = 1, > > + NSM_INVALID_INDEX = 2, > > + NSM_READONLY_INDEX = 3, > > + NSM_INVALID_OPERATION = 4, > > + NSM_BUFFER_TOO_SMALL = 5, > > + NSM_INPUT_TOO_LARGE = 6, > > + NSM_INTERNAL_ERROR = 7, > > +}; > > + > > +static const char *error_string(enum NSMResponseTypes type) > > +{ > > + const char *str; > > + switch (type) { > > + case NSM_INVALID_ARGUMENT: > > + str = "InvalidArgument"; > > + break; > > + case NSM_INVALID_INDEX: > > + str = "InvalidIndex"; > > + break; > > + case NSM_READONLY_INDEX: > > + str = "ReadOnlyIndex"; > > + break; > > + case NSM_INVALID_OPERATION: > > + str = "InvalidOperation"; > > + break; > > + case NSM_BUFFER_TOO_SMALL: > > + str = "BufferTooSmall"; > > + break; > > + case NSM_INPUT_TOO_LARGE: > > + str = "InputTooLarge"; > > + break; > > + default: > > + str = "InternalError"; > > + break; > > + } > > + > > + return str; > > +} > > + > > +/* > > + * Error response structure: > > + * > > + * { > > + * Map(1) { > > + * key = String("Error"), > > + * value = String(error_name) > > + * } > > + * } > > + * > > + * where error_name can be one of the following: > > + * InvalidArgument > > + * InvalidIndex > > + * InvalidResponse > > + * ReadOnlyIndex > > + * InvalidOperation > > + * BufferTooSmall > > + * InputTooLarge > > + * InternalError > > + */ > > + > > +static bool error_response(struct iovec *response, enum > NSMResponseTypes error, > > + Error **errp) > > +{ > > + cbor_item_t *root; > > + size_t len; > > + bool r = false; > > + > > + root = cbor_new_definite_map(1); > > + if (!root) { > > + goto err; > > + } > > + > > + if (!qemu_cbor_add_string_to_map(root, "Error", > error_string(error))) { > > + goto err; > > + } > > + > > + len = cbor_serialize(root, response->iov_base, response->iov_len); > > As far as I can tell, all these also need to be switched to use > iov_from_buf. > Sorry I didn't understand this. The iovecs passed in these functions are not the iovecs from virtqueue. We make an iovec for the response and then pass it down. We do the "iov_from_buf" after calling "get_nsm_request_response" in "handle_input" function. Am I missing something? Regards, Dorjoy