diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index dc0a364..9ad79ad 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -265,6 +265,12 @@ static int cryptodev_builtin_sym_operation(
         return -VIRTIO_CRYPTO_INVSESS;
     }
 
+    if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
+        error_setg(errp,
+               "Algorithm chain is unsupported for cryptdoev-builtin");
+        return -VIRTIO_CRYPTO_NOTSUPP;
+    }
+
     sess = builtin->sessions[op_info->session_id];
 
     ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv,
diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
index 1729579..0bd0686 100644
--- a/hw/virtio/virtio-crypto.c
+++ b/hw/virtio/virtio-crypto.c
@@ -390,21 +390,42 @@ virtio_crypto_get_request(VirtIOCrypto *s, VirtQueue *vq)
 
 static CryptoDevBackendSymOpInfo *
 virtio_crypto_sym_op_helper(VirtIODevice *vdev,
-           struct virtio_crypto_cipher_para *para,
-           uint32_t aad_len,
-           struct iovec *iov, unsigned int out_num,
-           uint32_t hash_result_len,
-           uint32_t hash_start_src_offset)
+           struct virtio_crypto_cipher_para *cipher_para,
+           struct virtio_crypto_alg_chain_data_para *alg_chain_para,
+           struct iovec *iov, unsigned int out_num)
 {
     CryptoDevBackendSymOpInfo *op_info;
-    uint32_t src_len, dst_len;
-    uint32_t iv_len;
+    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;
+
     size_t max_len, curr_size = 0;
     size_t s;
 
-    iv_len = virtio_ldl_p(vdev, &para->iv_len);
-    src_len = virtio_ldl_p(vdev, &para->src_data_len);
-    dst_len = virtio_ldl_p(vdev, &para->dst_data_len);
+    /* Plain cipher */
+    if (cipher_para) {
+        iv_len = virtio_ldl_p(vdev, &cipher_para->iv_len);
+        src_len = virtio_ldl_p(vdev, &cipher_para->src_data_len);
+        dst_len = virtio_ldl_p(vdev, &cipher_para->dst_data_len);
+    } else if (alg_chain_para) { /* Algorithm chain */
+        iv_len = virtio_ldl_p(vdev, &alg_chain_para->iv_len);
+        src_len = virtio_ldl_p(vdev, &alg_chain_para->src_data_len);
+        dst_len = virtio_ldl_p(vdev, &alg_chain_para->dst_data_len);
+
+        aad_len = virtio_ldl_p(vdev, &alg_chain_para->aad_len);
+        hash_result_len = virtio_ldl_p(vdev,
+                              &alg_chain_para->hash_result_len);
+        hash_start_src_offset = virtio_ldl_p(vdev,
+                         &alg_chain_para->hash_start_src_offset);
+        cipher_start_src_offset = virtio_ldl_p(vdev,
+                         &alg_chain_para->cipher_start_src_offset);
+        len_to_cipher = virtio_ldl_p(vdev, &alg_chain_para->len_to_cipher);
+        len_to_hash = virtio_ldl_p(vdev, &alg_chain_para->len_to_hash);
+    } else {
+        return NULL;
+    }
 
     max_len = iv_len + aad_len + src_len + dst_len + hash_result_len;
     op_info = g_malloc0(sizeof(CryptoDevBackendSymOpInfo) + max_len);
@@ -414,6 +435,9 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev,
     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 initilization vector */
     if (op_info->iv_len > 0) {
         DPRINTF("iv_len=%" PRIu32 "\n", op_info->iv_len);
@@ -491,25 +515,15 @@ virtio_crypto_handle_sym_req(VirtIOCrypto *vcrypto,
 
     if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
         op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para,
-                                              0, iov, out_num, 0, 0);
+                                              NULL, iov, out_num);
         if (!op_info) {
             return -EFAULT;
         }
         op_info->op_type = op_type;
     } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
-        uint32_t aad_len, hash_result_len;
-        uint32_t hash_start_src_offset;
-
-        aad_len = virtio_ldl_p(vdev, &req->u.chain.para.aad_len);
-        hash_result_len = virtio_ldl_p(vdev,
-                              &req->u.chain.para.hash_result_len);
-        hash_start_src_offset = virtio_ldl_p(vdev,
-                         &req->u.chain.para.hash_start_src_offset);
-        /* cipher part */
-        op_info = virtio_crypto_sym_op_helper(vdev, &req->u.chain.para.cipher,
-                                              aad_len, iov, out_num,
-                                              hash_result_len,
-                                              hash_start_src_offset);
+        op_info = virtio_crypto_sym_op_helper(vdev, NULL,
+                                              &req->u.chain.para,
+                                              iov, out_num);
         if (!op_info) {
             return -EFAULT;
         }
diff --git a/include/standard-headers/linux/virtio_crypto.h b/include/standard-headers/linux/virtio_crypto.h
index a62d192..3556fb3 100644
--- a/include/standard-headers/linux/virtio_crypto.h
+++ b/include/standard-headers/linux/virtio_crypto.h
@@ -301,7 +301,15 @@ struct virtio_crypto_mac_data_req {
 };
 
 struct virtio_crypto_alg_chain_data_para {
-    struct virtio_crypto_cipher_para cipher;
+    __virtio32 iv_len;
+    /* Length of source data */
+    __virtio32 src_data_len;
+    /* Length of destination data */
+    __virtio32 dst_data_len;
+    /* Starting point for cipher processing in source data */
+    __virtio32 cipher_start_src_offset;
+    /* Length of the source data that the cipher will be computed on */
+    __virtio32 len_to_cipher;
     /* Starting point for hash processing in source data */
     __virtio32 hash_start_src_offset;
     /* Length of the source data that the hash will be computed on */
@@ -310,6 +318,7 @@ struct virtio_crypto_alg_chain_data_para {
     __virtio32 aad_len;
     /* Length of the hash result */
     __virtio32 hash_result_len;
+    __virtio32 reserved;
 };
 
 struct virtio_crypto_alg_chain_data_req {
diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
index f55b79e..f9a4b36 100644
--- a/include/sysemu/cryptodev.h
+++ b/include/sysemu/cryptodev.h
@@ -108,7 +108,15 @@ typedef struct CryptoDevBackendSymSessionInfo {
  * @dst_len: byte length of destination data
  * @digest_result_len: byte length of hash digest result
  * @hash_start_src_offset: Starting point for hash processing, specified
- *                 as number of bytes from start of packet in source data
+ *  as number of bytes from start of packet in source data, only used for
+ *  algorithm chain
+ * @cipher_start_src_offset: Starting point for cipher processing, specified
+ *  as number of bytes from start of packet in source data, only used for
+ *  algorithm chain
+ * @len_to_hash: byte length of source data on which the hash
+ *  operation will be computed, only used for algorithm chain
+ * @len_to_cipher: byte length of source data on which the cipher
+ *  operation will be computed, only used for algorithm chain
  * @op_type: operation type (refer to virtio_crypto.h)
  * @iv: point to the initialization vector or counter
  * @src: point to the source data
@@ -126,6 +134,9 @@ typedef struct CryptoDevBackendSymOpInfo {
     uint32_t dst_len;
     uint32_t digest_result_len;
     uint32_t hash_start_src_offset;
+    uint32_t cipher_start_src_offset;
+    uint32_t len_to_hash;
+    uint32_t len_to_cipher;
     uint8_t op_type;
     uint8_t *iv;
     uint8_t *src;
