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 <dorjoychy111@gmail.com> Reviewed-by: Alexander Graf <graf@amazon.com> Link: https://lore.kernel.org/r/20241008211727.49088-3-dorjoychy111@gmail.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
		
			
				
	
	
		
			1733 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1733 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * AWS Nitro Secure Module (NSM) device
 | 
						|
 *
 | 
						|
 * 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/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_REQUEST_MAX_SIZE      0x1000
 | 
						|
#define NSM_RESPONSE_BUF_SIZE     0x3000
 | 
						|
#define NSM_RND_BUF_SIZE          256
 | 
						|
 | 
						|
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);
 | 
						|
    if (len == 0) {
 | 
						|
        error_setg(errp, "Response buffer is small for %s response",
 | 
						|
                   error_string(error));
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
 | 
						|
 out:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize %s response", error_string(error));
 | 
						|
    goto out;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * GetRandom response structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("GetRandom"),
 | 
						|
 *     value = Map(1) {
 | 
						|
 *       key = String("random"),
 | 
						|
 *       value = Byte_String()
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
static bool handle_get_random(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                              struct iovec *response, Error **errp)
 | 
						|
{
 | 
						|
    cbor_item_t *root, *nested_map;
 | 
						|
    size_t len;
 | 
						|
    uint8_t rnd[NSM_RND_BUF_SIZE];
 | 
						|
    bool r = false;
 | 
						|
 | 
						|
    root = cbor_new_definite_map(1);
 | 
						|
    if (!root) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_map_to_map(root, "GetRandom", 1, &nested_map)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    qemu_guest_getrandom_nofail(rnd, NSM_RND_BUF_SIZE);
 | 
						|
 | 
						|
    if (!qemu_cbor_add_bytestring_to_map(nested_map, "random", rnd,
 | 
						|
                                         NSM_RND_BUF_SIZE)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, response->iov_base, response->iov_len);
 | 
						|
    if (len == 0) {
 | 
						|
        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
 | 
						|
 out:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize GetRandom response");
 | 
						|
    goto out;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * DescribeNSM response structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("DescribeNSM"),
 | 
						|
 *     value = Map(7) {
 | 
						|
 *       key = String("digest"),
 | 
						|
 *       value = String("SHA384"),
 | 
						|
 *       key = String("max_pcrs"),
 | 
						|
 *       value = Uint8(32),
 | 
						|
 *       key = String("module_id"),
 | 
						|
 *       value = String("i-1234-enc5678"),
 | 
						|
 *       key = String("locked_pcrs"),
 | 
						|
 *       value = Array<Uint8>(),
 | 
						|
 *       key = String("version_major"),
 | 
						|
 *       value = Uint8(1),
 | 
						|
 *       key = String("version_minor"),
 | 
						|
 *       value = Uint8(0),
 | 
						|
 *       key = String("version_patch"),
 | 
						|
 *       value = Uint8(0)
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
static bool handle_describe_nsm(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                                struct iovec *response, Error **errp)
 | 
						|
{
 | 
						|
    cbor_item_t *root, *nested_map;
 | 
						|
    uint16_t locked_pcrs_cnt = 0;
 | 
						|
    uint8_t locked_pcrs_ind[NSM_MAX_PCRS];
 | 
						|
    size_t len;
 | 
						|
    bool r = false;
 | 
						|
 | 
						|
    root = cbor_new_definite_map(1);
 | 
						|
    if (!root) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_map_to_map(root, "DescribeNSM", 7, &nested_map)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_string_to_map(nested_map, "digest", vnsm->digest)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_uint8_to_map(nested_map, "max_pcrs", vnsm->max_pcrs)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_string_to_map(nested_map, "module_id",
 | 
						|
                                     vnsm->module_id)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
 | 
						|
        if (vnsm->pcrs[i].locked) {
 | 
						|
            locked_pcrs_ind[locked_pcrs_cnt++] = i;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_uint8_array_to_map(nested_map, "locked_pcrs",
 | 
						|
                                          locked_pcrs_ind, locked_pcrs_cnt)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_uint8_to_map(nested_map, "version_major",
 | 
						|
                                    vnsm->version_major)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_uint8_to_map(nested_map, "version_minor",
 | 
						|
                                    vnsm->version_minor)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_uint8_to_map(nested_map, "version_patch",
 | 
						|
                                    vnsm->version_patch)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, response->iov_base, response->iov_len);
 | 
						|
    if (len == 0) {
 | 
						|
        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
 | 
						|
 out:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize DescribeNSM response");
 | 
						|
    goto out;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * DescribePCR request structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("DescribePCR"),
 | 
						|
 *     value = Map(1) {
 | 
						|
 *       key = String("index"),
 | 
						|
 *       value = Uint8(pcr)
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
typedef struct NSMDescribePCRReq {
 | 
						|
    uint8_t index;
 | 
						|
} NSMDescribePCRReq;
 | 
						|
 | 
						|
static enum NSMResponseTypes get_nsm_describe_pcr_req(
 | 
						|
    uint8_t *req, size_t len,
 | 
						|
    NSMDescribePCRReq *nsm_req)
 | 
						|
{
 | 
						|
    size_t size;
 | 
						|
    uint8_t *str;
 | 
						|
    struct cbor_pair *pair;
 | 
						|
    cbor_item_t *item = NULL;
 | 
						|
    struct cbor_load_result result;
 | 
						|
    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
 | 
						|
 | 
						|
    item = cbor_load(req, len, &result);
 | 
						|
    if (!item || result.error.code != CBOR_ERR_NONE) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(item);
 | 
						|
    if (!cbor_isa_map(pair->value)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    size = cbor_map_size(pair->value);
 | 
						|
    if (size < 1) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(pair->value);
 | 
						|
    for (int i = 0; i < size; ++i) {
 | 
						|
        if (!cbor_isa_string(pair[i].key)) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        str = cbor_string_handle(pair[i].key);
 | 
						|
        if (str && cbor_string_length(pair[i].key) == 5 &&
 | 
						|
            memcmp(str, "index", 5) == 0) {
 | 
						|
            if (!cbor_isa_uint(pair[i].value) ||
 | 
						|
                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            nsm_req->index = cbor_get_uint8(pair[i].value);
 | 
						|
            r = NSM_SUCCESS;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (item) {
 | 
						|
        cbor_decref(&item);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * DescribePCR response structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("DescribePCR"),
 | 
						|
 *     value = Map(2) {
 | 
						|
 *       key = String("data"),
 | 
						|
 *       value = Byte_String(),
 | 
						|
 *       key = String("lock"),
 | 
						|
 *       value = Bool()
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
static bool handle_describe_pcr(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                                struct iovec *response, Error **errp)
 | 
						|
{
 | 
						|
    cbor_item_t *root = NULL;
 | 
						|
    cbor_item_t *nested_map;
 | 
						|
    size_t len;
 | 
						|
    NSMDescribePCRReq nsm_req;
 | 
						|
    enum NSMResponseTypes type;
 | 
						|
    struct PCRInfo *pcr;
 | 
						|
    bool r = false;
 | 
						|
 | 
						|
    type = get_nsm_describe_pcr_req(request->iov_base, request->iov_len,
 | 
						|
                                    &nsm_req);
 | 
						|
    if (type != NSM_SUCCESS) {
 | 
						|
        if (error_response(response, type, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
    if (nsm_req.index >= vnsm->max_pcrs) {
 | 
						|
        if (error_response(response, NSM_INVALID_INDEX, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
    pcr = &(vnsm->pcrs[nsm_req.index]);
 | 
						|
 | 
						|
    root = cbor_new_definite_map(1);
 | 
						|
    if (!root) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_map_to_map(root, "DescribePCR", 2, &nested_map)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
 | 
						|
                                         QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_bool_to_map(nested_map, "lock", pcr->locked)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, response->iov_base, response->iov_len);
 | 
						|
    if (len == 0) {
 | 
						|
        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
 | 
						|
 out:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize DescribePCR response");
 | 
						|
    goto out;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * ExtendPCR request structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("ExtendPCR"),
 | 
						|
 *     value = Map(2) {
 | 
						|
 *       key = String("index"),
 | 
						|
 *       value = Uint8(pcr),
 | 
						|
 *       key = String("data"),
 | 
						|
 *       value = Byte_String(data),
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
typedef struct NSMExtendPCRReq {
 | 
						|
    uint8_t index;
 | 
						|
    uint16_t data_len;
 | 
						|
    uint8_t data[NSM_REQUEST_MAX_SIZE];
 | 
						|
} NSMExtendPCRReq;
 | 
						|
 | 
						|
static enum NSMResponseTypes get_nsm_extend_pcr_req(uint8_t *req, size_t len,
 | 
						|
                                                    NSMExtendPCRReq *nsm_req)
 | 
						|
{
 | 
						|
    cbor_item_t *item = NULL;
 | 
						|
    size_t size ;
 | 
						|
    uint8_t *str;
 | 
						|
    bool index_found = false;
 | 
						|
    bool data_found = false;
 | 
						|
    struct cbor_pair *pair;
 | 
						|
    struct cbor_load_result result;
 | 
						|
    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
 | 
						|
 | 
						|
    item = cbor_load(req, len, &result);
 | 
						|
    if (!item || result.error.code != CBOR_ERR_NONE) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(item);
 | 
						|
    if (!cbor_isa_map(pair->value)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    size = cbor_map_size(pair->value);
 | 
						|
    if (size < 2) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(pair->value);
 | 
						|
    for (int i = 0; i < size; ++i) {
 | 
						|
        if (!cbor_isa_string(pair[i].key)) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        str = cbor_string_handle(pair[i].key);
 | 
						|
        if (!str) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (cbor_string_length(pair[i].key) == 5 &&
 | 
						|
            memcmp(str, "index", 5) == 0) {
 | 
						|
            if (!cbor_isa_uint(pair[i].value) ||
 | 
						|
                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
 | 
						|
                goto cleanup;
 | 
						|
            }
 | 
						|
            nsm_req->index = cbor_get_uint8(pair[i].value);
 | 
						|
            index_found = true;
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (cbor_string_length(pair[i].key) == 4 &&
 | 
						|
            memcmp(str, "data", 4) == 0) {
 | 
						|
            if (!cbor_isa_bytestring(pair[i].value)) {
 | 
						|
                goto cleanup;
 | 
						|
            }
 | 
						|
            str = cbor_bytestring_handle(pair[i].value);
 | 
						|
            if (!str) {
 | 
						|
                goto cleanup;
 | 
						|
            }
 | 
						|
            nsm_req->data_len = cbor_bytestring_length(pair[i].value);
 | 
						|
            /*
 | 
						|
             * nsm_req->data_len will be smaller than NSM_REQUEST_MAX_SIZE as
 | 
						|
             * we already check for the max request size before processing
 | 
						|
             * any request. So it's safe to copy.
 | 
						|
             */
 | 
						|
            memcpy(nsm_req->data, str, nsm_req->data_len);
 | 
						|
            data_found = true;
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (index_found && data_found) {
 | 
						|
        r = NSM_SUCCESS;
 | 
						|
    }
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (item) {
 | 
						|
        cbor_decref(&item);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * ExtendPCR response structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("ExtendPCR"),
 | 
						|
 *     value = Map(1) {
 | 
						|
 *       key = String("data"),
 | 
						|
 *       value = Byte_String()
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
static bool handle_extend_pcr(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                              struct iovec *response, Error **errp)
 | 
						|
{
 | 
						|
    cbor_item_t *root = NULL;
 | 
						|
    cbor_item_t *nested_map;
 | 
						|
    size_t len;
 | 
						|
    struct PCRInfo *pcr;
 | 
						|
    enum NSMResponseTypes type;
 | 
						|
    bool r = false;
 | 
						|
    g_autofree NSMExtendPCRReq *nsm_req = g_malloc(sizeof(NSMExtendPCRReq));
 | 
						|
 | 
						|
    type = get_nsm_extend_pcr_req(request->iov_base, request->iov_len,
 | 
						|
                                  nsm_req);
 | 
						|
    if (type != NSM_SUCCESS) {
 | 
						|
        if (error_response(response, type, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
    if (nsm_req->index >= vnsm->max_pcrs) {
 | 
						|
        if (error_response(response, NSM_INVALID_INDEX, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    pcr = &(vnsm->pcrs[nsm_req->index]);
 | 
						|
 | 
						|
    if (pcr->locked) {
 | 
						|
        if (error_response(response, NSM_READONLY_INDEX, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!vnsm->extend_pcr(vnsm, nsm_req->index, nsm_req->data,
 | 
						|
                          nsm_req->data_len)) {
 | 
						|
        if (error_response(response, NSM_INTERNAL_ERROR, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    root = cbor_new_definite_map(1);
 | 
						|
    if (!root) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_map_to_map(root, "ExtendPCR", 1, &nested_map)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
 | 
						|
                                         QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, response->iov_base, response->iov_len);
 | 
						|
    if (len == 0) {
 | 
						|
        if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
 | 
						|
 out:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize DescribePCR response");
 | 
						|
    goto out;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * LockPCR request structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("LockPCR"),
 | 
						|
 *     value = Map(1) {
 | 
						|
 *       key = String("index"),
 | 
						|
 *       value = Uint8(pcr)
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
typedef struct NSMLockPCRReq {
 | 
						|
    uint8_t index;
 | 
						|
} NSMLockPCRReq;
 | 
						|
 | 
						|
static enum NSMResponseTypes get_nsm_lock_pcr_req(uint8_t *req, size_t len,
 | 
						|
                                                  NSMLockPCRReq *nsm_req)
 | 
						|
{
 | 
						|
    cbor_item_t *item = NULL;
 | 
						|
    size_t size;
 | 
						|
    uint8_t *str;
 | 
						|
    struct cbor_pair *pair;
 | 
						|
    struct cbor_load_result result;
 | 
						|
    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
 | 
						|
 | 
						|
    item = cbor_load(req, len, &result);
 | 
						|
    if (!item || result.error.code != CBOR_ERR_NONE) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(item);
 | 
						|
    if (!cbor_isa_map(pair->value)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    size = cbor_map_size(pair->value);
 | 
						|
    if (size < 1) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(pair->value);
 | 
						|
    for (int i = 0; i < size; ++i) {
 | 
						|
        if (!cbor_isa_string(pair[i].key)) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        str = cbor_string_handle(pair[i].key);
 | 
						|
        if (str && cbor_string_length(pair[i].key) == 5 &&
 | 
						|
            memcmp(str, "index", 5) == 0) {
 | 
						|
            if (!cbor_isa_uint(pair[i].value) ||
 | 
						|
                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            nsm_req->index = cbor_get_uint8(pair[i].value);
 | 
						|
            r = NSM_SUCCESS;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (item) {
 | 
						|
        cbor_decref(&item);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * LockPCR success response structure:
 | 
						|
 * {
 | 
						|
 *   String("LockPCR")
 | 
						|
 * }
 | 
						|
 */
 | 
						|
static bool handle_lock_pcr(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                            struct iovec *response, Error **errp)
 | 
						|
{
 | 
						|
    cbor_item_t *root = NULL;
 | 
						|
    size_t len;
 | 
						|
    NSMLockPCRReq nsm_req;
 | 
						|
    enum NSMResponseTypes type;
 | 
						|
    struct PCRInfo *pcr;
 | 
						|
    bool r = false;
 | 
						|
 | 
						|
    type = get_nsm_lock_pcr_req(request->iov_base, request->iov_len, &nsm_req);
 | 
						|
    if (type != NSM_SUCCESS) {
 | 
						|
        if (error_response(response, type, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (nsm_req.index >= vnsm->max_pcrs) {
 | 
						|
        if (error_response(response, NSM_INVALID_INDEX, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pcr = &(vnsm->pcrs[nsm_req.index]);
 | 
						|
 | 
						|
    if (pcr->locked) {
 | 
						|
        if (error_response(response, NSM_READONLY_INDEX, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pcr->locked = true;
 | 
						|
 | 
						|
    root = cbor_build_string("LockPCR");
 | 
						|
    if (!root) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, response->iov_base, response->iov_len);
 | 
						|
    if (len == 0) {
 | 
						|
        if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
    goto cleanup;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize LockPCR response");
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * LockPCRs request structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("LockPCRs"),
 | 
						|
 *     value = Map(1) {
 | 
						|
 *       key = String("range"),
 | 
						|
 *       value = Uint8(pcr)
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
typedef struct NSMLockPCRsReq {
 | 
						|
    uint16_t range;
 | 
						|
} NSMLockPCRsReq;
 | 
						|
 | 
						|
static enum NSMResponseTypes get_nsm_lock_pcrs_req(uint8_t *req, size_t len,
 | 
						|
                                                   NSMLockPCRsReq *nsm_req)
 | 
						|
{
 | 
						|
    cbor_item_t *item = NULL;
 | 
						|
    size_t size;
 | 
						|
    uint8_t *str;
 | 
						|
    struct cbor_pair *pair;
 | 
						|
    struct cbor_load_result result;
 | 
						|
    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
 | 
						|
 | 
						|
    item = cbor_load(req, len, &result);
 | 
						|
    if (!item || result.error.code != CBOR_ERR_NONE) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(item);
 | 
						|
    if (!cbor_isa_map(pair->value)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    size = cbor_map_size(pair->value);
 | 
						|
    if (size < 1) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(pair->value);
 | 
						|
    for (int i = 0; i < size; ++i) {
 | 
						|
        if (!cbor_isa_string(pair[i].key)) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        str = cbor_string_handle(pair[i].key);
 | 
						|
        if (str && cbor_string_length(pair[i].key) == 5 &&
 | 
						|
            memcmp(str, "range", 5) == 0) {
 | 
						|
            if (!cbor_isa_uint(pair[i].value) ||
 | 
						|
                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            nsm_req->range = cbor_get_uint8(pair[i].value);
 | 
						|
            r = NSM_SUCCESS;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (item) {
 | 
						|
        cbor_decref(&item);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * LockPCRs success response structure:
 | 
						|
 * {
 | 
						|
 *   String("LockPCRs")
 | 
						|
 * }
 | 
						|
 */
 | 
						|
static bool handle_lock_pcrs(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                             struct iovec *response, Error **errp)
 | 
						|
{
 | 
						|
    cbor_item_t *root = NULL;
 | 
						|
    size_t len;
 | 
						|
    NSMLockPCRsReq nsm_req;
 | 
						|
    enum NSMResponseTypes type;
 | 
						|
    bool r = false;
 | 
						|
 | 
						|
    type = get_nsm_lock_pcrs_req(request->iov_base, request->iov_len, &nsm_req);
 | 
						|
    if (type != NSM_SUCCESS) {
 | 
						|
        if (error_response(response, type, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (nsm_req.range > vnsm->max_pcrs) {
 | 
						|
        if (error_response(response, NSM_INVALID_INDEX, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    for (int i = 0; i < nsm_req.range; ++i) {
 | 
						|
        vnsm->pcrs[i].locked = true;
 | 
						|
    }
 | 
						|
 | 
						|
    root = cbor_build_string("LockPCRs");
 | 
						|
    if (!root) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, response->iov_base, response->iov_len);
 | 
						|
    if (len == 0) {
 | 
						|
        if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
    goto cleanup;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize response");
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Attestation request structure:
 | 
						|
 *
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("Attestation"),
 | 
						|
 *     value = Map(3) {
 | 
						|
 *       key = String("user_data"),
 | 
						|
 *       value = Byte_String() || null, // Optional
 | 
						|
 *       key = String("nonce"),
 | 
						|
 *       value = Byte_String() || null, // Optional
 | 
						|
 *       key = String("public_key"),
 | 
						|
 *       value = Byte_String() || null, // Optional
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
 | 
						|
struct AttestationProperty {
 | 
						|
    bool is_null; /* True if property is not present in map or is null */
 | 
						|
    uint16_t len;
 | 
						|
    uint8_t buf[NSM_REQUEST_MAX_SIZE];
 | 
						|
};
 | 
						|
 | 
						|
typedef struct NSMAttestationReq {
 | 
						|
    struct AttestationProperty public_key;
 | 
						|
    struct AttestationProperty user_data;
 | 
						|
    struct AttestationProperty nonce;
 | 
						|
} NSMAttestationReq;
 | 
						|
 | 
						|
static bool fill_attestation_property(struct AttestationProperty *prop,
 | 
						|
                                      cbor_item_t *value)
 | 
						|
{
 | 
						|
    uint8_t *str;
 | 
						|
    bool ret = false;
 | 
						|
 | 
						|
    if (cbor_is_null(value)) {
 | 
						|
        prop->is_null = true;
 | 
						|
        ret = true;
 | 
						|
        goto out;
 | 
						|
    } else if (cbor_isa_bytestring(value)) {
 | 
						|
        str = cbor_bytestring_handle(value);
 | 
						|
        if (!str) {
 | 
						|
            goto out;
 | 
						|
        }
 | 
						|
        prop->len = cbor_bytestring_length(value);
 | 
						|
    } else if (cbor_isa_string(value)) {
 | 
						|
        str = cbor_string_handle(value);
 | 
						|
        if (!str) {
 | 
						|
            goto out;
 | 
						|
        }
 | 
						|
        prop->len = cbor_string_length(value);
 | 
						|
    } else {
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * prop->len will be smaller than NSM_REQUEST_MAX_SIZE as we
 | 
						|
     * already check for the max request size before processing
 | 
						|
     * any request. So it's safe to copy.
 | 
						|
     */
 | 
						|
    memcpy(prop->buf, str, prop->len);
 | 
						|
    prop->is_null = false;
 | 
						|
    ret = true;
 | 
						|
 | 
						|
 out:
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static enum NSMResponseTypes get_nsm_attestation_req(uint8_t *req, size_t len,
 | 
						|
                                                     NSMAttestationReq *nsm_req)
 | 
						|
{
 | 
						|
    cbor_item_t *item = NULL;
 | 
						|
    size_t size;
 | 
						|
    uint8_t *str;
 | 
						|
    struct cbor_pair *pair;
 | 
						|
    struct cbor_load_result result;
 | 
						|
    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
 | 
						|
 | 
						|
    nsm_req->public_key.is_null = true;
 | 
						|
    nsm_req->user_data.is_null = true;
 | 
						|
    nsm_req->nonce.is_null = true;
 | 
						|
 | 
						|
    item = cbor_load(req, len, &result);
 | 
						|
    if (!item || result.error.code != CBOR_ERR_NONE) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(item);
 | 
						|
    if (!cbor_isa_map(pair->value)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    size = cbor_map_size(pair->value);
 | 
						|
    if (size == 0) {
 | 
						|
        r = NSM_SUCCESS;
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    pair = cbor_map_handle(pair->value);
 | 
						|
    for (int i = 0; i < size; ++i) {
 | 
						|
        if (!cbor_isa_string(pair[i].key)) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        str = cbor_string_handle(pair[i].key);
 | 
						|
        if (!str) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (cbor_string_length(pair[i].key) == 10 &&
 | 
						|
            memcmp(str, "public_key", 10) == 0) {
 | 
						|
            if (!fill_attestation_property(&(nsm_req->public_key),
 | 
						|
                                           pair[i].value)) {
 | 
						|
                goto cleanup;
 | 
						|
            }
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (cbor_string_length(pair[i].key) == 9 &&
 | 
						|
            memcmp(str, "user_data", 9) == 0) {
 | 
						|
            if (!fill_attestation_property(&(nsm_req->user_data),
 | 
						|
                                           pair[i].value)) {
 | 
						|
                goto cleanup;
 | 
						|
            }
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (cbor_string_length(pair[i].key) == 5 &&
 | 
						|
            memcmp(str, "nonce", 5) == 0) {
 | 
						|
            if (!fill_attestation_property(&(nsm_req->nonce), pair[i].value)) {
 | 
						|
                goto cleanup;
 | 
						|
            }
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    r = NSM_SUCCESS;
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (item) {
 | 
						|
        cbor_decref(&item);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
static bool add_protected_header_to_cose(cbor_item_t *cose)
 | 
						|
{
 | 
						|
    cbor_item_t *map = NULL;
 | 
						|
    cbor_item_t *key = NULL;
 | 
						|
    cbor_item_t *value = NULL;
 | 
						|
    cbor_item_t *bs = NULL;
 | 
						|
    size_t len;
 | 
						|
    bool r = false;
 | 
						|
    size_t buf_len = 4096;
 | 
						|
    g_autofree uint8_t *buf = g_malloc(buf_len);
 | 
						|
 | 
						|
    map = cbor_new_definite_map(1);
 | 
						|
    if (!map) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    key = cbor_build_uint8(1);
 | 
						|
    if (!key) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    value = cbor_new_int8();
 | 
						|
    if (!value) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    cbor_mark_negint(value);
 | 
						|
    /* we don't actually sign the data, so we use -1 as the 'alg' value */
 | 
						|
    cbor_set_uint8(value, 0);
 | 
						|
 | 
						|
    if (!qemu_cbor_map_add(map, key, value)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(map, buf, buf_len);
 | 
						|
    if (len == 0) {
 | 
						|
        goto cleanup_map;
 | 
						|
    }
 | 
						|
 | 
						|
    bs = cbor_build_bytestring(buf, len);
 | 
						|
    if (!bs) {
 | 
						|
        goto cleanup_map;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_array_push(cose, bs)) {
 | 
						|
        cbor_decref(&bs);
 | 
						|
        goto cleanup_map;
 | 
						|
    }
 | 
						|
    r = true;
 | 
						|
    goto cleanup_map;
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (key) {
 | 
						|
        cbor_decref(&key);
 | 
						|
    }
 | 
						|
    if (value) {
 | 
						|
        cbor_decref(&value);
 | 
						|
    }
 | 
						|
 | 
						|
 cleanup_map:
 | 
						|
    if (map) {
 | 
						|
        cbor_decref(&map);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
static bool add_unprotected_header_to_cose(cbor_item_t *cose)
 | 
						|
{
 | 
						|
    cbor_item_t *map = cbor_new_definite_map(0);
 | 
						|
    if (!map) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_array_push(cose, map)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (map) {
 | 
						|
        cbor_decref(&map);
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool add_ca_bundle_to_payload(cbor_item_t *map)
 | 
						|
{
 | 
						|
    cbor_item_t *key_cbor = NULL;
 | 
						|
    cbor_item_t *value_cbor = NULL;
 | 
						|
    cbor_item_t *bs = NULL;
 | 
						|
    uint8_t zero[64] = {0};
 | 
						|
 | 
						|
    key_cbor = cbor_build_string("cabundle");
 | 
						|
    if (!key_cbor) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    value_cbor = cbor_new_definite_array(1);
 | 
						|
    if (!value_cbor) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    bs = cbor_build_bytestring(zero, 64);
 | 
						|
    if (!bs) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_array_push(value_cbor, bs)) {
 | 
						|
        cbor_decref(&bs);
 | 
						|
        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;
 | 
						|
}
 | 
						|
 | 
						|
static bool add_payload_to_cose(cbor_item_t *cose, VirtIONSM *vnsm,
 | 
						|
                                NSMAttestationReq *req)
 | 
						|
{
 | 
						|
    cbor_item_t *root = NULL;
 | 
						|
    cbor_item_t *nested_map;
 | 
						|
    cbor_item_t *bs = NULL;
 | 
						|
    size_t locked_cnt;
 | 
						|
    uint8_t ind[NSM_MAX_PCRS];
 | 
						|
    size_t payload_map_size = 9;
 | 
						|
    size_t len;
 | 
						|
    struct PCRInfo *pcr;
 | 
						|
    uint8_t zero[64] = {0};
 | 
						|
    bool r = false;
 | 
						|
    size_t buf_len = 16384;
 | 
						|
    g_autofree uint8_t *buf = g_malloc(buf_len);
 | 
						|
 | 
						|
    root = cbor_new_definite_map(payload_map_size);
 | 
						|
    if (!root) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_string_to_map(root, "module_id", vnsm->module_id)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_string_to_map(root, "digest", vnsm->digest)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_uint64_to_map(root, "timestamp",
 | 
						|
                                     (uint64_t) time(NULL) * 1000)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    locked_cnt = 0;
 | 
						|
    for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
 | 
						|
        if (vnsm->pcrs[i].locked) {
 | 
						|
            ind[locked_cnt++] = i;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_map_to_map(root, "pcrs", locked_cnt, &nested_map)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    for (uint8_t i = 0; i < locked_cnt; ++i) {
 | 
						|
        pcr = &(vnsm->pcrs[ind[i]]);
 | 
						|
        if (!qemu_cbor_add_uint8_key_bytestring_to_map(
 | 
						|
                nested_map, ind[i],
 | 
						|
                pcr->data,
 | 
						|
                QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
 | 
						|
            goto cleanup;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_bytestring_to_map(root, "certificate", zero, 64)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (!add_ca_bundle_to_payload(root)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    if (req->public_key.is_null) {
 | 
						|
        if (!qemu_cbor_add_null_to_map(root, "public_key")) {
 | 
						|
            goto cleanup;
 | 
						|
        }
 | 
						|
    } else if (!qemu_cbor_add_bytestring_to_map(root, "public_key",
 | 
						|
                                                req->public_key.buf,
 | 
						|
                                                req->public_key.len)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    if (req->user_data.is_null) {
 | 
						|
        if (!qemu_cbor_add_null_to_map(root, "user_data")) {
 | 
						|
            goto cleanup;
 | 
						|
        }
 | 
						|
    } else if (!qemu_cbor_add_bytestring_to_map(root, "user_data",
 | 
						|
                                                req->user_data.buf,
 | 
						|
                                                req->user_data.len)) {
 | 
						|
            goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    if (req->nonce.is_null) {
 | 
						|
        if (!qemu_cbor_add_null_to_map(root, "nonce")) {
 | 
						|
            goto cleanup;
 | 
						|
        }
 | 
						|
    } else if (!qemu_cbor_add_bytestring_to_map(root, "nonce",
 | 
						|
                                                req->nonce.buf,
 | 
						|
                                                req->nonce.len)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, buf, buf_len);
 | 
						|
    if (len == 0) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    bs = cbor_build_bytestring(buf, len);
 | 
						|
    if (!bs) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_array_push(cose, bs)) {
 | 
						|
        cbor_decref(&bs);
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    r = true;
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
static bool add_signature_to_cose(cbor_item_t *cose)
 | 
						|
{
 | 
						|
    cbor_item_t *bs = NULL;
 | 
						|
    uint8_t zero[64] = {0};
 | 
						|
 | 
						|
    /* we don't actually sign the data, so we just put 64 zero bytes */
 | 
						|
    bs = cbor_build_bytestring(zero, 64);
 | 
						|
    if (!bs) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!qemu_cbor_array_push(cose, bs)) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (bs) {
 | 
						|
        cbor_decref(&bs);
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Attestation response structure:
 | 
						|
 *
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = String("Attestation"),
 | 
						|
 *     value = Map(1) {
 | 
						|
 *       key = String("document"),
 | 
						|
 *       value = Byte_String()
 | 
						|
 *     }
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 *
 | 
						|
 * The document is a serialized COSE sign1 blob of the structure:
 | 
						|
 * {
 | 
						|
 *   Array(4) {
 | 
						|
 *     [0] { ByteString() }, // serialized protected header
 | 
						|
 *     [1] { Map(0) },       // 0 length map
 | 
						|
 *     [2] { ByteString() }, // serialized payload
 | 
						|
 *     [3] { ByteString() }, // signature
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 *
 | 
						|
 * where [0] protected header is a serialized CBOR blob of the structure:
 | 
						|
 * {
 | 
						|
 *   Map(1) {
 | 
						|
 *     key = Uint8(1)         // alg
 | 
						|
 *     value = NegativeInt8() // Signing algorithm
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 *
 | 
						|
 * [2] payload is serialized CBOR blob of the structure:
 | 
						|
 * {
 | 
						|
 *   Map(9) {
 | 
						|
 *     [0] { key = String("module_id"), value = String(module_id) },
 | 
						|
 *     [1] { key = String("digest"), value = String("SHA384") },
 | 
						|
 *     [2] {
 | 
						|
 *           key = String("timestamp"),
 | 
						|
 *           value = Uint64(unix epoch of  when document was created)
 | 
						|
 *         },
 | 
						|
 *     [3] {
 | 
						|
 *           key = String("pcrs"),
 | 
						|
 *           value = Map(locked_pcr_cnt) {
 | 
						|
 *                       key = Uint8(pcr_index),
 | 
						|
 *                       value = ByteString(pcr_data)
 | 
						|
 *                   },
 | 
						|
 *         },
 | 
						|
 *     [4] {
 | 
						|
 *           key = String("certificate"),
 | 
						|
 *           value = ByteString(Signing certificate)
 | 
						|
 *         },
 | 
						|
 *     [5] { key = String("cabundle"), value = Array(N) { ByteString()... } },
 | 
						|
 *     [6] { key = String("public_key"), value = ByteString() || null },
 | 
						|
 *     [7] { key = String("user_data"), value = ByteString() || null},
 | 
						|
 *     [8] { key = String("nonce"), value = ByteString() || null},
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 */
 | 
						|
static bool handle_attestation(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                               struct iovec *response, Error **errp)
 | 
						|
{
 | 
						|
    cbor_item_t *root = NULL;
 | 
						|
    cbor_item_t *cose = NULL;
 | 
						|
    cbor_item_t *nested_map;
 | 
						|
    size_t len;
 | 
						|
    enum NSMResponseTypes type;
 | 
						|
    bool r = false;
 | 
						|
    size_t buf_len = 16384;
 | 
						|
    g_autofree uint8_t *buf = g_malloc(buf_len);
 | 
						|
    g_autofree NSMAttestationReq *nsm_req = g_malloc(sizeof(NSMAttestationReq));
 | 
						|
 | 
						|
    nsm_req->public_key.is_null = true;
 | 
						|
    nsm_req->user_data.is_null = true;
 | 
						|
    nsm_req->nonce.is_null = true;
 | 
						|
 | 
						|
    type = get_nsm_attestation_req(request->iov_base, request->iov_len,
 | 
						|
                                   nsm_req);
 | 
						|
    if (type != NSM_SUCCESS) {
 | 
						|
        if (error_response(response, type, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    cose = cbor_new_definite_array(4);
 | 
						|
    if (!cose) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    if (!add_protected_header_to_cose(cose)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    if (!add_unprotected_header_to_cose(cose)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    if (!add_payload_to_cose(cose, vnsm, nsm_req)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    if (!add_signature_to_cose(cose)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(cose, buf, buf_len);
 | 
						|
    if (len == 0) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    root = cbor_new_definite_map(1);
 | 
						|
    if (!root) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_map_to_map(root, "Attestation", 1, &nested_map)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    if (!qemu_cbor_add_bytestring_to_map(nested_map, "document", buf, len)) {
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    len = cbor_serialize(root, response->iov_base, response->iov_len);
 | 
						|
    if (len == 0) {
 | 
						|
        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
 | 
						|
            r = true;
 | 
						|
        }
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    response->iov_len = len;
 | 
						|
    r = true;
 | 
						|
 | 
						|
 out:
 | 
						|
    if (root) {
 | 
						|
        cbor_decref(&root);
 | 
						|
    }
 | 
						|
    if (cose) {
 | 
						|
        cbor_decref(&cose);
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
 | 
						|
 err:
 | 
						|
    error_setg(errp, "Failed to initialize Attestation response");
 | 
						|
    goto out;
 | 
						|
}
 | 
						|
 | 
						|
enum CBOR_ROOT_TYPE {
 | 
						|
    CBOR_ROOT_TYPE_STRING = 0,
 | 
						|
    CBOR_ROOT_TYPE_MAP = 1,
 | 
						|
};
 | 
						|
 | 
						|
struct nsm_cmd {
 | 
						|
    char name[16];
 | 
						|
    /*
 | 
						|
     * There are 2 types of request
 | 
						|
     * 1) String(); "GetRandom", "DescribeNSM"
 | 
						|
     * 2) Map(1) { key: String(), value: ... }
 | 
						|
     */
 | 
						|
    enum CBOR_ROOT_TYPE root_type;
 | 
						|
    bool (*response_fn)(VirtIONSM *vnsm, struct iovec *request,
 | 
						|
                        struct iovec *response, Error **errp);
 | 
						|
};
 | 
						|
 | 
						|
const struct nsm_cmd nsm_cmds[] = {
 | 
						|
    { "GetRandom",   CBOR_ROOT_TYPE_STRING,  handle_get_random },
 | 
						|
    { "DescribeNSM", CBOR_ROOT_TYPE_STRING,  handle_describe_nsm },
 | 
						|
    { "DescribePCR", CBOR_ROOT_TYPE_MAP,     handle_describe_pcr },
 | 
						|
    { "ExtendPCR",   CBOR_ROOT_TYPE_MAP,     handle_extend_pcr },
 | 
						|
    { "LockPCR",     CBOR_ROOT_TYPE_MAP,     handle_lock_pcr },
 | 
						|
    { "LockPCRs",    CBOR_ROOT_TYPE_MAP,     handle_lock_pcrs },
 | 
						|
    { "Attestation", CBOR_ROOT_TYPE_MAP,     handle_attestation },
 | 
						|
};
 | 
						|
 | 
						|
static const struct nsm_cmd *get_nsm_request_cmd(uint8_t *buf, size_t len)
 | 
						|
{
 | 
						|
    size_t size;
 | 
						|
    uint8_t *req;
 | 
						|
    enum CBOR_ROOT_TYPE root_type;
 | 
						|
    struct cbor_load_result result;
 | 
						|
    cbor_item_t *item = cbor_load(buf, len, &result);
 | 
						|
    if (!item || result.error.code != CBOR_ERR_NONE) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    if (cbor_isa_string(item)) {
 | 
						|
        size = cbor_string_length(item);
 | 
						|
        req = cbor_string_handle(item);
 | 
						|
        root_type = CBOR_ROOT_TYPE_STRING;
 | 
						|
    } else if (cbor_isa_map(item) && cbor_map_size(item) == 1) {
 | 
						|
        struct cbor_pair *handle = cbor_map_handle(item);
 | 
						|
        if (cbor_isa_string(handle->key)) {
 | 
						|
            size = cbor_string_length(handle->key);
 | 
						|
            req = cbor_string_handle(handle->key);
 | 
						|
            root_type = CBOR_ROOT_TYPE_MAP;
 | 
						|
        } else {
 | 
						|
            goto cleanup;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    if  (size == 0 || req == NULL) {
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    for (int i = 0; i < ARRAY_SIZE(nsm_cmds); ++i) {
 | 
						|
        if (nsm_cmds[i].root_type == root_type &&
 | 
						|
            strlen(nsm_cmds[i].name) == size &&
 | 
						|
            memcmp(nsm_cmds[i].name, req, size) == 0) {
 | 
						|
            cbor_decref(&item);
 | 
						|
            return &nsm_cmds[i];
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    if (item) {
 | 
						|
        cbor_decref(&item);
 | 
						|
    }
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static bool get_nsm_request_response(VirtIONSM *vnsm, struct iovec *req,
 | 
						|
                                     struct iovec *resp, Error **errp)
 | 
						|
{
 | 
						|
    const struct nsm_cmd *cmd;
 | 
						|
 | 
						|
    if (req->iov_len > NSM_REQUEST_MAX_SIZE) {
 | 
						|
        if (error_response(resp, NSM_INPUT_TOO_LARGE, errp)) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        error_setg(errp, "Failed to initialize InputTooLarge response");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    cmd = get_nsm_request_cmd(req->iov_base, req->iov_len);
 | 
						|
 | 
						|
    if (cmd == NULL) {
 | 
						|
        if (error_response(resp, NSM_INVALID_OPERATION, errp)) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        error_setg(errp, "Failed to initialize InvalidOperation response");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return cmd->response_fn(vnsm, req, resp, errp);
 | 
						|
}
 | 
						|
 | 
						|
static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
 | 
						|
{
 | 
						|
    g_autofree VirtQueueElement *out_elem = NULL;
 | 
						|
    g_autofree VirtQueueElement *in_elem = NULL;
 | 
						|
    VirtIONSM *vnsm = VIRTIO_NSM(vdev);
 | 
						|
    Error *err = NULL;
 | 
						|
    size_t sz;
 | 
						|
    struct iovec req = {.iov_base = NULL, .iov_len = 0};
 | 
						|
    struct iovec res = {.iov_base = NULL, .iov_len = 0};
 | 
						|
 | 
						|
    out_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
 | 
						|
    if (!out_elem) {
 | 
						|
        /* nothing in virtqueue */
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    sz = iov_size(out_elem->out_sg, out_elem->out_num);
 | 
						|
    if (sz == 0) {
 | 
						|
        virtio_error(vdev, "Expected non-zero sized request buffer in "
 | 
						|
                     "virtqueue");
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    in_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
 | 
						|
    if (!in_elem) {
 | 
						|
        virtio_error(vdev, "Expected response buffer after request buffer "
 | 
						|
                     "in virtqueue");
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
    if (iov_size(in_elem->in_sg, in_elem->in_num) != NSM_RESPONSE_BUF_SIZE) {
 | 
						|
        virtio_error(vdev, "Expected response buffer of length 0x3000");
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    req.iov_base = g_malloc(sz);
 | 
						|
    req.iov_len = iov_to_buf(out_elem->out_sg, out_elem->out_num, 0,
 | 
						|
                             req.iov_base, sz);
 | 
						|
    if (req.iov_len != sz) {
 | 
						|
        virtio_error(vdev, "Failed to copy request buffer");
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    res.iov_base = g_malloc(NSM_RESPONSE_BUF_SIZE);
 | 
						|
    res.iov_len = NSM_RESPONSE_BUF_SIZE;
 | 
						|
 | 
						|
    if (!get_nsm_request_response(vnsm, &req, &res, &err)) {
 | 
						|
        error_report_err(err);
 | 
						|
        virtio_error(vdev, "Failed to get NSM request response");
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    sz = iov_from_buf(in_elem->in_sg, in_elem->in_num, 0, res.iov_base,
 | 
						|
                      res.iov_len);
 | 
						|
    if (sz != res.iov_len) {
 | 
						|
        virtio_error(vdev, "Failed to copy response buffer");
 | 
						|
        goto cleanup;
 | 
						|
    }
 | 
						|
 | 
						|
    g_free(req.iov_base);
 | 
						|
    g_free(res.iov_base);
 | 
						|
    virtqueue_push(vq, out_elem, 0);
 | 
						|
    virtqueue_push(vq, in_elem, in_elem->in_sg->iov_len);
 | 
						|
    virtio_notify(vdev, vq);
 | 
						|
    return;
 | 
						|
 | 
						|
 cleanup:
 | 
						|
    g_free(req.iov_base);
 | 
						|
    g_free(res.iov_base);
 | 
						|
    if (out_elem) {
 | 
						|
        virtqueue_detach_element(vq, out_elem, 0);
 | 
						|
    }
 | 
						|
    if (in_elem) {
 | 
						|
        virtqueue_detach_element(vq, in_elem, 0);
 | 
						|
    }
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
 | 
						|
{
 | 
						|
    return f;
 | 
						|
}
 | 
						|
 | 
						|
static bool extend_pcr(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len)
 | 
						|
{
 | 
						|
    Error *err = NULL;
 | 
						|
    struct PCRInfo *pcr = &(vnsm->pcrs[ind]);
 | 
						|
    size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
 | 
						|
    uint8_t result[QCRYPTO_HASH_DIGEST_LEN_SHA384];
 | 
						|
    uint8_t *ptr = result;
 | 
						|
    struct iovec iov[2] = {
 | 
						|
        { .iov_base = pcr->data, .iov_len = QCRYPTO_HASH_DIGEST_LEN_SHA384 },
 | 
						|
        { .iov_base = data, .iov_len = len },
 | 
						|
    };
 | 
						|
 | 
						|
    if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iov, 2, &ptr, &digest_len,
 | 
						|
                            &err) < 0) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    memcpy(pcr->data, result, QCRYPTO_HASH_DIGEST_LEN_SHA384);
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static void lock_pcr(VirtIONSM *vnsm, int ind)
 | 
						|
{
 | 
						|
    vnsm->pcrs[ind].locked = true;
 | 
						|
}
 | 
						|
 | 
						|
static void virtio_nsm_device_realize(DeviceState *dev, Error **errp)
 | 
						|
{
 | 
						|
    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 | 
						|
    VirtIONSM *vnsm = VIRTIO_NSM(dev);
 | 
						|
 | 
						|
    vnsm->max_pcrs = NSM_MAX_PCRS;
 | 
						|
    vnsm->digest = (char *) "SHA384";
 | 
						|
    if (vnsm->module_id == NULL) {
 | 
						|
        vnsm->module_id = (char *) "i-234-enc5678";
 | 
						|
    }
 | 
						|
    vnsm->version_major = 1;
 | 
						|
    vnsm->version_minor = 0;
 | 
						|
    vnsm->version_patch = 0;
 | 
						|
    vnsm->extend_pcr = extend_pcr;
 | 
						|
    vnsm->lock_pcr = lock_pcr;
 | 
						|
 | 
						|
    virtio_init(vdev, VIRTIO_ID_NITRO_SEC_MOD, 0);
 | 
						|
 | 
						|
    vnsm->vq = virtio_add_queue(vdev, 2, handle_input);
 | 
						|
}
 | 
						|
 | 
						|
static void virtio_nsm_device_unrealize(DeviceState *dev)
 | 
						|
{
 | 
						|
    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 | 
						|
 | 
						|
    virtio_del_queue(vdev, 0);
 | 
						|
    virtio_cleanup(vdev);
 | 
						|
}
 | 
						|
 | 
						|
static const VMStateDescription vmstate_pcr_info_entry = {
 | 
						|
    .name = "pcr_info_entry",
 | 
						|
    .minimum_version_id = 1,
 | 
						|
    .version_id = 1,
 | 
						|
    .fields = (const VMStateField[]) {
 | 
						|
        VMSTATE_BOOL(locked, struct PCRInfo),
 | 
						|
        VMSTATE_UINT8_ARRAY(data, struct PCRInfo,
 | 
						|
                            QCRYPTO_HASH_DIGEST_LEN_SHA384),
 | 
						|
        VMSTATE_END_OF_LIST()
 | 
						|
    },
 | 
						|
};
 | 
						|
 | 
						|
static const VMStateDescription vmstate_virtio_nsm_device = {
 | 
						|
    .name = "virtio-nsm-device",
 | 
						|
    .minimum_version_id = 1,
 | 
						|
    .version_id = 1,
 | 
						|
    .fields = (const VMStateField[]) {
 | 
						|
        VMSTATE_STRUCT_ARRAY(pcrs, VirtIONSM, NSM_MAX_PCRS, 1,
 | 
						|
                             vmstate_pcr_info_entry, struct PCRInfo),
 | 
						|
        VMSTATE_END_OF_LIST()
 | 
						|
    },
 | 
						|
};
 | 
						|
 | 
						|
static const VMStateDescription vmstate_virtio_nsm = {
 | 
						|
    .name = "virtio-nsm",
 | 
						|
    .minimum_version_id = 1,
 | 
						|
    .version_id = 1,
 | 
						|
    .fields = (const VMStateField[]) {
 | 
						|
        VMSTATE_VIRTIO_DEVICE,
 | 
						|
        VMSTATE_END_OF_LIST()
 | 
						|
    },
 | 
						|
};
 | 
						|
 | 
						|
static Property virtio_nsm_properties[] = {
 | 
						|
    DEFINE_PROP_STRING("module-id", VirtIONSM, module_id),
 | 
						|
    DEFINE_PROP_END_OF_LIST(),
 | 
						|
};
 | 
						|
 | 
						|
static void virtio_nsm_class_init(ObjectClass *klass, void *data)
 | 
						|
{
 | 
						|
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
						|
    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 | 
						|
 | 
						|
    device_class_set_props(dc, virtio_nsm_properties);
 | 
						|
    dc->vmsd = &vmstate_virtio_nsm;
 | 
						|
    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 | 
						|
    vdc->realize = virtio_nsm_device_realize;
 | 
						|
    vdc->unrealize = virtio_nsm_device_unrealize;
 | 
						|
    vdc->get_features = get_features;
 | 
						|
    vdc->vmsd = &vmstate_virtio_nsm_device;
 | 
						|
}
 | 
						|
 | 
						|
static const TypeInfo virtio_nsm_info = {
 | 
						|
    .name = TYPE_VIRTIO_NSM,
 | 
						|
    .parent = TYPE_VIRTIO_DEVICE,
 | 
						|
    .instance_size = sizeof(VirtIONSM),
 | 
						|
    .class_init = virtio_nsm_class_init,
 | 
						|
};
 | 
						|
 | 
						|
static void virtio_register_types(void)
 | 
						|
{
 | 
						|
    type_register_static(&virtio_nsm_info);
 | 
						|
}
 | 
						|
 | 
						|
type_init(virtio_register_types)
 |