diff mbox series

[v2,1/1] crypto: caam/rng: Add support for PRNG

Message ID 20220316180149.4070622-2-meenakshi.aggarwal@nxp.com (mailing list archive)
State Changes Requested
Delegated to: Herbert Xu
Headers show
Series crypto: caam/rng: Add support for PRNG | expand

Commit Message

Meenakshi Aggarwal March 16, 2022, 6:01 p.m. UTC
From: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>

Add support for random number generation using PRNG
mode of CAAM and expose the interface through crypto API.

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>
---
 drivers/crypto/caam/Kconfig    |   9 ++
 drivers/crypto/caam/Makefile   |   1 +
 drivers/crypto/caam/caamprng.c | 256 +++++++++++++++++++++++++++++++++
 drivers/crypto/caam/desc.h     |   1 +
 drivers/crypto/caam/intern.h   |  15 ++
 drivers/crypto/caam/jr.c       |   2 +
 6 files changed, 284 insertions(+)
 create mode 100644 drivers/crypto/caam/caamprng.c

Comments

Horia Geanta April 5, 2022, 12:31 p.m. UTC | #1
On 3/16/2022 8:02 PM, Meenakshi Aggarwal wrote:
> From: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>
> 
> Add support for random number generation using PRNG
> mode of CAAM and expose the interface through crypto API.
> 
According to the RM, the HW implementation of the DRBG follows
NIST SP 800-90A specification for DRBG_Hash SHA-256 function
(https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf).
This should be mentioned in the commit message at minimum.

> Reported-by: kernel test robot <lkp@intel.com>
This isn't required and doesn't make sense once you've squashed the fix.

> +/* prng per-device context */
> +struct caam_prng_ctx {
> +	struct device *jrdev;
jrdev doesn't have to be saved in this struct, it's lifetime is very limited.

> +	struct completion done;
> +};
> +
> +struct caam_prng_alg {
> +	struct rng_alg rng;
> +	bool registered;
> +};
> +
> +static void caam_prng_done(struct device *jrdev, u32 *desc, u32 err,
> +			  void *context)
> +{
> +	struct caam_prng_ctx *jctx = context;
> +
> +	if (err)
> +		caam_jr_strstatus(jrdev, err);
The error returned by caam_jr_strstatus() should be propagated back to
who initially enqueued the corresponding.
For this purpose, struct caam_prng_ctx could be extended with an "err" member.

> +
> +	complete(&jctx->done);
> +}
> +

> +static int caam_prng_generate(struct crypto_rng *tfm,
> +			     const u8 *src, unsigned int slen,
> +			     u8 *dst, unsigned int dlen)
> +{
> +	struct caam_prng_ctx ctx;
> +	dma_addr_t dst_dma;
> +	u32 *desc;
> +	u8 *buf;
> +	int ret;
> +
> +	buf = kzalloc(dlen, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ctx.jrdev = caam_jr_alloc();
> +	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
> +	if (ret) {
> +		pr_err("Job Ring Device allocation failed\n");
> +		kfree(buf);
> +		return ret;
> +	}
> +
> +	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
> +	if (!desc) {
> +		caam_jr_free(ctx.jrdev);
> +		kfree(buf);
> +		return -ENOMEM;
Please fix the error handling to reuse the free code
at the end of the function.

> +	}
> +
> +	dst_dma = dma_map_single(ctx.jrdev, buf, dlen, DMA_FROM_DEVICE);
> +	if (dma_mapping_error(ctx.jrdev, dst_dma)) {
> +		dev_err(ctx.jrdev, "Failed to map destination buffer memory\n");
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_completion(&ctx.done);
> +	ret = caam_jr_enqueue(ctx.jrdev,
> +			      caam_init_prng_desc(desc, dst_dma, dlen),
> +			      caam_prng_done, &ctx);
> +
> +	if (ret == -EINPROGRESS) {
> +		wait_for_completion(&ctx.done);
> +		ret = 0;
> +	}
> +
> +	dma_unmap_single(ctx.jrdev, dst_dma, dlen, DMA_FROM_DEVICE);
> +
> +	memcpy(dst, buf, dlen);
I am a bit worried wrt. performance, considering the memory allocations
and the memcpy on the hotpath.

Previous version of CAAM PRNG driver was getting ~ 200 MB/s on LS
and 50 MB/s on i.MX8.

How does the current version compare?
Given that there's no prefetch buffer and there are memory allocation, copy
operations on the hotpath, I'd expect a hefty penalty.

> +out:
> +	caam_jr_free(ctx.jrdev);
> +	kfree(desc);
> +	kfree(buf);
> +	return ret;
> +}
> +
> +static void caam_prng_exit(struct crypto_tfm *tfm) {}
> +
> +static int caam_prng_init(struct crypto_tfm *tfm)
> +{
> +	return 0;
> +}
> +
> +static int caam_prng_seed(struct crypto_rng *tfm,
> +			 const u8 *seed, unsigned int slen)
> +{
> +	struct caam_prng_ctx ctx;
> +	dma_addr_t seed_dma;
> +	u32 *desc;
> +	u8 *buf;
> +	int ret = 0;
> +
> +	if (seed == NULL) {
> +		pr_err("Seed not provided\n");
> +		return ret;
> +	}
> +
> +	buf = kzalloc(slen, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ctx.jrdev = caam_jr_alloc();
> +	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
> +	if (ret) {
> +		pr_err("Job Ring Device allocation failed\n");
> +		kfree(buf);
> +		return ret;
> +	}
> +
> +	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
> +	if (!desc) {
> +		caam_jr_free(ctx.jrdev);
> +		kfree(buf);
> +		return -ENOMEM;
Same here, error handling at the end of the function should be reused.

> +	}
> +
> +	memcpy(buf, seed, slen);
> +
> +	seed_dma = dma_map_single(ctx.jrdev, buf, slen, DMA_FROM_DEVICE);
> +	if (dma_mapping_error(ctx.jrdev, seed_dma)) {
> +		dev_err(ctx.jrdev, "Failed to map seed buffer memory\n");
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_completion(&ctx.done);
> +	ret = caam_jr_enqueue(ctx.jrdev,
> +			      caam_init_reseed_desc(desc, seed_dma, slen),
> +			      caam_prng_done, &ctx);
> +
> +	if (ret == -EINPROGRESS) {
> +		wait_for_completion(&ctx.done);
> +		ret = 0;
> +	}
> +
> +	dma_unmap_single(ctx.jrdev, seed_dma, slen, DMA_FROM_DEVICE);
> +
> +out:
> +	caam_jr_free(ctx.jrdev);
> +	kfree(desc);
> +	kfree(buf);
> +	return ret;
> +}
> +
> +static struct caam_prng_alg caam_prng_alg = {
> +	.rng = {
> +		.generate = caam_prng_generate,
> +		.seed = caam_prng_seed,
> +		.seedsize = 32,
seedsize should be set to 0, HW does not need an externally-provided seed
since it fetches it internally from TRNG.

> +int caam_prng_register(struct device *ctrldev)
> +{
> +	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
> +	u32 rng_inst;
> +	int ret = 0;
> +
> +	/* Check for available RNG blocks before registration */
> +	if (priv->era < 10)
> +		rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
> +			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
> +	else
> +		rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK;
> +
> +	if (!rng_inst) {
> +		dev_dbg(ctrldev, "RNG block is not available... skipping registering algorithm\n");
> +		return ret;
> +	}
> +
> +	ret = crypto_register_rng(&caam_prng_alg.rng);
> +	if (ret) {
> +		dev_err(ctrldev,
> +			"couldn't register rng crypto alg: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	caam_prng_alg.registered = true;
> +
> +	dev_info(ctrldev,
> +		 "rng crypto API alg registered %s\n", caam_prng_alg.rng.base.cra_name);
driver_name should be printed, it's more specific / unique.

> diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
> index 7f2b1101f567..11849362f912 100644
> --- a/drivers/crypto/caam/jr.c
> +++ b/drivers/crypto/caam/jr.c
> @@ -39,6 +39,7 @@ static void register_algs(struct caam_drv_private_jr *jrpriv,
>  	caam_algapi_hash_init(dev);
>  	caam_pkc_init(dev);
>  	jrpriv->hwrng = !caam_rng_init(dev);
> +	caam_prng_register(dev);
>  	caam_qi_algapi_init(dev);
>  
>  algs_unlock:
> @@ -56,6 +57,7 @@ static void unregister_algs(void)
>  
>  	caam_pkc_exit();
>  	caam_algapi_hash_exit();
> +	caam_prng_unregister(NULL);
Unregistering order should be the reverse order of registering.

Horia
Meenakshi Aggarwal April 20, 2022, 5:57 a.m. UTC | #2
HI,

Thanks for the review.

Will incorporate all suggested changes in v3.

On i.MX8, performance is coming  ~ 42 MB/s

Thanks,
Meenakshi
> -----Original Message-----
> From: Horia Geanta <horia.geanta@nxp.com>
> Sent: Tuesday, April 5, 2022 6:02 PM
> To: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>; Pankaj Gupta
> <pankaj.gupta@nxp.com>; Gaurav Jain <gaurav.jain@nxp.com>; Varun Sethi
> <V.Sethi@nxp.com>; Herbert Xu <herbert@gondor.apana.org.au>; David S .
> Miller <davem@davemloft.net>
> Cc: linux-crypto@vger.kernel.org; linux-kernel@vger.kernel.org; dl-linux-imx
> <linux-imx@nxp.com>; kernel test robot <lkp@intel.com>
> Subject: Re: [PATCH v2 1/1] crypto: caam/rng: Add support for PRNG
> 
> On 3/16/2022 8:02 PM, Meenakshi Aggarwal wrote:
> > From: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>
> >
> > Add support for random number generation using PRNG mode of CAAM and
> > expose the interface through crypto API.
> >
> According to the RM, the HW implementation of the DRBG follows NIST SP 800-
> 90A specification for DRBG_Hash SHA-256 function
> (https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf).
> This should be mentioned in the commit message at minimum.
> 
> > Reported-by: kernel test robot <lkp@intel.com>
> This isn't required and doesn't make sense once you've squashed the fix.
> 
> > +/* prng per-device context */
> > +struct caam_prng_ctx {
> > +	struct device *jrdev;
> jrdev doesn't have to be saved in this struct, it's lifetime is very limited.
> 
> > +	struct completion done;
> > +};
> > +
> > +struct caam_prng_alg {
> > +	struct rng_alg rng;
> > +	bool registered;
> > +};
> > +
> > +static void caam_prng_done(struct device *jrdev, u32 *desc, u32 err,
> > +			  void *context)
> > +{
> > +	struct caam_prng_ctx *jctx = context;
> > +
> > +	if (err)
> > +		caam_jr_strstatus(jrdev, err);
> The error returned by caam_jr_strstatus() should be propagated back to who
> initially enqueued the corresponding.
> For this purpose, struct caam_prng_ctx could be extended with an "err" member.
> 
> > +
> > +	complete(&jctx->done);
> > +}
> > +
> 
> > +static int caam_prng_generate(struct crypto_rng *tfm,
> > +			     const u8 *src, unsigned int slen,
> > +			     u8 *dst, unsigned int dlen)
> > +{
> > +	struct caam_prng_ctx ctx;
> > +	dma_addr_t dst_dma;
> > +	u32 *desc;
> > +	u8 *buf;
> > +	int ret;
> > +
> > +	buf = kzalloc(dlen, GFP_KERNEL);
> > +	if (!buf)
> > +		return -ENOMEM;
> > +
> > +	ctx.jrdev = caam_jr_alloc();
> > +	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
> > +	if (ret) {
> > +		pr_err("Job Ring Device allocation failed\n");
> > +		kfree(buf);
> > +		return ret;
> > +	}
> > +
> > +	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
> > +	if (!desc) {
> > +		caam_jr_free(ctx.jrdev);
> > +		kfree(buf);
> > +		return -ENOMEM;
> Please fix the error handling to reuse the free code at the end of the function.
> 
> > +	}
> > +
> > +	dst_dma = dma_map_single(ctx.jrdev, buf, dlen, DMA_FROM_DEVICE);
> > +	if (dma_mapping_error(ctx.jrdev, dst_dma)) {
> > +		dev_err(ctx.jrdev, "Failed to map destination buffer memory\n");
> > +		ret = -ENOMEM;
> > +		goto out;
> > +	}
> > +
> > +	init_completion(&ctx.done);
> > +	ret = caam_jr_enqueue(ctx.jrdev,
> > +			      caam_init_prng_desc(desc, dst_dma, dlen),
> > +			      caam_prng_done, &ctx);
> > +
> > +	if (ret == -EINPROGRESS) {
> > +		wait_for_completion(&ctx.done);
> > +		ret = 0;
> > +	}
> > +
> > +	dma_unmap_single(ctx.jrdev, dst_dma, dlen, DMA_FROM_DEVICE);
> > +
> > +	memcpy(dst, buf, dlen);
> I am a bit worried wrt. performance, considering the memory allocations and
> the memcpy on the hotpath.
> 
> Previous version of CAAM PRNG driver was getting ~ 200 MB/s on LS and 50
> MB/s on i.MX8.
> 
> How does the current version compare?
> Given that there's no prefetch buffer and there are memory allocation, copy
> operations on the hotpath, I'd expect a hefty penalty.
> 
> > +out:
> > +	caam_jr_free(ctx.jrdev);
> > +	kfree(desc);
> > +	kfree(buf);
> > +	return ret;
> > +}
> > +
> > +static void caam_prng_exit(struct crypto_tfm *tfm) {}
> > +
> > +static int caam_prng_init(struct crypto_tfm *tfm) {
> > +	return 0;
> > +}
> > +
> > +static int caam_prng_seed(struct crypto_rng *tfm,
> > +			 const u8 *seed, unsigned int slen) {
> > +	struct caam_prng_ctx ctx;
> > +	dma_addr_t seed_dma;
> > +	u32 *desc;
> > +	u8 *buf;
> > +	int ret = 0;
> > +
> > +	if (seed == NULL) {
> > +		pr_err("Seed not provided\n");
> > +		return ret;
> > +	}
> > +
> > +	buf = kzalloc(slen, GFP_KERNEL);
> > +	if (!buf)
> > +		return -ENOMEM;
> > +
> > +	ctx.jrdev = caam_jr_alloc();
> > +	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
> > +	if (ret) {
> > +		pr_err("Job Ring Device allocation failed\n");
> > +		kfree(buf);
> > +		return ret;
> > +	}
> > +
> > +	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
> > +	if (!desc) {
> > +		caam_jr_free(ctx.jrdev);
> > +		kfree(buf);
> > +		return -ENOMEM;
> Same here, error handling at the end of the function should be reused.
> 
> > +	}
> > +
> > +	memcpy(buf, seed, slen);
> > +
> > +	seed_dma = dma_map_single(ctx.jrdev, buf, slen, DMA_FROM_DEVICE);
> > +	if (dma_mapping_error(ctx.jrdev, seed_dma)) {
> > +		dev_err(ctx.jrdev, "Failed to map seed buffer memory\n");
> > +		ret = -ENOMEM;
> > +		goto out;
> > +	}
> > +
> > +	init_completion(&ctx.done);
> > +	ret = caam_jr_enqueue(ctx.jrdev,
> > +			      caam_init_reseed_desc(desc, seed_dma, slen),
> > +			      caam_prng_done, &ctx);
> > +
> > +	if (ret == -EINPROGRESS) {
> > +		wait_for_completion(&ctx.done);
> > +		ret = 0;
> > +	}
> > +
> > +	dma_unmap_single(ctx.jrdev, seed_dma, slen, DMA_FROM_DEVICE);
> > +
> > +out:
> > +	caam_jr_free(ctx.jrdev);
> > +	kfree(desc);
> > +	kfree(buf);
> > +	return ret;
> > +}
> > +
> > +static struct caam_prng_alg caam_prng_alg = {
> > +	.rng = {
> > +		.generate = caam_prng_generate,
> > +		.seed = caam_prng_seed,
> > +		.seedsize = 32,
> seedsize should be set to 0, HW does not need an externally-provided seed since
> it fetches it internally from TRNG.
> 
> > +int caam_prng_register(struct device *ctrldev) {
> > +	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
> > +	u32 rng_inst;
> > +	int ret = 0;
> > +
> > +	/* Check for available RNG blocks before registration */
> > +	if (priv->era < 10)
> > +		rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
> > +			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
> > +	else
> > +		rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) &
> CHA_VER_NUM_MASK;
> > +
> > +	if (!rng_inst) {
> > +		dev_dbg(ctrldev, "RNG block is not available... skipping
> registering algorithm\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = crypto_register_rng(&caam_prng_alg.rng);
> > +	if (ret) {
> > +		dev_err(ctrldev,
> > +			"couldn't register rng crypto alg: %d\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	caam_prng_alg.registered = true;
> > +
> > +	dev_info(ctrldev,
> > +		 "rng crypto API alg registered %s\n",
> > +caam_prng_alg.rng.base.cra_name);
> driver_name should be printed, it's more specific / unique.
> 
> > diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index
> > 7f2b1101f567..11849362f912 100644
> > --- a/drivers/crypto/caam/jr.c
> > +++ b/drivers/crypto/caam/jr.c
> > @@ -39,6 +39,7 @@ static void register_algs(struct caam_drv_private_jr
> *jrpriv,
> >  	caam_algapi_hash_init(dev);
> >  	caam_pkc_init(dev);
> >  	jrpriv->hwrng = !caam_rng_init(dev);
> > +	caam_prng_register(dev);
> >  	caam_qi_algapi_init(dev);
> >
> >  algs_unlock:
> > @@ -56,6 +57,7 @@ static void unregister_algs(void)
> >
> >  	caam_pkc_exit();
> >  	caam_algapi_hash_exit();
> > +	caam_prng_unregister(NULL);
> Unregistering order should be the reverse order of registering.
> 
> Horia
diff mbox series

Patch

diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index 84ea7cba5ee5..d94250348b32 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -151,6 +151,15 @@  config CRYPTO_DEV_FSL_CAAM_RNG_API
 	  Selecting this will register the SEC4 hardware rng to
 	  the hw_random API for supplying the kernel entropy pool.
 
+
+config CRYPTO_DEV_FSL_CAAM_PRNG_API
+	bool "Register Pseudo random number generation implementation with Crypto API"
+	default y
+	select CRYPTO_RNG
+	help
+	  Selecting this will register the SEC hardware prng to
+	  the Crypto API.
+
 endif # CRYPTO_DEV_FSL_CAAM_JR
 
 endif # CRYPTO_DEV_FSL_CAAM
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index 3570286eb9ce..59961a0bbfe4 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -21,6 +21,7 @@  caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += caamalg_qi.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API) += caampkc.o pkc_desc.o
+caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PRNG_API) += caamprng.o
 
 caam-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += qi.o
 ifneq ($(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI),)
diff --git a/drivers/crypto/caam/caamprng.c b/drivers/crypto/caam/caamprng.c
new file mode 100644
index 000000000000..0524017a5e73
--- /dev/null
+++ b/drivers/crypto/caam/caamprng.c
@@ -0,0 +1,256 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver to expose SEC4 PRNG via crypto RNG API
+ *
+ * Copyright 2022 NXP
+ *
+ */
+
+#include <linux/completion.h>
+#include <crypto/internal/rng.h>
+#include "compat.h"
+#include "regs.h"
+#include "intern.h"
+#include "desc_constr.h"
+#include "jr.h"
+#include "error.h"
+
+/*
+ * Length of used descriptors, see caam_init_desc()
+ */
+#define CAAM_PRNG_DESC_LEN (CAAM_CMD_SZ +				\
+			    CAAM_CMD_SZ +				\
+			    CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
+
+/* prng per-device context */
+struct caam_prng_ctx {
+	struct device *jrdev;
+	struct completion done;
+};
+
+struct caam_prng_alg {
+	struct rng_alg rng;
+	bool registered;
+};
+
+static void caam_prng_done(struct device *jrdev, u32 *desc, u32 err,
+			  void *context)
+{
+	struct caam_prng_ctx *jctx = context;
+
+	if (err)
+		caam_jr_strstatus(jrdev, err);
+
+	complete(&jctx->done);
+}
+
+static u32 *caam_init_reseed_desc(u32 *desc, dma_addr_t seed_dma, u32 len)
+{
+	init_job_desc(desc, 0);	/* + 1 cmd_sz */
+	/* Generate random bytes: + 1 cmd_sz */
+	append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
+			OP_ALG_AS_FINALIZE | OP_ALG_AI_ON);
+	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
+	append_load(desc, seed_dma, len, CLASS_1 | LDST_SRCDST_BYTE_CONTEXT);
+
+	print_hex_dump_debug("prng reseed desc@: ", DUMP_PREFIX_ADDRESS,
+			     16, 4, desc, desc_bytes(desc), 1);
+
+	return desc;
+}
+
+static u32 *caam_init_prng_desc(u32 *desc, dma_addr_t dst_dma, u32 len)
+{
+	init_job_desc(desc, 0);	/* + 1 cmd_sz */
+	/* Generate random bytes: + 1 cmd_sz */
+	append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG);
+	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
+	append_fifo_store(desc, dst_dma,
+			  len, FIFOST_TYPE_RNGSTORE);
+
+	print_hex_dump_debug("prng job desc@: ", DUMP_PREFIX_ADDRESS,
+			     16, 4, desc, desc_bytes(desc), 1);
+
+	return desc;
+}
+
+static int caam_prng_generate(struct crypto_rng *tfm,
+			     const u8 *src, unsigned int slen,
+			     u8 *dst, unsigned int dlen)
+{
+	struct caam_prng_ctx ctx;
+	dma_addr_t dst_dma;
+	u32 *desc;
+	u8 *buf;
+	int ret;
+
+	buf = kzalloc(dlen, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ctx.jrdev = caam_jr_alloc();
+	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
+	if (ret) {
+		pr_err("Job Ring Device allocation failed\n");
+		kfree(buf);
+		return ret;
+	}
+
+	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
+	if (!desc) {
+		caam_jr_free(ctx.jrdev);
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	dst_dma = dma_map_single(ctx.jrdev, buf, dlen, DMA_FROM_DEVICE);
+	if (dma_mapping_error(ctx.jrdev, dst_dma)) {
+		dev_err(ctx.jrdev, "Failed to map destination buffer memory\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	init_completion(&ctx.done);
+	ret = caam_jr_enqueue(ctx.jrdev,
+			      caam_init_prng_desc(desc, dst_dma, dlen),
+			      caam_prng_done, &ctx);
+
+	if (ret == -EINPROGRESS) {
+		wait_for_completion(&ctx.done);
+		ret = 0;
+	}
+
+	dma_unmap_single(ctx.jrdev, dst_dma, dlen, DMA_FROM_DEVICE);
+
+	memcpy(dst, buf, dlen);
+out:
+	caam_jr_free(ctx.jrdev);
+	kfree(desc);
+	kfree(buf);
+	return ret;
+}
+
+static void caam_prng_exit(struct crypto_tfm *tfm) {}
+
+static int caam_prng_init(struct crypto_tfm *tfm)
+{
+	return 0;
+}
+
+static int caam_prng_seed(struct crypto_rng *tfm,
+			 const u8 *seed, unsigned int slen)
+{
+	struct caam_prng_ctx ctx;
+	dma_addr_t seed_dma;
+	u32 *desc;
+	u8 *buf;
+	int ret = 0;
+
+	if (seed == NULL) {
+		pr_err("Seed not provided\n");
+		return ret;
+	}
+
+	buf = kzalloc(slen, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ctx.jrdev = caam_jr_alloc();
+	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
+	if (ret) {
+		pr_err("Job Ring Device allocation failed\n");
+		kfree(buf);
+		return ret;
+	}
+
+	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
+	if (!desc) {
+		caam_jr_free(ctx.jrdev);
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, seed, slen);
+
+	seed_dma = dma_map_single(ctx.jrdev, buf, slen, DMA_FROM_DEVICE);
+	if (dma_mapping_error(ctx.jrdev, seed_dma)) {
+		dev_err(ctx.jrdev, "Failed to map seed buffer memory\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	init_completion(&ctx.done);
+	ret = caam_jr_enqueue(ctx.jrdev,
+			      caam_init_reseed_desc(desc, seed_dma, slen),
+			      caam_prng_done, &ctx);
+
+	if (ret == -EINPROGRESS) {
+		wait_for_completion(&ctx.done);
+		ret = 0;
+	}
+
+	dma_unmap_single(ctx.jrdev, seed_dma, slen, DMA_FROM_DEVICE);
+
+out:
+	caam_jr_free(ctx.jrdev);
+	kfree(desc);
+	kfree(buf);
+	return ret;
+}
+
+static struct caam_prng_alg caam_prng_alg = {
+	.rng = {
+		.generate = caam_prng_generate,
+		.seed = caam_prng_seed,
+		.seedsize = 32,
+		.base = {
+			.cra_name = "stdrng",
+			.cra_driver_name = "prng-caam",
+			.cra_priority = 500,
+			.cra_ctxsize = sizeof(struct caam_prng_ctx),
+			.cra_module = THIS_MODULE,
+			.cra_init = caam_prng_init,
+			.cra_exit = caam_prng_exit,
+		},
+	}
+};
+
+void caam_prng_unregister(void *data)
+{
+	if (caam_prng_alg.registered)
+		crypto_unregister_rng(&caam_prng_alg.rng);
+}
+
+int caam_prng_register(struct device *ctrldev)
+{
+	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
+	u32 rng_inst;
+	int ret = 0;
+
+	/* Check for available RNG blocks before registration */
+	if (priv->era < 10)
+		rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
+			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
+	else
+		rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK;
+
+	if (!rng_inst) {
+		dev_dbg(ctrldev, "RNG block is not available... skipping registering algorithm\n");
+		return ret;
+	}
+
+	ret = crypto_register_rng(&caam_prng_alg.rng);
+	if (ret) {
+		dev_err(ctrldev,
+			"couldn't register rng crypto alg: %d\n",
+			ret);
+		return ret;
+	}
+
+	caam_prng_alg.registered = true;
+
+	dev_info(ctrldev,
+		 "rng crypto API alg registered %s\n", caam_prng_alg.rng.base.cra_name);
+
+	return 0;
+}
diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h
index e13470901586..33db8ed2b49e 100644
--- a/drivers/crypto/caam/desc.h
+++ b/drivers/crypto/caam/desc.h
@@ -1255,6 +1255,7 @@ 
 #define OP_ALG_ICV_ON		(1 << OP_ALG_ICV_SHIFT)
 
 #define OP_ALG_PR_ON		BIT(1)
+#define OP_ALG_AI_ON		BIT(11)
 
 #define OP_ALG_DIR_SHIFT	0
 #define OP_ALG_DIR_MASK		1
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index 7d45b21bd55a..c2f51365df1b 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -185,6 +185,21 @@  static inline void caam_rng_exit(struct device *dev) {}
 
 #endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API */
 
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_PRNG_API
+
+int caam_prng_register(struct device *dev);
+void caam_prng_unregister(void *data);
+
+#else
+
+static inline int caam_prng_register(struct device *dev)
+{
+	return 0;
+}
+
+static inline void caam_prng_unregister(void *data) {}
+#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_PRNG_API */
+
 #ifdef CONFIG_CAAM_QI
 
 int caam_qi_algapi_init(struct device *dev);
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index 7f2b1101f567..11849362f912 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -39,6 +39,7 @@  static void register_algs(struct caam_drv_private_jr *jrpriv,
 	caam_algapi_hash_init(dev);
 	caam_pkc_init(dev);
 	jrpriv->hwrng = !caam_rng_init(dev);
+	caam_prng_register(dev);
 	caam_qi_algapi_init(dev);
 
 algs_unlock:
@@ -56,6 +57,7 @@  static void unregister_algs(void)
 
 	caam_pkc_exit();
 	caam_algapi_hash_exit();
+	caam_prng_unregister(NULL);
 	caam_algapi_exit();
 
 algs_unlock: