|  | /* | 
|  | * Virtio crypto Support | 
|  | * | 
|  | * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. | 
|  | * | 
|  | * Authors: | 
|  | *    Gonglei <arei.gonglei@huawei.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/main-loop.h" | 
|  | #include "qemu/module.h" | 
|  | #include "qapi/error.h" | 
|  | #include "qemu/error-report.h" | 
|  |  | 
|  | #include "hw/virtio/virtio.h" | 
|  | #include "hw/virtio/virtio-crypto.h" | 
|  | #include "hw/qdev-properties.h" | 
|  | #include "standard-headers/linux/virtio_ids.h" | 
|  | #include "sysemu/cryptodev-vhost.h" | 
|  |  | 
|  | #define VIRTIO_CRYPTO_VM_VERSION 1 | 
|  |  | 
|  | typedef struct VirtIOCryptoSessionReq { | 
|  | VirtIODevice *vdev; | 
|  | VirtQueue *vq; | 
|  | VirtQueueElement *elem; | 
|  | CryptoDevBackendSessionInfo info; | 
|  | CryptoDevCompletionFunc cb; | 
|  | } VirtIOCryptoSessionReq; | 
|  |  | 
|  | static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq) | 
|  | { | 
|  | switch (sreq->info.op_code) { | 
|  | case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: | 
|  | g_free(sreq->info.u.sym_sess_info.cipher_key); | 
|  | g_free(sreq->info.u.sym_sess_info.auth_key); | 
|  | break; | 
|  |  | 
|  | case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: | 
|  | g_free(sreq->info.u.asym_sess_info.key); | 
|  | break; | 
|  |  | 
|  | case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_HASH_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_MAC_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION: | 
|  | break; | 
|  |  | 
|  | default: | 
|  | error_report("Unknown opcode: %u", sreq->info.op_code); | 
|  | } | 
|  | g_free(sreq); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Transfer virtqueue index to crypto queue index. | 
|  | * The control virtqueue is after the data virtqueues | 
|  | * so the input value doesn't need to be adjusted | 
|  | */ | 
|  | static inline int virtio_crypto_vq2q(int queue_index) | 
|  | { | 
|  | return queue_index; | 
|  | } | 
|  |  | 
|  | static int | 
|  | virtio_crypto_cipher_session_helper(VirtIODevice *vdev, | 
|  | CryptoDevBackendSymSessionInfo *info, | 
|  | struct virtio_crypto_cipher_session_para *cipher_para, | 
|  | struct iovec **iov, unsigned int *out_num) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | unsigned int num = *out_num; | 
|  |  | 
|  | info->cipher_alg = ldl_le_p(&cipher_para->algo); | 
|  | info->key_len = ldl_le_p(&cipher_para->keylen); | 
|  | info->direction = ldl_le_p(&cipher_para->op); | 
|  | DPRINTF("cipher_alg=%" PRIu32 ", info->direction=%" PRIu32 "\n", | 
|  | info->cipher_alg, info->direction); | 
|  |  | 
|  | if (info->key_len > vcrypto->conf.max_cipher_key_len) { | 
|  | error_report("virtio-crypto length of cipher key is too big: %u", | 
|  | info->key_len); | 
|  | return -VIRTIO_CRYPTO_ERR; | 
|  | } | 
|  | /* Get cipher key */ | 
|  | if (info->key_len > 0) { | 
|  | size_t s; | 
|  | DPRINTF("keylen=%" PRIu32 "\n", info->key_len); | 
|  |  | 
|  | info->cipher_key = g_malloc(info->key_len); | 
|  | s = iov_to_buf(*iov, num, 0, info->cipher_key, info->key_len); | 
|  | if (unlikely(s != info->key_len)) { | 
|  | virtio_error(vdev, "virtio-crypto cipher key incorrect"); | 
|  | return -EFAULT; | 
|  | } | 
|  | iov_discard_front(iov, &num, info->key_len); | 
|  | *out_num = num; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, | 
|  | struct virtio_crypto_sym_create_session_req *sess_req, | 
|  | uint32_t queue_id, | 
|  | uint32_t opcode, | 
|  | struct iovec *iov, unsigned int out_num, | 
|  | VirtIOCryptoSessionReq *sreq) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); | 
|  | CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info; | 
|  | int queue_index; | 
|  | uint32_t op_type; | 
|  | int ret; | 
|  |  | 
|  | op_type = ldl_le_p(&sess_req->op_type); | 
|  | sreq->info.op_code = opcode; | 
|  |  | 
|  | sym_info = &sreq->info.u.sym_sess_info; | 
|  | sym_info->op_type = op_type; | 
|  |  | 
|  | if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { | 
|  | ret = virtio_crypto_cipher_session_helper(vdev, sym_info, | 
|  | &sess_req->u.cipher.para, | 
|  | &iov, &out_num); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { | 
|  | size_t s; | 
|  | /* cipher part */ | 
|  | ret = virtio_crypto_cipher_session_helper(vdev, sym_info, | 
|  | &sess_req->u.chain.para.cipher_param, | 
|  | &iov, &out_num); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | /* hash part */ | 
|  | sym_info->alg_chain_order = ldl_le_p( | 
|  | &sess_req->u.chain.para.alg_chain_order); | 
|  | sym_info->add_len = ldl_le_p(&sess_req->u.chain.para.aad_len); | 
|  | sym_info->hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode); | 
|  | if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) { | 
|  | sym_info->hash_alg = | 
|  | ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo); | 
|  | sym_info->auth_key_len = ldl_le_p( | 
|  | &sess_req->u.chain.para.u.mac_param.auth_key_len); | 
|  | sym_info->hash_result_len = ldl_le_p( | 
|  | &sess_req->u.chain.para.u.mac_param.hash_result_len); | 
|  | if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) { | 
|  | error_report("virtio-crypto length of auth key is too big: %u", | 
|  | sym_info->auth_key_len); | 
|  | return -VIRTIO_CRYPTO_ERR; | 
|  | } | 
|  | /* get auth key */ | 
|  | if (sym_info->auth_key_len > 0) { | 
|  | sym_info->auth_key = g_malloc(sym_info->auth_key_len); | 
|  | s = iov_to_buf(iov, out_num, 0, sym_info->auth_key, | 
|  | sym_info->auth_key_len); | 
|  | if (unlikely(s != sym_info->auth_key_len)) { | 
|  | virtio_error(vdev, | 
|  | "virtio-crypto authenticated key incorrect"); | 
|  | return -EFAULT; | 
|  | } | 
|  | iov_discard_front(&iov, &out_num, sym_info->auth_key_len); | 
|  | } | 
|  | } else if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) { | 
|  | sym_info->hash_alg = ldl_le_p( | 
|  | &sess_req->u.chain.para.u.hash_param.algo); | 
|  | sym_info->hash_result_len = ldl_le_p( | 
|  | &sess_req->u.chain.para.u.hash_param.hash_result_len); | 
|  | } else { | 
|  | /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */ | 
|  | error_report("unsupported hash mode"); | 
|  | return -VIRTIO_CRYPTO_NOTSUPP; | 
|  | } | 
|  | } else { | 
|  | /* VIRTIO_CRYPTO_SYM_OP_NONE */ | 
|  | error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE"); | 
|  | return -VIRTIO_CRYPTO_NOTSUPP; | 
|  | } | 
|  |  | 
|  | queue_index = virtio_crypto_vq2q(queue_id); | 
|  | return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info, | 
|  | queue_index, sreq->cb, sreq); | 
|  | } | 
|  |  | 
|  | static int | 
|  | virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto, | 
|  | struct virtio_crypto_akcipher_create_session_req *sess_req, | 
|  | uint32_t queue_id, uint32_t opcode, | 
|  | struct iovec *iov, unsigned int out_num, | 
|  | VirtIOCryptoSessionReq *sreq) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); | 
|  | CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info; | 
|  | int queue_index; | 
|  | uint32_t algo, keytype, keylen; | 
|  |  | 
|  | sreq->info.op_code = opcode; | 
|  | algo = ldl_le_p(&sess_req->para.algo); | 
|  | keytype = ldl_le_p(&sess_req->para.keytype); | 
|  | keylen = ldl_le_p(&sess_req->para.keylen); | 
|  |  | 
|  | if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC) | 
|  | && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) { | 
|  | error_report("unsupported asym keytype: %d", keytype); | 
|  | return -VIRTIO_CRYPTO_NOTSUPP; | 
|  | } | 
|  |  | 
|  | if (keylen) { | 
|  | asym_info->key = g_malloc(keylen); | 
|  | if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) { | 
|  | virtio_error(vdev, "virtio-crypto asym key incorrect"); | 
|  | return -EFAULT; | 
|  | } | 
|  | iov_discard_front(&iov, &out_num, keylen); | 
|  | } | 
|  |  | 
|  | asym_info = &sreq->info.u.asym_sess_info; | 
|  | asym_info->algo = algo; | 
|  | asym_info->keytype = keytype; | 
|  | asym_info->keylen = keylen; | 
|  | switch (asym_info->algo) { | 
|  | case VIRTIO_CRYPTO_AKCIPHER_RSA: | 
|  | asym_info->u.rsa.padding_algo = | 
|  | ldl_le_p(&sess_req->para.u.rsa.padding_algo); | 
|  | asym_info->u.rsa.hash_algo = | 
|  | ldl_le_p(&sess_req->para.u.rsa.hash_algo); | 
|  | break; | 
|  |  | 
|  | /* TODO DSA&ECDSA handling */ | 
|  |  | 
|  | default: | 
|  | return -VIRTIO_CRYPTO_ERR; | 
|  | } | 
|  |  | 
|  | queue_index = virtio_crypto_vq2q(queue_id); | 
|  | return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info, | 
|  | queue_index, sreq->cb, sreq); | 
|  | } | 
|  |  | 
|  | static int | 
|  | virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto, | 
|  | struct virtio_crypto_destroy_session_req *close_sess_req, | 
|  | uint32_t queue_id, | 
|  | VirtIOCryptoSessionReq *sreq) | 
|  | { | 
|  | uint64_t session_id; | 
|  |  | 
|  | session_id = ldq_le_p(&close_sess_req->session_id); | 
|  | DPRINTF("close session, id=%" PRIu64 "\n", session_id); | 
|  |  | 
|  | return cryptodev_backend_close_session( | 
|  | vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq); | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_create_session_completion(void *opaque, int ret) | 
|  | { | 
|  | VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque; | 
|  | VirtQueue *vq = sreq->vq; | 
|  | VirtQueueElement *elem = sreq->elem; | 
|  | VirtIODevice *vdev = sreq->vdev; | 
|  | struct virtio_crypto_session_input input; | 
|  | struct iovec *in_iov = elem->in_sg; | 
|  | unsigned in_num = elem->in_num; | 
|  | size_t s; | 
|  |  | 
|  | memset(&input, 0, sizeof(input)); | 
|  | /* Serious errors, need to reset virtio crypto device */ | 
|  | if (ret == -EFAULT) { | 
|  | virtqueue_detach_element(vq, elem, 0); | 
|  | goto out; | 
|  | } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) { | 
|  | stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); | 
|  | } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) { | 
|  | stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED); | 
|  | } else if (ret != VIRTIO_CRYPTO_OK) { | 
|  | stl_le_p(&input.status, VIRTIO_CRYPTO_ERR); | 
|  | } else { | 
|  | /* Set the session id */ | 
|  | stq_le_p(&input.session_id, sreq->info.session_id); | 
|  | stl_le_p(&input.status, VIRTIO_CRYPTO_OK); | 
|  | } | 
|  |  | 
|  | s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); | 
|  | if (unlikely(s != sizeof(input))) { | 
|  | virtio_error(vdev, "virtio-crypto input incorrect"); | 
|  | virtqueue_detach_element(vq, elem, 0); | 
|  | goto out; | 
|  | } | 
|  | virtqueue_push(vq, elem, sizeof(input)); | 
|  | virtio_notify(vdev, vq); | 
|  |  | 
|  | out: | 
|  | g_free(elem); | 
|  | virtio_crypto_free_create_session_req(sreq); | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_destroy_session_completion(void *opaque, int ret) | 
|  | { | 
|  | VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque; | 
|  | VirtQueue *vq = sreq->vq; | 
|  | VirtQueueElement *elem = sreq->elem; | 
|  | VirtIODevice *vdev = sreq->vdev; | 
|  | struct iovec *in_iov = elem->in_sg; | 
|  | unsigned in_num = elem->in_num; | 
|  | uint8_t status; | 
|  | size_t s; | 
|  |  | 
|  | if (ret < 0) { | 
|  | status = VIRTIO_CRYPTO_ERR; | 
|  | } else { | 
|  | status = VIRTIO_CRYPTO_OK; | 
|  | } | 
|  | s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status)); | 
|  | if (unlikely(s != sizeof(status))) { | 
|  | virtio_error(vdev, "virtio-crypto status incorrect"); | 
|  | virtqueue_detach_element(vq, elem, 0); | 
|  | goto out; | 
|  | } | 
|  | virtqueue_push(vq, elem, sizeof(status)); | 
|  | virtio_notify(vdev, vq); | 
|  |  | 
|  | out: | 
|  | g_free(elem); | 
|  | g_free(sreq); | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | struct virtio_crypto_op_ctrl_req ctrl; | 
|  | VirtQueueElement *elem; | 
|  | VirtIOCryptoSessionReq *sreq; | 
|  | unsigned out_num; | 
|  | unsigned in_num; | 
|  | uint32_t queue_id; | 
|  | uint32_t opcode; | 
|  | struct virtio_crypto_session_input input; | 
|  | size_t s; | 
|  | int ret; | 
|  | struct iovec *out_iov; | 
|  | struct iovec *in_iov; | 
|  |  | 
|  | for (;;) { | 
|  | g_autofree struct iovec *out_iov_copy = NULL; | 
|  |  | 
|  | elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); | 
|  | if (!elem) { | 
|  | break; | 
|  | } | 
|  | if (elem->out_num < 1 || elem->in_num < 1) { | 
|  | virtio_error(vdev, "virtio-crypto ctrl missing headers"); | 
|  | virtqueue_detach_element(vq, elem, 0); | 
|  | g_free(elem); | 
|  | break; | 
|  | } | 
|  |  | 
|  | out_num = elem->out_num; | 
|  | out_iov_copy = g_memdup2(elem->out_sg, sizeof(out_iov[0]) * out_num); | 
|  | out_iov = out_iov_copy; | 
|  |  | 
|  | in_num = elem->in_num; | 
|  | in_iov = elem->in_sg; | 
|  |  | 
|  | if (unlikely(iov_to_buf(out_iov, out_num, 0, &ctrl, sizeof(ctrl)) | 
|  | != sizeof(ctrl))) { | 
|  | virtio_error(vdev, "virtio-crypto request ctrl_hdr too short"); | 
|  | virtqueue_detach_element(vq, elem, 0); | 
|  | g_free(elem); | 
|  | break; | 
|  | } | 
|  | iov_discard_front(&out_iov, &out_num, sizeof(ctrl)); | 
|  |  | 
|  | opcode = ldl_le_p(&ctrl.header.opcode); | 
|  | queue_id = ldl_le_p(&ctrl.header.queue_id); | 
|  |  | 
|  | sreq = g_new0(VirtIOCryptoSessionReq, 1); | 
|  | sreq->vdev = vdev; | 
|  | sreq->vq = vq; | 
|  | sreq->elem = elem; | 
|  |  | 
|  | switch (opcode) { | 
|  | case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: | 
|  | sreq->cb = virtio_crypto_create_session_completion; | 
|  | ret = virtio_crypto_create_sym_session(vcrypto, | 
|  | &ctrl.u.sym_create_session, | 
|  | queue_id, opcode, | 
|  | out_iov, out_num, | 
|  | sreq); | 
|  | if (ret < 0) { | 
|  | virtio_crypto_create_session_completion(sreq, ret); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: | 
|  | sreq->cb = virtio_crypto_create_session_completion; | 
|  | ret = virtio_crypto_create_asym_session(vcrypto, | 
|  | &ctrl.u.akcipher_create_session, | 
|  | queue_id, opcode, | 
|  | out_iov, out_num, | 
|  | sreq); | 
|  | if (ret < 0) { | 
|  | virtio_crypto_create_session_completion(sreq, ret); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_HASH_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_MAC_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION: | 
|  | case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION: | 
|  | sreq->cb = virtio_crypto_destroy_session_completion; | 
|  | ret = virtio_crypto_handle_close_session(vcrypto, | 
|  | &ctrl.u.destroy_session, queue_id, | 
|  | sreq); | 
|  | if (ret < 0) { | 
|  | virtio_crypto_destroy_session_completion(sreq, ret); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case VIRTIO_CRYPTO_HASH_CREATE_SESSION: | 
|  | case VIRTIO_CRYPTO_MAC_CREATE_SESSION: | 
|  | case VIRTIO_CRYPTO_AEAD_CREATE_SESSION: | 
|  | default: | 
|  | memset(&input, 0, sizeof(input)); | 
|  | error_report("virtio-crypto unsupported ctrl opcode: %d", opcode); | 
|  | stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); | 
|  | s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); | 
|  | if (unlikely(s != sizeof(input))) { | 
|  | virtio_error(vdev, "virtio-crypto input incorrect"); | 
|  | virtqueue_detach_element(vq, elem, 0); | 
|  | } else { | 
|  | virtqueue_push(vq, elem, sizeof(input)); | 
|  | virtio_notify(vdev, vq); | 
|  | } | 
|  | g_free(sreq); | 
|  | g_free(elem); | 
|  |  | 
|  | break; | 
|  | } /* end switch case */ | 
|  |  | 
|  | } /* end for loop */ | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_init_request(VirtIOCrypto *vcrypto, VirtQueue *vq, | 
|  | VirtIOCryptoReq *req) | 
|  | { | 
|  | req->vcrypto = vcrypto; | 
|  | req->vq = vq; | 
|  | req->in = NULL; | 
|  | req->in_iov = NULL; | 
|  | req->in_num = 0; | 
|  | req->in_len = 0; | 
|  | req->flags = QCRYPTODEV_BACKEND_ALGO_TYPE__MAX; | 
|  | memset(&req->op_info, 0x00, sizeof(req->op_info)); | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_free_request(VirtIOCryptoReq *req) | 
|  | { | 
|  | if (!req) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_SYM) { | 
|  | size_t max_len; | 
|  | CryptoDevBackendSymOpInfo *op_info = req->op_info.u.sym_op_info; | 
|  |  | 
|  | if (op_info) { | 
|  | max_len = op_info->iv_len + | 
|  | op_info->aad_len + | 
|  | op_info->src_len + | 
|  | op_info->dst_len + | 
|  | op_info->digest_result_len; | 
|  |  | 
|  | /* Zeroize and free request data structure */ | 
|  | memset(op_info, 0, sizeof(*op_info) + max_len); | 
|  | g_free(op_info); | 
|  | } | 
|  | } else if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM) { | 
|  | CryptoDevBackendAsymOpInfo *op_info = req->op_info.u.asym_op_info; | 
|  | if (op_info) { | 
|  | g_free(op_info->src); | 
|  | g_free(op_info->dst); | 
|  | memset(op_info, 0, sizeof(*op_info)); | 
|  | g_free(op_info); | 
|  | } | 
|  | } | 
|  |  | 
|  | g_free(req->in_iov); | 
|  | g_free(req); | 
|  | } | 
|  |  | 
|  | static void | 
|  | virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, | 
|  | VirtIOCryptoReq *req, | 
|  | uint32_t status, | 
|  | CryptoDevBackendSymOpInfo *sym_op_info) | 
|  | { | 
|  | size_t s, len; | 
|  | struct iovec *in_iov = req->in_iov; | 
|  |  | 
|  | if (status != VIRTIO_CRYPTO_OK) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | len = sym_op_info->src_len; | 
|  | /* Save the cipher result */ | 
|  | s = iov_from_buf(in_iov, req->in_num, 0, sym_op_info->dst, len); | 
|  | if (s != len) { | 
|  | virtio_error(vdev, "virtio-crypto dest data incorrect"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | iov_discard_front(&in_iov, &req->in_num, len); | 
|  |  | 
|  | if (sym_op_info->op_type == | 
|  | VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { | 
|  | /* Save the digest result */ | 
|  | s = iov_from_buf(in_iov, req->in_num, 0, | 
|  | sym_op_info->digest_result, | 
|  | sym_op_info->digest_result_len); | 
|  | if (s != sym_op_info->digest_result_len) { | 
|  | virtio_error(vdev, "virtio-crypto digest result incorrect"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev, | 
|  | VirtIOCryptoReq *req, int32_t status, | 
|  | CryptoDevBackendAsymOpInfo *asym_op_info) | 
|  | { | 
|  | size_t s, len; | 
|  | struct iovec *in_iov = req->in_iov; | 
|  |  | 
|  | if (status != VIRTIO_CRYPTO_OK) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | len = asym_op_info->dst_len; | 
|  | if (!len) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | s = iov_from_buf(in_iov, req->in_num, 0, asym_op_info->dst, len); | 
|  | if (s != len) { | 
|  | virtio_error(vdev, "virtio-crypto asym dest data incorrect"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | iov_discard_front(&in_iov, &req->in_num, len); | 
|  |  | 
|  | /* For akcipher, dst_len may be changed after operation */ | 
|  | req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_req_complete(void *opaque, int ret) | 
|  | { | 
|  | VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque; | 
|  | VirtIOCrypto *vcrypto = req->vcrypto; | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); | 
|  | uint8_t status = -ret; | 
|  |  | 
|  | if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_SYM) { | 
|  | virtio_crypto_sym_input_data_helper(vdev, req, status, | 
|  | req->op_info.u.sym_op_info); | 
|  | } else if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM) { | 
|  | virtio_crypto_akcipher_input_data_helper(vdev, req, status, | 
|  | req->op_info.u.asym_op_info); | 
|  | } | 
|  | stb_p(&req->in->status, status); | 
|  | virtqueue_push(req->vq, &req->elem, req->in_len); | 
|  | virtio_notify(vdev, req->vq); | 
|  | virtio_crypto_free_request(req); | 
|  | } | 
|  |  | 
|  | static VirtIOCryptoReq * | 
|  | virtio_crypto_get_request(VirtIOCrypto *s, VirtQueue *vq) | 
|  | { | 
|  | VirtIOCryptoReq *req = virtqueue_pop(vq, sizeof(VirtIOCryptoReq)); | 
|  |  | 
|  | if (req) { | 
|  | virtio_crypto_init_request(s, vq, req); | 
|  | } | 
|  | return req; | 
|  | } | 
|  |  | 
|  | static CryptoDevBackendSymOpInfo * | 
|  | virtio_crypto_sym_op_helper(VirtIODevice *vdev, | 
|  | struct virtio_crypto_cipher_para *cipher_para, | 
|  | struct virtio_crypto_alg_chain_data_para *alg_chain_para, | 
|  | struct iovec *iov, unsigned int out_num) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | CryptoDevBackendSymOpInfo *op_info; | 
|  | uint32_t src_len = 0, dst_len = 0; | 
|  | uint32_t iv_len = 0; | 
|  | uint32_t aad_len = 0, hash_result_len = 0; | 
|  | uint32_t hash_start_src_offset = 0, len_to_hash = 0; | 
|  | uint32_t cipher_start_src_offset = 0, len_to_cipher = 0; | 
|  |  | 
|  | uint64_t max_len, curr_size = 0; | 
|  | size_t s; | 
|  |  | 
|  | /* Plain cipher */ | 
|  | if (cipher_para) { | 
|  | iv_len = ldl_le_p(&cipher_para->iv_len); | 
|  | src_len = ldl_le_p(&cipher_para->src_data_len); | 
|  | dst_len = ldl_le_p(&cipher_para->dst_data_len); | 
|  | } else if (alg_chain_para) { /* Algorithm chain */ | 
|  | iv_len = ldl_le_p(&alg_chain_para->iv_len); | 
|  | src_len = ldl_le_p(&alg_chain_para->src_data_len); | 
|  | dst_len = ldl_le_p(&alg_chain_para->dst_data_len); | 
|  |  | 
|  | aad_len = ldl_le_p(&alg_chain_para->aad_len); | 
|  | hash_result_len = ldl_le_p(&alg_chain_para->hash_result_len); | 
|  | hash_start_src_offset = ldl_le_p( | 
|  | &alg_chain_para->hash_start_src_offset); | 
|  | cipher_start_src_offset = ldl_le_p( | 
|  | &alg_chain_para->cipher_start_src_offset); | 
|  | len_to_cipher = ldl_le_p(&alg_chain_para->len_to_cipher); | 
|  | len_to_hash = ldl_le_p(&alg_chain_para->len_to_hash); | 
|  | } else { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if (unlikely(src_len != dst_len)) { | 
|  | virtio_error(vdev, "sym request src len is different from dst len"); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len; | 
|  | if (unlikely(max_len > vcrypto->conf.max_size)) { | 
|  | virtio_error(vdev, "virtio-crypto too big length"); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | op_info = g_malloc0(sizeof(CryptoDevBackendSymOpInfo) + max_len); | 
|  | op_info->iv_len = iv_len; | 
|  | op_info->src_len = src_len; | 
|  | op_info->dst_len = dst_len; | 
|  | op_info->aad_len = aad_len; | 
|  | op_info->digest_result_len = hash_result_len; | 
|  | op_info->hash_start_src_offset = hash_start_src_offset; | 
|  | op_info->len_to_hash = len_to_hash; | 
|  | op_info->cipher_start_src_offset = cipher_start_src_offset; | 
|  | op_info->len_to_cipher = len_to_cipher; | 
|  | /* Handle the initialization vector */ | 
|  | if (op_info->iv_len > 0) { | 
|  | DPRINTF("iv_len=%" PRIu32 "\n", op_info->iv_len); | 
|  | op_info->iv = op_info->data + curr_size; | 
|  |  | 
|  | s = iov_to_buf(iov, out_num, 0, op_info->iv, op_info->iv_len); | 
|  | if (unlikely(s != op_info->iv_len)) { | 
|  | virtio_error(vdev, "virtio-crypto iv incorrect"); | 
|  | goto err; | 
|  | } | 
|  | iov_discard_front(&iov, &out_num, op_info->iv_len); | 
|  | curr_size += op_info->iv_len; | 
|  | } | 
|  |  | 
|  | /* Handle additional authentication data if exists */ | 
|  | if (op_info->aad_len > 0) { | 
|  | DPRINTF("aad_len=%" PRIu32 "\n", op_info->aad_len); | 
|  | op_info->aad_data = op_info->data + curr_size; | 
|  |  | 
|  | s = iov_to_buf(iov, out_num, 0, op_info->aad_data, op_info->aad_len); | 
|  | if (unlikely(s != op_info->aad_len)) { | 
|  | virtio_error(vdev, "virtio-crypto additional auth data incorrect"); | 
|  | goto err; | 
|  | } | 
|  | iov_discard_front(&iov, &out_num, op_info->aad_len); | 
|  |  | 
|  | curr_size += op_info->aad_len; | 
|  | } | 
|  |  | 
|  | /* Handle the source data */ | 
|  | if (op_info->src_len > 0) { | 
|  | DPRINTF("src_len=%" PRIu32 "\n", op_info->src_len); | 
|  | op_info->src = op_info->data + curr_size; | 
|  |  | 
|  | s = iov_to_buf(iov, out_num, 0, op_info->src, op_info->src_len); | 
|  | if (unlikely(s != op_info->src_len)) { | 
|  | virtio_error(vdev, "virtio-crypto source data incorrect"); | 
|  | goto err; | 
|  | } | 
|  | iov_discard_front(&iov, &out_num, op_info->src_len); | 
|  |  | 
|  | curr_size += op_info->src_len; | 
|  | } | 
|  |  | 
|  | /* Handle the destination data */ | 
|  | op_info->dst = op_info->data + curr_size; | 
|  | curr_size += op_info->dst_len; | 
|  |  | 
|  | DPRINTF("dst_len=%" PRIu32 "\n", op_info->dst_len); | 
|  |  | 
|  | /* Handle the hash digest result */ | 
|  | if (hash_result_len > 0) { | 
|  | DPRINTF("hash_result_len=%" PRIu32 "\n", hash_result_len); | 
|  | op_info->digest_result = op_info->data + curr_size; | 
|  | } | 
|  |  | 
|  | return op_info; | 
|  |  | 
|  | err: | 
|  | g_free(op_info); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static int | 
|  | virtio_crypto_handle_sym_req(VirtIOCrypto *vcrypto, | 
|  | struct virtio_crypto_sym_data_req *req, | 
|  | CryptoDevBackendOpInfo *op_info, | 
|  | struct iovec *iov, unsigned int out_num) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); | 
|  | CryptoDevBackendSymOpInfo *sym_op_info; | 
|  | uint32_t op_type; | 
|  |  | 
|  | op_type = ldl_le_p(&req->op_type); | 
|  | if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { | 
|  | sym_op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para, | 
|  | NULL, iov, out_num); | 
|  | if (!sym_op_info) { | 
|  | return -EFAULT; | 
|  | } | 
|  | } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { | 
|  | sym_op_info = virtio_crypto_sym_op_helper(vdev, NULL, | 
|  | &req->u.chain.para, | 
|  | iov, out_num); | 
|  | if (!sym_op_info) { | 
|  | return -EFAULT; | 
|  | } | 
|  | } else { | 
|  | /* VIRTIO_CRYPTO_SYM_OP_NONE */ | 
|  | error_report("virtio-crypto unsupported cipher type"); | 
|  | return -VIRTIO_CRYPTO_NOTSUPP; | 
|  | } | 
|  |  | 
|  | sym_op_info->op_type = op_type; | 
|  | op_info->u.sym_op_info = sym_op_info; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | virtio_crypto_handle_asym_req(VirtIOCrypto *vcrypto, | 
|  | struct virtio_crypto_akcipher_data_req *req, | 
|  | CryptoDevBackendOpInfo *op_info, | 
|  | struct iovec *iov, unsigned int out_num) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); | 
|  | CryptoDevBackendAsymOpInfo *asym_op_info; | 
|  | uint32_t src_len; | 
|  | uint32_t dst_len; | 
|  | uint32_t len; | 
|  | uint8_t *src = NULL; | 
|  | uint8_t *dst = NULL; | 
|  |  | 
|  | asym_op_info = g_new0(CryptoDevBackendAsymOpInfo, 1); | 
|  | src_len = ldl_le_p(&req->para.src_data_len); | 
|  | dst_len = ldl_le_p(&req->para.dst_data_len); | 
|  |  | 
|  | if (src_len > 0) { | 
|  | src = g_malloc0(src_len); | 
|  | len = iov_to_buf(iov, out_num, 0, src, src_len); | 
|  | if (unlikely(len != src_len)) { | 
|  | virtio_error(vdev, "virtio-crypto asym src data incorrect" | 
|  | "expected %u, actual %u", src_len, len); | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | iov_discard_front(&iov, &out_num, src_len); | 
|  | } | 
|  |  | 
|  | if (dst_len > 0) { | 
|  | dst = g_malloc0(dst_len); | 
|  |  | 
|  | if (op_info->op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) { | 
|  | len = iov_to_buf(iov, out_num, 0, dst, dst_len); | 
|  | if (unlikely(len != dst_len)) { | 
|  | virtio_error(vdev, "virtio-crypto asym dst data incorrect" | 
|  | "expected %u, actual %u", dst_len, len); | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | iov_discard_front(&iov, &out_num, dst_len); | 
|  | } | 
|  | } | 
|  |  | 
|  | asym_op_info->src_len = src_len; | 
|  | asym_op_info->dst_len = dst_len; | 
|  | asym_op_info->src = src; | 
|  | asym_op_info->dst = dst; | 
|  | op_info->u.asym_op_info = asym_op_info; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err: | 
|  | g_free(asym_op_info); | 
|  | g_free(src); | 
|  | g_free(dst); | 
|  |  | 
|  | return -EFAULT; | 
|  | } | 
|  |  | 
|  | static int | 
|  | virtio_crypto_handle_request(VirtIOCryptoReq *request) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = request->vcrypto; | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); | 
|  | VirtQueueElement *elem = &request->elem; | 
|  | int queue_index = virtio_crypto_vq2q(virtio_get_queue_index(request->vq)); | 
|  | struct virtio_crypto_op_data_req req; | 
|  | int ret; | 
|  | g_autofree struct iovec *in_iov_copy = NULL; | 
|  | g_autofree struct iovec *out_iov_copy = NULL; | 
|  | struct iovec *in_iov; | 
|  | struct iovec *out_iov; | 
|  | unsigned in_num; | 
|  | unsigned out_num; | 
|  | uint32_t opcode; | 
|  | CryptoDevBackendOpInfo *op_info = &request->op_info; | 
|  |  | 
|  | if (elem->out_num < 1 || elem->in_num < 1) { | 
|  | virtio_error(vdev, "virtio-crypto dataq missing headers"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | out_num = elem->out_num; | 
|  | out_iov_copy = g_memdup2(elem->out_sg, sizeof(out_iov[0]) * out_num); | 
|  | out_iov = out_iov_copy; | 
|  |  | 
|  | in_num = elem->in_num; | 
|  | in_iov_copy = g_memdup2(elem->in_sg, sizeof(in_iov[0]) * in_num); | 
|  | in_iov = in_iov_copy; | 
|  |  | 
|  | if (unlikely(iov_to_buf(out_iov, out_num, 0, &req, sizeof(req)) | 
|  | != sizeof(req))) { | 
|  | virtio_error(vdev, "virtio-crypto request outhdr too short"); | 
|  | return -1; | 
|  | } | 
|  | iov_discard_front(&out_iov, &out_num, sizeof(req)); | 
|  |  | 
|  | if (in_iov[in_num - 1].iov_len < | 
|  | sizeof(struct virtio_crypto_inhdr)) { | 
|  | virtio_error(vdev, "virtio-crypto request inhdr too short"); | 
|  | return -1; | 
|  | } | 
|  | /* We always touch the last byte, so just see how big in_iov is. */ | 
|  | request->in_len = iov_size(in_iov, in_num); | 
|  | request->in = (void *)in_iov[in_num - 1].iov_base | 
|  | + in_iov[in_num - 1].iov_len | 
|  | - sizeof(struct virtio_crypto_inhdr); | 
|  | iov_discard_back(in_iov, &in_num, sizeof(struct virtio_crypto_inhdr)); | 
|  |  | 
|  | /* | 
|  | * The length of operation result, including dest_data | 
|  | * and digest_result if exists. | 
|  | */ | 
|  | request->in_num = in_num; | 
|  | request->in_iov = in_iov; | 
|  | /* now, we free the in_iov_copy inside virtio_crypto_free_request */ | 
|  | in_iov_copy = NULL; | 
|  |  | 
|  | opcode = ldl_le_p(&req.header.opcode); | 
|  | op_info->session_id = ldq_le_p(&req.header.session_id); | 
|  | op_info->op_code = opcode; | 
|  | op_info->queue_index = queue_index; | 
|  | op_info->cb = virtio_crypto_req_complete; | 
|  | op_info->opaque = request; | 
|  |  | 
|  | switch (opcode) { | 
|  | case VIRTIO_CRYPTO_CIPHER_ENCRYPT: | 
|  | case VIRTIO_CRYPTO_CIPHER_DECRYPT: | 
|  | op_info->algtype = request->flags = QCRYPTODEV_BACKEND_ALGO_TYPE_SYM; | 
|  | ret = virtio_crypto_handle_sym_req(vcrypto, | 
|  | &req.u.sym_req, op_info, | 
|  | out_iov, out_num); | 
|  | goto check_result; | 
|  |  | 
|  | case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: | 
|  | case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: | 
|  | case VIRTIO_CRYPTO_AKCIPHER_SIGN: | 
|  | case VIRTIO_CRYPTO_AKCIPHER_VERIFY: | 
|  | op_info->algtype = request->flags = QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM; | 
|  | ret = virtio_crypto_handle_asym_req(vcrypto, | 
|  | &req.u.akcipher_req, op_info, | 
|  | out_iov, out_num); | 
|  |  | 
|  | check_result: | 
|  | /* Serious errors, need to reset virtio crypto device */ | 
|  | if (ret == -EFAULT) { | 
|  | return -1; | 
|  | } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) { | 
|  | virtio_crypto_req_complete(request, -VIRTIO_CRYPTO_NOTSUPP); | 
|  | } else { | 
|  | ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev, | 
|  | op_info); | 
|  | if (ret < 0) { | 
|  | virtio_crypto_req_complete(request, ret); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case VIRTIO_CRYPTO_HASH: | 
|  | case VIRTIO_CRYPTO_MAC: | 
|  | case VIRTIO_CRYPTO_AEAD_ENCRYPT: | 
|  | case VIRTIO_CRYPTO_AEAD_DECRYPT: | 
|  | default: | 
|  | error_report("virtio-crypto unsupported dataq opcode: %u", | 
|  | opcode); | 
|  | virtio_crypto_req_complete(request, -VIRTIO_CRYPTO_NOTSUPP); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_handle_dataq(VirtIODevice *vdev, VirtQueue *vq) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | VirtIOCryptoReq *req; | 
|  |  | 
|  | while ((req = virtio_crypto_get_request(vcrypto, vq))) { | 
|  | if (virtio_crypto_handle_request(req) < 0) { | 
|  | virtqueue_detach_element(req->vq, &req->elem, 0); | 
|  | virtio_crypto_free_request(req); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_dataq_bh(void *opaque) | 
|  | { | 
|  | VirtIOCryptoQueue *q = opaque; | 
|  | VirtIOCrypto *vcrypto = q->vcrypto; | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); | 
|  |  | 
|  | /* This happens when device was stopped but BH wasn't. */ | 
|  | if (!vdev->vm_running) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Just in case the driver is not ready on more */ | 
|  | if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (;;) { | 
|  | virtio_crypto_handle_dataq(vdev, q->dataq); | 
|  | virtio_queue_set_notification(q->dataq, 1); | 
|  |  | 
|  | /* Are we done or did the guest add more buffers? */ | 
|  | if (virtio_queue_empty(q->dataq)) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | virtio_queue_set_notification(q->dataq, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | virtio_crypto_handle_dataq_bh(VirtIODevice *vdev, VirtQueue *vq) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | VirtIOCryptoQueue *q = | 
|  | &vcrypto->vqs[virtio_crypto_vq2q(virtio_get_queue_index(vq))]; | 
|  |  | 
|  | /* This happens when device was stopped but VCPU wasn't. */ | 
|  | if (!vdev->vm_running) { | 
|  | return; | 
|  | } | 
|  | virtio_queue_set_notification(vq, 0); | 
|  | qemu_bh_schedule(q->dataq_bh); | 
|  | } | 
|  |  | 
|  | static uint64_t virtio_crypto_get_features(VirtIODevice *vdev, | 
|  | uint64_t features, | 
|  | Error **errp) | 
|  | { | 
|  | return features; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_reset(VirtIODevice *vdev) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | /* multiqueue is disabled by default */ | 
|  | vcrypto->curr_queues = 1; | 
|  | if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) { | 
|  | vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY; | 
|  | } else { | 
|  | vcrypto->status |= VIRTIO_CRYPTO_S_HW_READY; | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint32_t virtio_crypto_init_services(uint32_t qservices) | 
|  | { | 
|  | uint32_t vservices = 0; | 
|  |  | 
|  | if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_CIPHER)) { | 
|  | vservices |= (1 << VIRTIO_CRYPTO_SERVICE_CIPHER); | 
|  | } | 
|  | if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_HASH)) { | 
|  | vservices |= (1 << VIRTIO_CRYPTO_SERVICE_HASH); | 
|  | } | 
|  | if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC)) { | 
|  | vservices |= (1 << VIRTIO_CRYPTO_SERVICE_MAC); | 
|  | } | 
|  | if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_AEAD)) { | 
|  | vservices |= (1 << VIRTIO_CRYPTO_SERVICE_AEAD); | 
|  | } | 
|  | if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER)) { | 
|  | vservices |= (1 << VIRTIO_CRYPTO_SERVICE_AKCIPHER); | 
|  | } | 
|  |  | 
|  | return vservices; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_init_config(VirtIODevice *vdev) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  |  | 
|  | vcrypto->conf.crypto_services = virtio_crypto_init_services( | 
|  | vcrypto->conf.cryptodev->conf.crypto_services); | 
|  | vcrypto->conf.cipher_algo_l = | 
|  | vcrypto->conf.cryptodev->conf.cipher_algo_l; | 
|  | vcrypto->conf.cipher_algo_h = | 
|  | vcrypto->conf.cryptodev->conf.cipher_algo_h; | 
|  | vcrypto->conf.hash_algo = vcrypto->conf.cryptodev->conf.hash_algo; | 
|  | vcrypto->conf.mac_algo_l = vcrypto->conf.cryptodev->conf.mac_algo_l; | 
|  | vcrypto->conf.mac_algo_h = vcrypto->conf.cryptodev->conf.mac_algo_h; | 
|  | vcrypto->conf.aead_algo = vcrypto->conf.cryptodev->conf.aead_algo; | 
|  | vcrypto->conf.akcipher_algo = vcrypto->conf.cryptodev->conf.akcipher_algo; | 
|  | vcrypto->conf.max_cipher_key_len = | 
|  | vcrypto->conf.cryptodev->conf.max_cipher_key_len; | 
|  | vcrypto->conf.max_auth_key_len = | 
|  | vcrypto->conf.cryptodev->conf.max_auth_key_len; | 
|  | vcrypto->conf.max_size = vcrypto->conf.cryptodev->conf.max_size; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev); | 
|  | int i; | 
|  |  | 
|  | vcrypto->cryptodev = vcrypto->conf.cryptodev; | 
|  | if (vcrypto->cryptodev == NULL) { | 
|  | error_setg(errp, "'cryptodev' parameter expects a valid object"); | 
|  | return; | 
|  | } else if (cryptodev_backend_is_used(vcrypto->cryptodev)) { | 
|  | error_setg(errp, "can't use already used cryptodev backend: %s", | 
|  | object_get_canonical_path_component(OBJECT(vcrypto->conf.cryptodev))); | 
|  | return; | 
|  | } | 
|  |  | 
|  | vcrypto->max_queues = MAX(vcrypto->cryptodev->conf.peers.queues, 1); | 
|  | if (vcrypto->max_queues + 1 > VIRTIO_QUEUE_MAX) { | 
|  | error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " | 
|  | "must be a positive integer less than %d.", | 
|  | vcrypto->max_queues, VIRTIO_QUEUE_MAX); | 
|  | return; | 
|  | } | 
|  |  | 
|  | virtio_init(vdev, VIRTIO_ID_CRYPTO, vcrypto->config_size); | 
|  | vcrypto->curr_queues = 1; | 
|  | vcrypto->vqs = g_new0(VirtIOCryptoQueue, vcrypto->max_queues); | 
|  | for (i = 0; i < vcrypto->max_queues; i++) { | 
|  | vcrypto->vqs[i].dataq = | 
|  | virtio_add_queue(vdev, 1024, virtio_crypto_handle_dataq_bh); | 
|  | vcrypto->vqs[i].dataq_bh = | 
|  | virtio_bh_new_guarded(dev, virtio_crypto_dataq_bh, | 
|  | &vcrypto->vqs[i]); | 
|  | vcrypto->vqs[i].vcrypto = vcrypto; | 
|  | } | 
|  |  | 
|  | vcrypto->ctrl_vq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_ctrl); | 
|  | if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) { | 
|  | vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY; | 
|  | } else { | 
|  | vcrypto->status |= VIRTIO_CRYPTO_S_HW_READY; | 
|  | } | 
|  |  | 
|  | virtio_crypto_init_config(vdev); | 
|  | cryptodev_backend_set_used(vcrypto->cryptodev, true); | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_device_unrealize(DeviceState *dev) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev); | 
|  | VirtIOCryptoQueue *q; | 
|  | int i, max_queues; | 
|  |  | 
|  | max_queues = vcrypto->multiqueue ? vcrypto->max_queues : 1; | 
|  | for (i = 0; i < max_queues; i++) { | 
|  | virtio_delete_queue(vcrypto->vqs[i].dataq); | 
|  | q = &vcrypto->vqs[i]; | 
|  | qemu_bh_delete(q->dataq_bh); | 
|  | } | 
|  |  | 
|  | g_free(vcrypto->vqs); | 
|  | virtio_delete_queue(vcrypto->ctrl_vq); | 
|  |  | 
|  | virtio_cleanup(vdev); | 
|  | cryptodev_backend_set_used(vcrypto->cryptodev, false); | 
|  | } | 
|  |  | 
|  | static const VMStateDescription vmstate_virtio_crypto = { | 
|  | .name = "virtio-crypto", | 
|  | .unmigratable = 1, | 
|  | .minimum_version_id = VIRTIO_CRYPTO_VM_VERSION, | 
|  | .version_id = VIRTIO_CRYPTO_VM_VERSION, | 
|  | .fields = (const VMStateField[]) { | 
|  | VMSTATE_VIRTIO_DEVICE, | 
|  | VMSTATE_END_OF_LIST() | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static Property virtio_crypto_properties[] = { | 
|  | DEFINE_PROP_LINK("cryptodev", VirtIOCrypto, conf.cryptodev, | 
|  | TYPE_CRYPTODEV_BACKEND, CryptoDevBackend *), | 
|  | DEFINE_PROP_END_OF_LIST(), | 
|  | }; | 
|  |  | 
|  | static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config) | 
|  | { | 
|  | VirtIOCrypto *c = VIRTIO_CRYPTO(vdev); | 
|  | struct virtio_crypto_config crypto_cfg = {}; | 
|  |  | 
|  | /* | 
|  | * Virtio-crypto device conforms to VIRTIO 1.0 which is always LE, | 
|  | * so we can use LE accessors directly. | 
|  | */ | 
|  | stl_le_p(&crypto_cfg.status, c->status); | 
|  | stl_le_p(&crypto_cfg.max_dataqueues, c->max_queues); | 
|  | stl_le_p(&crypto_cfg.crypto_services, c->conf.crypto_services); | 
|  | stl_le_p(&crypto_cfg.cipher_algo_l, c->conf.cipher_algo_l); | 
|  | stl_le_p(&crypto_cfg.cipher_algo_h, c->conf.cipher_algo_h); | 
|  | stl_le_p(&crypto_cfg.hash_algo, c->conf.hash_algo); | 
|  | stl_le_p(&crypto_cfg.mac_algo_l, c->conf.mac_algo_l); | 
|  | stl_le_p(&crypto_cfg.mac_algo_h, c->conf.mac_algo_h); | 
|  | stl_le_p(&crypto_cfg.aead_algo, c->conf.aead_algo); | 
|  | stl_le_p(&crypto_cfg.max_cipher_key_len, c->conf.max_cipher_key_len); | 
|  | stl_le_p(&crypto_cfg.max_auth_key_len, c->conf.max_auth_key_len); | 
|  | stq_le_p(&crypto_cfg.max_size, c->conf.max_size); | 
|  | stl_le_p(&crypto_cfg.akcipher_algo, c->conf.akcipher_algo); | 
|  |  | 
|  | memcpy(config, &crypto_cfg, c->config_size); | 
|  | } | 
|  |  | 
|  | static bool virtio_crypto_started(VirtIOCrypto *c, uint8_t status) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(c); | 
|  | return (status & VIRTIO_CONFIG_S_DRIVER_OK) && | 
|  | (c->status & VIRTIO_CRYPTO_S_HW_READY) && vdev->vm_running; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_vhost_status(VirtIOCrypto *c, uint8_t status) | 
|  | { | 
|  | VirtIODevice *vdev = VIRTIO_DEVICE(c); | 
|  | int queues = c->multiqueue ? c->max_queues : 1; | 
|  | CryptoDevBackend *b = c->cryptodev; | 
|  | CryptoDevBackendClient *cc = b->conf.peers.ccs[0]; | 
|  |  | 
|  | if (!cryptodev_get_vhost(cc, b, 0)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if ((virtio_crypto_started(c, status)) == !!c->vhost_started) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!c->vhost_started) { | 
|  | int r; | 
|  |  | 
|  | c->vhost_started = 1; | 
|  | r = cryptodev_vhost_start(vdev, queues); | 
|  | if (r < 0) { | 
|  | error_report("unable to start vhost crypto: %d: " | 
|  | "falling back on userspace virtio", -r); | 
|  | c->vhost_started = 0; | 
|  | } | 
|  | } else { | 
|  | cryptodev_vhost_stop(vdev, queues); | 
|  | c->vhost_started = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_set_status(VirtIODevice *vdev, uint8_t status) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  |  | 
|  | virtio_crypto_vhost_status(vcrypto, status); | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_guest_notifier_mask(VirtIODevice *vdev, int idx, | 
|  | bool mask) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | int queue = virtio_crypto_vq2q(idx); | 
|  |  | 
|  | assert(vcrypto->vhost_started); | 
|  |  | 
|  | /* | 
|  | * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 | 
|  | * as the macro of configure interrupt's IDX, If this driver does not | 
|  | * support, the function will return | 
|  | */ | 
|  |  | 
|  | if (idx == VIRTIO_CONFIG_IRQ_IDX) { | 
|  | return; | 
|  | } | 
|  | cryptodev_vhost_virtqueue_mask(vdev, queue, idx, mask); | 
|  | } | 
|  |  | 
|  | static bool virtio_crypto_guest_notifier_pending(VirtIODevice *vdev, int idx) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | int queue = virtio_crypto_vq2q(idx); | 
|  |  | 
|  | assert(vcrypto->vhost_started); | 
|  |  | 
|  | /* | 
|  | * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 | 
|  | * as the macro of configure interrupt's IDX, If this driver does not | 
|  | * support, the function will return | 
|  | */ | 
|  |  | 
|  | if (idx == VIRTIO_CONFIG_IRQ_IDX) { | 
|  | return false; | 
|  | } | 
|  | return cryptodev_vhost_virtqueue_pending(vdev, queue, idx); | 
|  | } | 
|  |  | 
|  | static struct vhost_dev *virtio_crypto_get_vhost(VirtIODevice *vdev) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); | 
|  | CryptoDevBackend *b = vcrypto->cryptodev; | 
|  | CryptoDevBackendClient *cc = b->conf.peers.ccs[0]; | 
|  | CryptoDevBackendVhost *vhost_crypto = cryptodev_get_vhost(cc, b, 0); | 
|  | return &vhost_crypto->dev; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_class_init(ObjectClass *klass, void *data) | 
|  | { | 
|  | DeviceClass *dc = DEVICE_CLASS(klass); | 
|  | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | 
|  |  | 
|  | device_class_set_props(dc, virtio_crypto_properties); | 
|  | dc->vmsd = &vmstate_virtio_crypto; | 
|  | set_bit(DEVICE_CATEGORY_MISC, dc->categories); | 
|  | vdc->realize = virtio_crypto_device_realize; | 
|  | vdc->unrealize = virtio_crypto_device_unrealize; | 
|  | vdc->get_config = virtio_crypto_get_config; | 
|  | vdc->get_features = virtio_crypto_get_features; | 
|  | vdc->reset = virtio_crypto_reset; | 
|  | vdc->set_status = virtio_crypto_set_status; | 
|  | vdc->guest_notifier_mask = virtio_crypto_guest_notifier_mask; | 
|  | vdc->guest_notifier_pending = virtio_crypto_guest_notifier_pending; | 
|  | vdc->get_vhost = virtio_crypto_get_vhost; | 
|  | } | 
|  |  | 
|  | static void virtio_crypto_instance_init(Object *obj) | 
|  | { | 
|  | VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(obj); | 
|  |  | 
|  | /* | 
|  | * The default config_size is sizeof(struct virtio_crypto_config). | 
|  | * Can be overridden with virtio_crypto_set_config_size. | 
|  | */ | 
|  | vcrypto->config_size = sizeof(struct virtio_crypto_config); | 
|  | } | 
|  |  | 
|  | static const TypeInfo virtio_crypto_info = { | 
|  | .name = TYPE_VIRTIO_CRYPTO, | 
|  | .parent = TYPE_VIRTIO_DEVICE, | 
|  | .instance_size = sizeof(VirtIOCrypto), | 
|  | .instance_init = virtio_crypto_instance_init, | 
|  | .class_init = virtio_crypto_class_init, | 
|  | }; | 
|  |  | 
|  | static void virtio_register_types(void) | 
|  | { | 
|  | type_register_static(&virtio_crypto_info); | 
|  | } | 
|  |  | 
|  | type_init(virtio_register_types) |