diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
index b5a108f..1090946 100644
--- a/hw/virtio/virtio-crypto.c
+++ b/hw/virtio/virtio-crypto.c
@@ -26,8 +26,181 @@ static void virtio_crypto_process(VirtIOCrypto *vcrypto)
 {
 }
 
+static inline int virtio_crypto_vq2q(int queue_index)
+{
+    return queue_index;
+}
+
+static void
+virtio_crypto_cipher_session_helper(VirtIODevice *vdev,
+           CryptoSymSessionInfo *info,
+           struct virtio_crypto_cipher_session_para *cipher_para,
+           struct virtio_crypto_cipher_session_output *cipher_out)
+{
+    hwaddr key_gpa;
+    void *key_hva;
+    hwaddr len;
+
+    info->cipher_alg = cipher_para->algo;
+    info->key_len = cipher_para->keylen;
+    info->direction = cipher_para->op;
+    len = info->key_len;
+    /* get cipher key */
+    if (len > 0) {
+        DPRINTF("keylen=%" PRIu32 "\n", info->key_len);
+        key_gpa = cipher_out->key_addr;
+
+        key_hva = cpu_physical_memory_map(key_gpa, &len, 0);
+
+        info->cipher_key = g_malloc(info->key_len);
+        memcpy(info->cipher_key, key_hva, info->key_len);
+        cpu_physical_memory_unmap(key_hva, len, 0, len);
+    }
+}
+
+static int64_t
+virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
+               struct virtio_crypto_sym_create_session_req *sess_req,
+               uint32_t queue_id,
+               uint64_t *session_id)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+    CryptoSymSessionInfo info;
+    int ret;
+    CryptoClientState *cc;
+    int queue_index;;
+    uint32_t op_type;
+    hwaddr auth_key_gpa;
+    void *auth_key_hva;
+    struct virtio_crypto_session_input *input;
+    hwaddr len;
+
+    memset(&info, 0, sizeof(info));
+    op_type = sess_req->op_type;
+    info.op_type = op_type;
+
+    if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
+        virtio_crypto_cipher_session_helper(vdev, &info,
+                           &sess_req->u.cipher.para,
+                           &sess_req->u.cipher.out);
+        input = &sess_req->u.cipher.input;
+    } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
+        /* cipher part */
+        virtio_crypto_cipher_session_helper(vdev, &info,
+                           &sess_req->u.chain.para.cipher_param,
+                           &sess_req->u.chain.out.cipher);
+        input = &sess_req->u.chain.input;
+        /* hash part */
+        info.alg_chain_order = sess_req->u.chain.para.alg_chain_order;
+        info.add_len = sess_req->u.chain.para.aad_len;
+        info.hash_mode = sess_req->u.chain.para.hash_mode;
+        if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
+            info.hash_alg = sess_req->u.chain.para.u.mac_param.algo;
+            len = info.auth_key_len =
+                       sess_req->u.chain.para.u.mac_param.auth_key_len;
+            info.hash_result_len =
+                    sess_req->u.chain.para.u.mac_param.hash_result_len;
+            /* get auth key */
+            if (len > 0) {
+                DPRINTF("keylen=%" PRIu32 "\n", info.auth_key_len);
+                auth_key_gpa = sess_req->u.chain.out.mac.auth_key_addr;
+                auth_key_hva = cpu_physical_memory_map(auth_key_gpa,
+                               &len, false);
+                info.auth_key = g_malloc(len);
+                memcpy(info.auth_key, auth_key_hva, len);
+                cpu_physical_memory_unmap(auth_key_hva, len, false, len);
+            }
+        } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
+            info.hash_alg = sess_req->u.chain.para.u.hash_param.algo;
+            info.hash_result_len =
+                   sess_req->u.chain.para.u.hash_param.hash_result_len;
+        } else {
+            /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
+            error_report("unsupported hash mode");
+            goto err;
+        }
+    } else {
+        /* VIRTIO_CRYPTO_SYM_OP_NONE */
+        error_report("unsupported cipher type");
+        goto err;
+    }
+
+    queue_index = virtio_crypto_vq2q(queue_id);
+    cc = qemu_get_crypto_subqueue(vcrypto->crypto, queue_index);
+    ret = qemu_crypto_create_session(cc, &info, session_id);
+    if (ret == 0) {
+        DPRINTF("create session_id=%" PRIu64 "\n", *session_id);
+        /* Set the result, notify the frontend driver soon */
+        input->status = VIRTIO_CRYPTO_OP_OK;
+        input->session_id = *session_id;
+
+        g_free(info.cipher_key);
+        g_free(info.auth_key);
+        return 0;
+    }
+
+err:
+    g_free(info.cipher_key);
+    g_free(info.auth_key);
+    input->status = VIRTIO_CRYPTO_OP_ERR;
+    return -1;
+}
+
 static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
+    VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
+    struct virtio_crypto_op_ctrl_req ctrl;
+    VirtQueueElement *elem;
+    size_t s;
+    struct iovec *iov;
+    unsigned int iov_cnt;
+    uint32_t queue_id;
+    uint32_t opcode;
+    uint64_t session_id = 0;
+
+    for (;;) {
+        elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+        if (!elem) {
+            break;
+        }
+        if (elem->in_num < 1 ||
+            iov_size(elem->in_sg, elem->in_num) < sizeof(ctrl)) {
+            error_report("virtio-crypto ctrl missing headers");
+            exit(1);
+        }
+
+        iov_cnt = elem->in_num;
+        iov = elem->in_sg;
+        s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
+        assert(s == sizeof(ctrl));
+        opcode = ctrl.header.opcode;
+        queue_id = ctrl.header.queue_id;
+
+        switch (opcode) {
+        case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
+            virtio_crypto_create_sym_session(vcrypto,
+                             &ctrl.u.sym_create_session,
+                             queue_id,
+                             &session_id);
+
+            break;
+        case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
+        case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
+        case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
+        case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
+        case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
+        case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
+        case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
+        default:
+            error_report("virtio-crypto unsupported ctrl opcode: %u",
+                         opcode);
+            exit(1);
+        }
+
+        virtqueue_push(vq, elem, sizeof(ctrl));
+        virtio_notify(vdev, vq);
+        g_free(elem);
+    }
 }
 
 static void virtio_crypto_handle_dataq_bh(VirtIODevice *vdev, VirtQueue *vq)
@@ -213,7 +386,7 @@ static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config)
     crypto_cfg.aead_algo = c->legacy_conf.aead_algo;
     crypto_cfg.primitive_algo = c->legacy_conf.primitive_algo;
 
-    memcpy(config, &crypto_cfg, c->config_size;
+    memcpy(config, &crypto_cfg, c->config_size);
 }
 
 static void virtio_crypto_set_config(VirtIODevice *vdev, const uint8_t *config)
