diff mbox series

crypto: caam/rng: Add support for PRNG

Message ID 20220304114123.3762649-1-meenakshi.aggarwal@nxp.com
State New
Headers show
Series crypto: caam/rng: Add support for PRNG | expand

Commit Message

Meenakshi Aggarwal March 4, 2022, 11:41 a.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.

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

Comments

kernel test robot March 8, 2022, 10:49 p.m. UTC | #1
Hi,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on herbert-cryptodev-2.6/master]
[also build test WARNING on herbert-crypto-2.6/master v5.17-rc7 next-20220308]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/meenakshi-aggarwal-nxp-com/crypto-caam-rng-Add-support-for-PRNG/20220307-142401
base:   https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
config: arm-multi_v7_defconfig (https://download.01.org/0day-ci/archive/20220309/202203090603.qhbToPLJ-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/bc27c1a2748de5052fb4c042d90fa1e0e4132043
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review meenakshi-aggarwal-nxp-com/crypto-caam-rng-Add-support-for-PRNG/20220307-142401
        git checkout bc27c1a2748de5052fb4c042d90fa1e0e4132043
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arm SHELL=/bin/bash drivers/crypto/caam/ drivers/rtc/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from drivers/crypto/caam/compat.h:18,
                    from drivers/crypto/caam/caamprng.c:11:
   drivers/crypto/caam/caamprng.c: In function 'caam_prng_seed':
>> drivers/crypto/caam/caamprng.c:156:46: warning: passing argument 2 of 'dma_map_single_attrs' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
     156 |         seed_dma = dma_map_single(ctx.jrdev, seed, slen, DMA_FROM_DEVICE);
         |                                              ^~~~
   include/linux/dma-mapping.h:406:60: note: in definition of macro 'dma_map_single'
     406 | #define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0)
         |                                                            ^
   include/linux/dma-mapping.h:322:73: note: expected 'void *' but argument is of type 'const u8 *' {aka 'const unsigned char *'}
     322 | static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
         |                                                                   ~~~~~~^~~


vim +156 drivers/crypto/caam/caamprng.c

  > 11	#include "compat.h"
    12	#include "regs.h"
    13	#include "intern.h"
    14	#include "desc_constr.h"
    15	#include "jr.h"
    16	#include "error.h"
    17	
    18	/*
    19	 * Length of used descriptors, see caam_init_desc()
    20	 */
    21	#define CAAM_PRNG_DESC_LEN (CAAM_CMD_SZ +				\
    22				    CAAM_CMD_SZ +				\
    23				    CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
    24	
    25	/* prng per-device context */
    26	struct caam_prng_ctx {
    27		struct device *jrdev;
    28		struct completion done;
    29	};
    30	
    31	struct caam_prng_alg {
    32		struct rng_alg rng;
    33		bool registered;
    34	};
    35	
    36	static void caam_prng_done(struct device *jrdev, u32 *desc, u32 err,
    37				  void *context)
    38	{
    39		struct caam_prng_ctx *jctx = context;
    40	
    41		if (err)
    42			caam_jr_strstatus(jrdev, err);
    43	
    44		complete(&jctx->done);
    45	}
    46	
    47	static u32 *caam_init_reseed_desc(u32 *desc, dma_addr_t seed_dma, u32 len)
    48	{
    49		init_job_desc(desc, 0);	/* + 1 cmd_sz */
    50		/* Generate random bytes: + 1 cmd_sz */
    51		append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
    52				OP_ALG_AS_FINALIZE | OP_ALG_AI_ON);
    53		/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
    54		append_load(desc, seed_dma, len, CLASS_1 | LDST_SRCDST_BYTE_CONTEXT);
    55	
    56		print_hex_dump_debug("prng reseed desc@: ", DUMP_PREFIX_ADDRESS,
    57				     16, 4, desc, desc_bytes(desc), 1);
    58	
    59		return desc;
    60	}
    61	
    62	static u32 *caam_init_prng_desc(u32 *desc, dma_addr_t dst_dma, u32 len)
    63	{
    64		init_job_desc(desc, 0);	/* + 1 cmd_sz */
    65		/* Generate random bytes: + 1 cmd_sz */
    66		append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG);
    67		/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
    68		append_fifo_store(desc, dst_dma,
    69				  len, FIFOST_TYPE_RNGSTORE);
    70	
    71		print_hex_dump_debug("prng job desc@: ", DUMP_PREFIX_ADDRESS,
    72				     16, 4, desc, desc_bytes(desc), 1);
    73	
    74		return desc;
    75	}
    76	
    77	static int caam_prng_generate(struct crypto_rng *tfm,
    78				     const u8 *src, unsigned int slen,
    79				     u8 *dst, unsigned int dlen)
    80	{
    81		struct caam_prng_ctx ctx;
    82		dma_addr_t dst_dma;
    83		u32 *desc;
    84		int ret;
    85	
    86		ctx.jrdev = caam_jr_alloc();
    87		ret = PTR_ERR_OR_ZERO(ctx.jrdev);
    88		if (ret) {
    89			pr_err("Job Ring Device allocation failed\n");
    90			return ret;
    91		}
    92	
    93		desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
    94		if (!desc) {
    95			caam_jr_free(ctx.jrdev);
    96			return -ENOMEM;
    97		}
    98	
    99		dst_dma = dma_map_single(ctx.jrdev, dst, dlen, DMA_FROM_DEVICE);
   100		if (dma_mapping_error(ctx.jrdev, dst_dma)) {
   101			dev_err(ctx.jrdev, "Failed to map destination buffer memory\n");
   102			ret = -ENOMEM;
   103			goto out;
   104		}
   105	
   106		init_completion(&ctx.done);
   107		ret = caam_jr_enqueue(ctx.jrdev,
   108				      caam_init_prng_desc(desc, dst_dma, dlen),
   109				      caam_prng_done, &ctx);
   110	
   111		if (ret == -EINPROGRESS) {
   112			wait_for_completion(&ctx.done);
   113			ret = 0;
   114		}
   115	
   116		dma_unmap_single(ctx.jrdev, dst_dma, dlen, DMA_FROM_DEVICE);
   117	
   118	out:
   119		kfree(desc);
   120		caam_jr_free(ctx.jrdev);
   121		return ret;
   122	}
   123	
   124	static void caam_prng_exit(struct crypto_tfm *tfm)
   125	{
   126	
   127		return;
   128	}
   129	
   130	static int caam_prng_init(struct crypto_tfm *tfm)
   131	{
   132		return 0;
   133	}
   134	
   135	static int caam_prng_seed(struct crypto_rng *tfm,
   136				 const u8 *seed, unsigned int slen)
   137	{
   138		struct caam_prng_ctx ctx;
   139		dma_addr_t seed_dma;
   140		u32 *desc;
   141		int ret;
   142	
   143		ctx.jrdev = caam_jr_alloc();
   144		ret = PTR_ERR_OR_ZERO(ctx.jrdev);
   145		if (ret) {
   146			pr_err("Job Ring Device allocation failed\n");
   147			return ret;
   148		}
   149	
   150		desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
   151		if (!desc) {
   152			caam_jr_free(ctx.jrdev);
   153			return -ENOMEM;
   154		}
   155	
 > 156		seed_dma = dma_map_single(ctx.jrdev, seed, slen, DMA_FROM_DEVICE);
   157		if (dma_mapping_error(ctx.jrdev, seed_dma)) {
   158			dev_err(ctx.jrdev, "Failed to map destination buffer memory\n");
   159			ret = -ENOMEM;
   160			goto out;
   161		}
   162	
   163		init_completion(&ctx.done);
   164		ret = caam_jr_enqueue(ctx.jrdev,
   165				      caam_init_reseed_desc(desc, seed_dma, slen),
   166				      caam_prng_done, &ctx);
   167	
   168		if (ret == -EINPROGRESS) {
   169			wait_for_completion(&ctx.done);
   170			ret = 0;
   171		}
   172	
   173		dma_unmap_single(ctx.jrdev, seed_dma, slen, DMA_FROM_DEVICE);
   174	
   175	out:
   176		kfree(desc);
   177		caam_jr_free(ctx.jrdev);
   178		return ret;
   179	}
   180	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Horia Geanta April 5, 2022, 12:31 p.m. UTC | #2
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 | #3
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..be0cf0e214f0
--- /dev/null
+++ b/drivers/crypto/caam/caamprng.c
@@ -0,0 +1,238 @@ 
+// 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;
+	int ret;
+
+	ctx.jrdev = caam_jr_alloc();
+	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
+	if (ret) {
+		pr_err("Job Ring Device allocation failed\n");
+		return ret;
+	}
+
+	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
+	if (!desc) {
+		caam_jr_free(ctx.jrdev);
+		return -ENOMEM;
+	}
+
+	dst_dma = dma_map_single(ctx.jrdev, dst, 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);
+
+out:
+	kfree(desc);
+	caam_jr_free(ctx.jrdev);
+	return ret;
+}
+
+static void caam_prng_exit(struct crypto_tfm *tfm)
+{
+
+	return;
+}
+
+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;
+	int ret;
+
+	ctx.jrdev = caam_jr_alloc();
+	ret = PTR_ERR_OR_ZERO(ctx.jrdev);
+	if (ret) {
+		pr_err("Job Ring Device allocation failed\n");
+		return ret;
+	}
+
+	desc = kzalloc(CAAM_PRNG_DESC_LEN, GFP_KERNEL | GFP_DMA);
+	if (!desc) {
+		caam_jr_free(ctx.jrdev);
+		return -ENOMEM;
+	}
+
+	seed_dma = dma_map_single(ctx.jrdev, seed, slen, DMA_FROM_DEVICE);
+	if (dma_mapping_error(ctx.jrdev, seed_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_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:
+	kfree(desc);
+	caam_jr_free(ctx.jrdev);
+	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 rng 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: