diff mbox series

[v2,4/9] staging: ccree: add IV generation support

Message ID 1492693983-8175-5-git-send-email-gilad@benyossef.com
State Accepted
Commit a4d826b94a634e7319c212d8ab1d8cbcf783d0f2
Headers show
Series None | expand

Commit Message

Gilad Ben-Yossef April 20, 2017, 1:12 p.m. UTC
Add CryptoCell IV hardware generation support.

This patch adds the needed support to drive the HW but does not expose
the ability via the kernel crypto API yet.

Signed-off-by: Gilad Ben-Yossef <gilad@benyossef.com>

---
 drivers/staging/ccree/Makefile          |   2 +-
 drivers/staging/ccree/ssi_buffer_mgr.c  |   2 +
 drivers/staging/ccree/ssi_cipher.c      |  11 ++
 drivers/staging/ccree/ssi_cipher.h      |   1 +
 drivers/staging/ccree/ssi_driver.c      |   9 +
 drivers/staging/ccree/ssi_driver.h      |   7 +
 drivers/staging/ccree/ssi_ivgen.c       | 301 ++++++++++++++++++++++++++++++++
 drivers/staging/ccree/ssi_ivgen.h       |  72 ++++++++
 drivers/staging/ccree/ssi_pm.c          |   2 +
 drivers/staging/ccree/ssi_request_mgr.c |  33 +++-
 10 files changed, 438 insertions(+), 2 deletions(-)
 create mode 100644 drivers/staging/ccree/ssi_ivgen.c
 create mode 100644 drivers/staging/ccree/ssi_ivgen.h

-- 
2.1.4
diff mbox series

Patch

diff --git a/drivers/staging/ccree/Makefile b/drivers/staging/ccree/Makefile
index 21a80d5..89afe9a 100644
--- a/drivers/staging/ccree/Makefile
+++ b/drivers/staging/ccree/Makefile
@@ -1,2 +1,2 @@ 
 obj-$(CONFIG_CRYPTO_DEV_CCREE) := ccree.o
-ccree-y := ssi_driver.o ssi_sysfs.o ssi_buffer_mgr.o ssi_request_mgr.o ssi_cipher.o ssi_hash.o ssi_sram_mgr.o ssi_pm.o ssi_pm_ext.o
+ccree-y := ssi_driver.o ssi_sysfs.o ssi_buffer_mgr.o ssi_request_mgr.o ssi_cipher.o ssi_hash.o ssi_ivgen.o ssi_sram_mgr.o ssi_pm.o ssi_pm_ext.o
diff --git a/drivers/staging/ccree/ssi_buffer_mgr.c b/drivers/staging/ccree/ssi_buffer_mgr.c
index a0fafa9..6a9c964 100644
--- a/drivers/staging/ccree/ssi_buffer_mgr.c
+++ b/drivers/staging/ccree/ssi_buffer_mgr.c
@@ -534,6 +534,7 @@  void ssi_buffer_mgr_unmap_blkcipher_request(
 		SSI_RESTORE_DMA_ADDR_TO_48BIT(req_ctx->gen_ctx.iv_dma_addr);
 		dma_unmap_single(dev, req_ctx->gen_ctx.iv_dma_addr, 
 				 ivsize, 
+				 req_ctx->is_giv ? DMA_BIDIRECTIONAL :
 				 DMA_TO_DEVICE);
 	}
 	/* Release pool */
@@ -587,6 +588,7 @@  int ssi_buffer_mgr_map_blkcipher_request(
 		req_ctx->gen_ctx.iv_dma_addr = 
 			dma_map_single(dev, (void *)info, 
 				       ivsize, 
+				       req_ctx->is_giv ? DMA_BIDIRECTIONAL:
 				       DMA_TO_DEVICE);
 		if (unlikely(dma_mapping_error(dev, 
 					req_ctx->gen_ctx.iv_dma_addr))) {
diff --git a/drivers/staging/ccree/ssi_cipher.c b/drivers/staging/ccree/ssi_cipher.c
index 01467e8..2e4ce90 100644
--- a/drivers/staging/ccree/ssi_cipher.c
+++ b/drivers/staging/ccree/ssi_cipher.c
@@ -819,6 +819,13 @@  static int ssi_blkcipher_process(
 			      areq,
 			      desc, &seq_len);
 
+	/* do we need to generate IV? */
+	if (req_ctx->is_giv == true) {
+		ssi_req.ivgen_dma_addr[0] = req_ctx->gen_ctx.iv_dma_addr;
+		ssi_req.ivgen_dma_addr_len = 1;
+		/* set the IV size (8/16 B long)*/
+		ssi_req.ivgen_size = ivsize;
+	}
 	END_CYCLE_COUNT(ssi_req.op_type, STAT_PHASE_2);
 
 	/* STAT_PHASE_3: Lock HW and push sequence */
@@ -901,6 +908,7 @@  static int ssi_sblkcipher_encrypt(struct blkcipher_desc *desc,
 	unsigned int ivsize = crypto_blkcipher_ivsize(blk_tfm);
 
 	req_ctx->backup_info = desc->info;
+	req_ctx->is_giv = false;
 
 	return ssi_blkcipher_process(tfm, req_ctx, dst, src, nbytes, desc->info, ivsize, NULL, DRV_CRYPTO_DIRECTION_ENCRYPT);
 }
@@ -916,6 +924,7 @@  static int ssi_sblkcipher_decrypt(struct blkcipher_desc *desc,
 	unsigned int ivsize = crypto_blkcipher_ivsize(blk_tfm);
 
 	req_ctx->backup_info = desc->info;
+	req_ctx->is_giv = false;
 
 	return ssi_blkcipher_process(tfm, req_ctx, dst, src, nbytes, desc->info, ivsize, NULL, DRV_CRYPTO_DIRECTION_DECRYPT);
 }
@@ -948,6 +957,7 @@  static int ssi_ablkcipher_encrypt(struct ablkcipher_request *req)
 	unsigned int ivsize = crypto_ablkcipher_ivsize(ablk_tfm);
 
 	req_ctx->backup_info = req->info;
+	req_ctx->is_giv = false;
 
 	return ssi_blkcipher_process(tfm, req_ctx, req->dst, req->src, req->nbytes, req->info, ivsize, (void *)req, DRV_CRYPTO_DIRECTION_ENCRYPT);
 }
@@ -960,6 +970,7 @@  static int ssi_ablkcipher_decrypt(struct ablkcipher_request *req)
 	unsigned int ivsize = crypto_ablkcipher_ivsize(ablk_tfm);
 
 	req_ctx->backup_info = req->info;
+	req_ctx->is_giv = false;
 	return ssi_blkcipher_process(tfm, req_ctx, req->dst, req->src, req->nbytes, req->info, ivsize, (void *)req, DRV_CRYPTO_DIRECTION_DECRYPT);
 }
 
diff --git a/drivers/staging/ccree/ssi_cipher.h b/drivers/staging/ccree/ssi_cipher.h
index 511800f1..d1a98f9 100644
--- a/drivers/staging/ccree/ssi_cipher.h
+++ b/drivers/staging/ccree/ssi_cipher.h
@@ -45,6 +45,7 @@  struct blkcipher_req_ctx {
 	uint32_t out_nents;
 	uint32_t out_mlli_nents;
 	uint8_t *backup_info; /*store iv for generated IV flow*/
+	bool is_giv;
 	struct mlli_params mlli_params;
 };
 
diff --git a/drivers/staging/ccree/ssi_driver.c b/drivers/staging/ccree/ssi_driver.c
index 1310ac5..aee5469 100644
--- a/drivers/staging/ccree/ssi_driver.c
+++ b/drivers/staging/ccree/ssi_driver.c
@@ -64,6 +64,7 @@ 
 #include "ssi_sysfs.h"
 #include "ssi_cipher.h"
 #include "ssi_hash.h"
+#include "ssi_ivgen.h"
 #include "ssi_sram_mgr.h"
 #include "ssi_pm.h"
 
@@ -348,6 +349,12 @@  static int init_cc_resources(struct platform_device *plat_dev)
 		goto init_cc_res_err;
 	}
 
+	rc = ssi_ivgen_init(new_drvdata);
+	if (unlikely(rc != 0)) {
+		SSI_LOG_ERR("ssi_ivgen_init failed\n");
+		goto init_cc_res_err;
+	}
+
 	/* Allocate crypto algs */
 	rc = ssi_ablkcipher_alloc(new_drvdata);
 	if (unlikely(rc != 0)) {
@@ -369,6 +376,7 @@  static int init_cc_resources(struct platform_device *plat_dev)
 	if (new_drvdata != NULL) {
 		ssi_hash_free(new_drvdata);
 		ssi_ablkcipher_free(new_drvdata);
+		ssi_ivgen_fini(new_drvdata);
 		ssi_power_mgr_fini(new_drvdata);
 		ssi_buffer_mgr_fini(new_drvdata);
 		request_mgr_fini(new_drvdata);
@@ -410,6 +418,7 @@  static void cleanup_cc_resources(struct platform_device *plat_dev)
 
         ssi_hash_free(drvdata);
         ssi_ablkcipher_free(drvdata);
+	ssi_ivgen_fini(drvdata);
 	ssi_power_mgr_fini(drvdata);
 	ssi_buffer_mgr_fini(drvdata);
 	request_mgr_fini(drvdata);
diff --git a/drivers/staging/ccree/ssi_driver.h b/drivers/staging/ccree/ssi_driver.h
index baac9bf..5f4b14e 100644
--- a/drivers/staging/ccree/ssi_driver.h
+++ b/drivers/staging/ccree/ssi_driver.h
@@ -106,9 +106,15 @@ 
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 
+#define SSI_MAX_IVGEN_DMA_ADDRESSES 	3
 struct ssi_crypto_req {
 	void (*user_cb)(struct device *dev, void *req, void __iomem *cc_base);
 	void *user_arg;
+	dma_addr_t ivgen_dma_addr[SSI_MAX_IVGEN_DMA_ADDRESSES]; /* For the first 'ivgen_dma_addr_len' addresses of this array,
+					 generated IV would be placed in it by send_request().
+					 Same generated IV for all addresses! */
+	unsigned int ivgen_dma_addr_len; /* Amount of 'ivgen_dma_addr' elements to be filled. */
+	unsigned int ivgen_size; /* The generated IV size required, 8/16 B allowed. */
 	struct completion seq_compl; /* request completion */
 #ifdef ENABLE_CYCLE_COUNT
 	enum stat_op op_type;
@@ -144,6 +150,7 @@  struct ssi_drvdata {
 	void *hash_handle;
 	void *blkcipher_handle;
 	void *request_mgr_handle;
+	void *ivgen_handle;
 	void *sram_mgr_handle;
 
 #ifdef ENABLE_CYCLE_COUNT
diff --git a/drivers/staging/ccree/ssi_ivgen.c b/drivers/staging/ccree/ssi_ivgen.c
new file mode 100644
index 0000000..4d268d1
--- /dev/null
+++ b/drivers/staging/ccree/ssi_ivgen.c
@@ -0,0 +1,301 @@ 
+/*
+ * Copyright (C) 2012-2016 ARM Limited or its affiliates.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/platform_device.h>
+#include <crypto/ctr.h>
+#include "ssi_config.h"
+#include "ssi_driver.h"
+#include "ssi_ivgen.h"
+#include "ssi_request_mgr.h"
+#include "ssi_sram_mgr.h"
+#include "ssi_buffer_mgr.h"
+
+/* The max. size of pool *MUST* be <= SRAM total size */
+#define SSI_IVPOOL_SIZE 1024
+/* The first 32B fraction of pool are dedicated to the
+   next encryption "key" & "IV" for pool regeneration */
+#define SSI_IVPOOL_META_SIZE (CC_AES_IV_SIZE + AES_KEYSIZE_128)
+#define SSI_IVPOOL_GEN_SEQ_LEN	4
+
+/**
+ * struct ssi_ivgen_ctx -IV pool generation context 
+ * @pool:          the start address of the iv-pool resides in internal RAM 
+ * @ctr_key_dma:   address of pool's encryption key material in internal RAM
+ * @ctr_iv_dma:    address of pool's counter iv in internal RAM
+ * @next_iv_ofs:   the offset to the next available IV in pool
+ * @pool_meta:     virt. address of the initial enc. key/IV
+ * @pool_meta_dma: phys. address of the initial enc. key/IV
+ */
+struct ssi_ivgen_ctx {
+	ssi_sram_addr_t pool;
+	ssi_sram_addr_t ctr_key;
+	ssi_sram_addr_t ctr_iv;
+	uint32_t next_iv_ofs;
+	uint8_t *pool_meta;
+	dma_addr_t pool_meta_dma;
+};
+
+/*!
+ * Generates SSI_IVPOOL_SIZE of random bytes by 
+ * encrypting 0's using AES128-CTR.
+ * 
+ * \param ivgen iv-pool context
+ * \param iv_seq IN/OUT array to the descriptors sequence
+ * \param iv_seq_len IN/OUT pointer to the sequence length 
+ */
+static int ssi_ivgen_generate_pool(
+	struct ssi_ivgen_ctx *ivgen_ctx,
+	HwDesc_s iv_seq[],
+	unsigned int *iv_seq_len)
+{
+	unsigned int idx = *iv_seq_len;
+
+	if ( (*iv_seq_len + SSI_IVPOOL_GEN_SEQ_LEN) > SSI_IVPOOL_SEQ_LEN) {
+		/* The sequence will be longer than allowed */
+		return -EINVAL;
+	}
+	/* Setup key */
+	HW_DESC_INIT(&iv_seq[idx]);
+	HW_DESC_SET_DIN_SRAM(&iv_seq[idx], ivgen_ctx->ctr_key, AES_KEYSIZE_128);
+	HW_DESC_SET_SETUP_MODE(&iv_seq[idx], SETUP_LOAD_KEY0);
+	HW_DESC_SET_CIPHER_CONFIG0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+	HW_DESC_SET_FLOW_MODE(&iv_seq[idx], S_DIN_to_AES);
+	HW_DESC_SET_KEY_SIZE_AES(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
+	HW_DESC_SET_CIPHER_MODE(&iv_seq[idx], DRV_CIPHER_CTR);
+	idx++;
+
+	/* Setup cipher state */
+	HW_DESC_INIT(&iv_seq[idx]);
+	HW_DESC_SET_DIN_SRAM(&iv_seq[idx], ivgen_ctx->ctr_iv, CC_AES_IV_SIZE);
+	HW_DESC_SET_CIPHER_CONFIG0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+	HW_DESC_SET_FLOW_MODE(&iv_seq[idx], S_DIN_to_AES);
+	HW_DESC_SET_SETUP_MODE(&iv_seq[idx], SETUP_LOAD_STATE1);
+	HW_DESC_SET_KEY_SIZE_AES(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
+	HW_DESC_SET_CIPHER_MODE(&iv_seq[idx], DRV_CIPHER_CTR);
+	idx++;
+
+	/* Perform dummy encrypt to skip first block */
+	HW_DESC_INIT(&iv_seq[idx]);
+	HW_DESC_SET_DIN_CONST(&iv_seq[idx], 0, CC_AES_IV_SIZE);
+	HW_DESC_SET_DOUT_SRAM(&iv_seq[idx], ivgen_ctx->pool, CC_AES_IV_SIZE);
+	HW_DESC_SET_FLOW_MODE(&iv_seq[idx], DIN_AES_DOUT);
+	idx++;
+
+	/* Generate IV pool */
+	HW_DESC_INIT(&iv_seq[idx]);
+	HW_DESC_SET_DIN_CONST(&iv_seq[idx], 0, SSI_IVPOOL_SIZE);
+	HW_DESC_SET_DOUT_SRAM(&iv_seq[idx], ivgen_ctx->pool, SSI_IVPOOL_SIZE);
+	HW_DESC_SET_FLOW_MODE(&iv_seq[idx], DIN_AES_DOUT);
+	idx++;
+
+	*iv_seq_len = idx; /* Update sequence length */
+
+	/* queue ordering assures pool readiness */
+	ivgen_ctx->next_iv_ofs = SSI_IVPOOL_META_SIZE;
+
+	return 0;
+}
+
+/*!
+ * Generates the initial pool in SRAM. 
+ * This function should be invoked when resuming DX driver. 
+ * 
+ * \param drvdata 
+ *  
+ * \return int Zero for success, negative value otherwise.
+ */
+int ssi_ivgen_init_sram_pool(struct ssi_drvdata *drvdata)
+{
+	struct ssi_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
+	HwDesc_s iv_seq[SSI_IVPOOL_SEQ_LEN];
+	unsigned int iv_seq_len = 0;
+	int rc;
+
+	/* Generate initial enc. key/iv */
+	get_random_bytes(ivgen_ctx->pool_meta, SSI_IVPOOL_META_SIZE);
+
+	/* The first 32B reserved for the enc. Key/IV */
+	ivgen_ctx->ctr_key = ivgen_ctx->pool;
+	ivgen_ctx->ctr_iv = ivgen_ctx->pool + AES_KEYSIZE_128;
+
+	/* Copy initial enc. key and IV to SRAM at a single descriptor */
+	HW_DESC_INIT(&iv_seq[iv_seq_len]);
+	HW_DESC_SET_DIN_TYPE(&iv_seq[iv_seq_len], DMA_DLLI,
+		ivgen_ctx->pool_meta_dma, SSI_IVPOOL_META_SIZE,
+		NS_BIT);
+	HW_DESC_SET_DOUT_SRAM(&iv_seq[iv_seq_len], ivgen_ctx->pool,
+		SSI_IVPOOL_META_SIZE);
+	HW_DESC_SET_FLOW_MODE(&iv_seq[iv_seq_len], BYPASS);
+	iv_seq_len++;
+
+	/* Generate initial pool */
+	rc = ssi_ivgen_generate_pool(ivgen_ctx, iv_seq, &iv_seq_len);
+	if (unlikely(rc != 0)) {
+		return rc;
+	}
+	/* Fire-and-forget */
+	return send_request_init(drvdata, iv_seq, iv_seq_len);
+}
+
+/*!
+ * Free iv-pool and ivgen context.
+ *  
+ * \param drvdata 
+ */
+void ssi_ivgen_fini(struct ssi_drvdata *drvdata)
+{
+	struct ssi_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
+	struct device *device = &(drvdata->plat_dev->dev);
+
+	if (ivgen_ctx == NULL)
+		return;
+
+	if (ivgen_ctx->pool_meta != NULL) {
+		memset(ivgen_ctx->pool_meta, 0, SSI_IVPOOL_META_SIZE);
+		SSI_RESTORE_DMA_ADDR_TO_48BIT(ivgen_ctx->pool_meta_dma);
+		dma_free_coherent(device, SSI_IVPOOL_META_SIZE,
+			ivgen_ctx->pool_meta, ivgen_ctx->pool_meta_dma);
+	}
+
+	ivgen_ctx->pool = NULL_SRAM_ADDR;
+
+	/* release "this" context */
+	kfree(ivgen_ctx);
+}
+
+/*!
+ * Allocates iv-pool and maps resources. 
+ * This function generates the first IV pool.  
+ * 
+ * \param drvdata Driver's private context
+ * 
+ * \return int Zero for success, negative value otherwise.
+ */
+int ssi_ivgen_init(struct ssi_drvdata *drvdata)
+{
+	struct ssi_ivgen_ctx *ivgen_ctx;
+	struct device *device = &drvdata->plat_dev->dev;
+	int rc;
+
+	/* Allocate "this" context */
+	drvdata->ivgen_handle = kzalloc(sizeof(struct ssi_ivgen_ctx), GFP_KERNEL);
+	if (!drvdata->ivgen_handle) {
+		SSI_LOG_ERR("Not enough memory to allocate IVGEN context "
+			   "(%zu B)\n", sizeof(struct ssi_ivgen_ctx));
+		rc = -ENOMEM;
+		goto out;
+	}
+	ivgen_ctx = drvdata->ivgen_handle;
+
+	/* Allocate pool's header for intial enc. key/IV */
+	ivgen_ctx->pool_meta = dma_alloc_coherent(device, SSI_IVPOOL_META_SIZE,
+			&ivgen_ctx->pool_meta_dma, GFP_KERNEL);
+	if (!ivgen_ctx->pool_meta) {
+		SSI_LOG_ERR("Not enough memory to allocate DMA of pool_meta "
+			   "(%u B)\n", SSI_IVPOOL_META_SIZE);
+		rc = -ENOMEM;
+		goto out;
+	}
+	SSI_UPDATE_DMA_ADDR_TO_48BIT(ivgen_ctx->pool_meta_dma,
+							SSI_IVPOOL_META_SIZE);
+	/* Allocate IV pool in SRAM */
+	ivgen_ctx->pool = ssi_sram_mgr_alloc(drvdata, SSI_IVPOOL_SIZE);
+	if (ivgen_ctx->pool == NULL_SRAM_ADDR) {
+		SSI_LOG_ERR("SRAM pool exhausted\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	return ssi_ivgen_init_sram_pool(drvdata);
+
+out:
+	ssi_ivgen_fini(drvdata);
+	return rc;
+}
+
+/*!
+ * Acquires 16 Bytes IV from the iv-pool
+ * 
+ * \param drvdata Driver private context
+ * \param iv_out_dma Array of physical IV out addresses
+ * \param iv_out_dma_len Length of iv_out_dma array (additional elements of iv_out_dma array are ignore)
+ * \param iv_out_size May be 8 or 16 bytes long 
+ * \param iv_seq IN/OUT array to the descriptors sequence
+ * \param iv_seq_len IN/OUT pointer to the sequence length 
+ *  
+ * \return int Zero for success, negative value otherwise. 
+ */
+int ssi_ivgen_getiv(
+	struct ssi_drvdata *drvdata,
+	dma_addr_t iv_out_dma[],
+	unsigned int iv_out_dma_len,
+	unsigned int iv_out_size,
+	HwDesc_s iv_seq[],
+	unsigned int *iv_seq_len)
+{
+	struct ssi_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
+	unsigned int idx = *iv_seq_len;
+	unsigned int t;
+
+	if ((iv_out_size != CC_AES_IV_SIZE) &&
+	    (iv_out_size != CTR_RFC3686_IV_SIZE)) {
+		return -EINVAL;
+	}
+	if ( (iv_out_dma_len + 1) > SSI_IVPOOL_SEQ_LEN) {
+		/* The sequence will be longer than allowed */
+		return -EINVAL;
+	}
+
+	//check that number of generated IV is limited to max dma address iv buffer size
+	if ( iv_out_dma_len > SSI_MAX_IVGEN_DMA_ADDRESSES) {
+		/* The sequence will be longer than allowed */
+		return -EINVAL;
+	}
+
+	for (t = 0; t < iv_out_dma_len; t++) {
+		/* Acquire IV from pool */
+		HW_DESC_INIT(&iv_seq[idx]);
+		HW_DESC_SET_DIN_SRAM(&iv_seq[idx],
+			ivgen_ctx->pool + ivgen_ctx->next_iv_ofs,
+			iv_out_size);
+		HW_DESC_SET_DOUT_DLLI(&iv_seq[idx], iv_out_dma[t],
+			iv_out_size, NS_BIT, 0);
+		HW_DESC_SET_FLOW_MODE(&iv_seq[idx], BYPASS);
+		idx++;
+	}
+
+	/* Bypass operation is proceeded by crypto sequence, hence must
+	*  assure bypass-write-transaction by a memory barrier */
+	HW_DESC_INIT(&iv_seq[idx]);
+	HW_DESC_SET_DIN_NO_DMA(&iv_seq[idx], 0, 0xfffff0);
+	HW_DESC_SET_DOUT_NO_DMA(&iv_seq[idx], 0, 0, 1);
+	idx++;
+
+	*iv_seq_len = idx; /* update seq length */
+
+	/* Update iv index */
+	ivgen_ctx->next_iv_ofs += iv_out_size;
+
+	if ((SSI_IVPOOL_SIZE - ivgen_ctx->next_iv_ofs) < CC_AES_IV_SIZE) {
+		SSI_LOG_DEBUG("Pool exhausted, regenerating iv-pool\n");
+		/* pool is drained -regenerate it! */
+		return ssi_ivgen_generate_pool(ivgen_ctx, iv_seq, iv_seq_len);
+	}
+
+	return 0;
+}
+
+
diff --git a/drivers/staging/ccree/ssi_ivgen.h b/drivers/staging/ccree/ssi_ivgen.h
new file mode 100644
index 0000000..cf45f4f
--- /dev/null
+++ b/drivers/staging/ccree/ssi_ivgen.h
@@ -0,0 +1,72 @@ 
+/*
+ * Copyright (C) 2012-2016 ARM Limited or its affiliates.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __SSI_IVGEN_H__
+#define __SSI_IVGEN_H__
+
+#include "cc_hw_queue_defs.h"
+
+
+#define SSI_IVPOOL_SEQ_LEN 8
+
+/*!
+ * Allocates iv-pool and maps resources. 
+ * This function generates the first IV pool.  
+ * 
+ * \param drvdata Driver's private context
+ * 
+ * \return int Zero for success, negative value otherwise.
+ */
+int ssi_ivgen_init(struct ssi_drvdata *drvdata);
+
+/*!
+ * Free iv-pool and ivgen context.
+ *  
+ * \param drvdata 
+ */
+void ssi_ivgen_fini(struct ssi_drvdata *drvdata);
+
+/*!
+ * Generates the initial pool in SRAM. 
+ * This function should be invoked when resuming DX driver. 
+ * 
+ * \param drvdata 
+ *  
+ * \return int Zero for success, negative value otherwise.
+ */
+int ssi_ivgen_init_sram_pool(struct ssi_drvdata *drvdata);
+
+/*!
+ * Acquires 16 Bytes IV from the iv-pool
+ * 
+ * \param drvdata Driver private context
+ * \param iv_out_dma Array of physical IV out addresses
+ * \param iv_out_dma_len Length of iv_out_dma array (additional elements of iv_out_dma array are ignore)
+ * \param iv_out_size May be 8 or 16 bytes long 
+ * \param iv_seq IN/OUT array to the descriptors sequence
+ * \param iv_seq_len IN/OUT pointer to the sequence length 
+ *  
+ * \return int Zero for success, negative value otherwise. 
+ */
+int ssi_ivgen_getiv(
+	struct ssi_drvdata *drvdata,
+	dma_addr_t iv_out_dma[],
+	unsigned int iv_out_dma_len,
+	unsigned int iv_out_size,
+	HwDesc_s iv_seq[],
+	unsigned int *iv_seq_len);
+
+#endif /*__SSI_IVGEN_H__*/
diff --git a/drivers/staging/ccree/ssi_pm.c b/drivers/staging/ccree/ssi_pm.c
index da5f2d5..c2e3bb5 100644
--- a/drivers/staging/ccree/ssi_pm.c
+++ b/drivers/staging/ccree/ssi_pm.c
@@ -26,6 +26,7 @@ 
 #include "ssi_request_mgr.h"
 #include "ssi_sram_mgr.h"
 #include "ssi_sysfs.h"
+#include "ssi_ivgen.h"
 #include "ssi_hash.h"
 #include "ssi_pm.h"
 #include "ssi_pm_ext.h"
@@ -83,6 +84,7 @@  int ssi_power_mgr_runtime_resume(struct device *dev)
 	/* must be after the queue resuming as it uses the HW queue*/
 	ssi_hash_init_sram_digest_consts(drvdata);
 	
+	ssi_ivgen_init_sram_pool(drvdata);
 	return 0;
 }
 
diff --git a/drivers/staging/ccree/ssi_request_mgr.c b/drivers/staging/ccree/ssi_request_mgr.c
index 976a54c..c19c006 100644
--- a/drivers/staging/ccree/ssi_request_mgr.c
+++ b/drivers/staging/ccree/ssi_request_mgr.c
@@ -28,6 +28,7 @@ 
 #include "ssi_buffer_mgr.h"
 #include "ssi_request_mgr.h"
 #include "ssi_sysfs.h"
+#include "ssi_ivgen.h"
 #include "ssi_pm.h"
 
 #define SSI_MAX_POLL_ITER	10
@@ -359,9 +360,14 @@  int send_request(
 	void __iomem *cc_base = drvdata->cc_base;
 	struct ssi_request_mgr_handle *req_mgr_h = drvdata->request_mgr_handle;
 	unsigned int used_sw_slots;
+	unsigned int iv_seq_len = 0;
 	unsigned int total_seq_len = len; /*initial sequence length*/
+	HwDesc_s iv_seq[SSI_IVPOOL_SEQ_LEN];
 	int rc;
-	unsigned int max_required_seq_len = total_seq_len + ((is_dout == 0) ? 1 : 0);
+	unsigned int max_required_seq_len = (total_seq_len +
+					((ssi_req->ivgen_dma_addr_len == 0) ? 0 :
+					SSI_IVPOOL_SEQ_LEN ) +
+					((is_dout == 0 )? 1 : 0));
 	DECL_CYCLE_COUNT_RESOURCES;
 
 #if defined (CONFIG_PM_RUNTIME) || defined (CONFIG_PM_SLEEP)
@@ -410,6 +416,30 @@  int send_request(
 		total_seq_len++;
 	}
 
+	if (ssi_req->ivgen_dma_addr_len > 0) {
+		SSI_LOG_DEBUG("Acquire IV from pool into %d DMA addresses 0x%llX, 0x%llX, 0x%llX, IV-size=%u\n",
+			ssi_req->ivgen_dma_addr_len,
+			(unsigned long long)ssi_req->ivgen_dma_addr[0],
+			(unsigned long long)ssi_req->ivgen_dma_addr[1],
+			(unsigned long long)ssi_req->ivgen_dma_addr[2],
+			ssi_req->ivgen_size);
+
+		/* Acquire IV from pool */
+		rc = ssi_ivgen_getiv(drvdata, ssi_req->ivgen_dma_addr, ssi_req->ivgen_dma_addr_len,
+			ssi_req->ivgen_size, iv_seq, &iv_seq_len);
+
+		if (unlikely(rc != 0)) {
+			SSI_LOG_ERR("Failed to generate IV (rc=%d)\n", rc);
+			spin_unlock_bh(&req_mgr_h->hw_lock);
+#if defined (CONFIG_PM_RUNTIME) || defined (CONFIG_PM_SLEEP)
+			ssi_power_mgr_runtime_put_suspend(&drvdata->plat_dev->dev);
+#endif
+			return rc;
+		}
+
+		total_seq_len += iv_seq_len;
+	}
+	
 	used_sw_slots = ((req_mgr_h->req_queue_head - req_mgr_h->req_queue_tail) & (MAX_REQUEST_QUEUE_SIZE-1));
 	if (unlikely(used_sw_slots > req_mgr_h->max_used_sw_slots)) {
 		req_mgr_h->max_used_sw_slots = used_sw_slots;
@@ -432,6 +462,7 @@  int send_request(
 
 	/* STAT_PHASE_4: Push sequence */
 	START_CYCLE_COUNT();
+	enqueue_seq(cc_base, iv_seq, iv_seq_len);
 	enqueue_seq(cc_base, desc, len);
 	enqueue_seq(cc_base, &req_mgr_h->compl_desc, (is_dout ? 0 : 1));
 	END_CYCLE_COUNT(ssi_req->op_type, STAT_PHASE_4);