diff options
Diffstat (limited to 'drivers/crypto/caam/caamrng.c')
| -rw-r--r-- | drivers/crypto/caam/caamrng.c | 405 | 
1 files changed, 154 insertions, 251 deletions
| diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index e8baacaabe07..77d048dfe5d0 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -7,35 +7,12 @@   *   * Based on caamalg.c crypto API driver.   * - * relationship between job descriptors to shared descriptors: - * - * ---------------                     -------------- - * | JobDesc #0  |-------------------->| ShareDesc  | - * | *(buffer 0) |      |------------->| (generate) | - * ---------------      |              | (move)     | - *                      |              | (store)    | - * ---------------      |              -------------- - * | JobDesc #1  |------| - * | *(buffer 1) | - * --------------- - * - * A job desc looks like this: - * - * --------------------- - * | Header            | - * | ShareDesc Pointer | - * | SEQ_OUT_PTR       | - * | (output buffer)   | - * --------------------- - * - * The SharedDesc never changes, and each job descriptor points to one of two - * buffers for each device, from which the data will be copied into the - * requested destination   */  #include <linux/hw_random.h>  #include <linux/completion.h>  #include <linux/atomic.h> +#include <linux/kfifo.h>  #include "compat.h" @@ -45,278 +22,205 @@  #include "jr.h"  #include "error.h" +#define CAAM_RNG_MAX_FIFO_STORE_SIZE	16 +  /* - * Maximum buffer size: maximum number of random, cache-aligned bytes that - * will be generated and moved to seq out ptr (extlen not allowed) + * Length of used descriptors, see caam_init_desc()   */ -#define RN_BUF_SIZE			(0xffff / L1_CACHE_BYTES * \ -					 L1_CACHE_BYTES) - -/* length of descriptors */ -#define DESC_JOB_O_LEN			(CAAM_CMD_SZ * 2 + CAAM_PTR_SZ_MAX * 2) -#define DESC_RNG_LEN			(3 * CAAM_CMD_SZ) - -/* Buffer, its dma address and lock */ -struct buf_data { -	u8 buf[RN_BUF_SIZE] ____cacheline_aligned; -	dma_addr_t addr; -	struct completion filled; -	u32 hw_desc[DESC_JOB_O_LEN]; -#define BUF_NOT_EMPTY 0 -#define BUF_EMPTY 1 -#define BUF_PENDING 2  /* Empty, but with job pending --don't submit another */ -	atomic_t empty; -}; +#define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ +				\ +			   CAAM_CMD_SZ +				\ +			   CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)  /* rng per-device context */  struct caam_rng_ctx { +	struct hwrng rng;  	struct device *jrdev; -	dma_addr_t sh_desc_dma; -	u32 sh_desc[DESC_RNG_LEN]; -	unsigned int cur_buf_idx; -	int current_buf; -	struct buf_data bufs[2]; +	struct device *ctrldev; +	void *desc_async; +	void *desc_sync; +	struct work_struct worker; +	struct kfifo fifo;  }; -static struct caam_rng_ctx *rng_ctx; - -/* - * Variable used to avoid double free of resources in case - * algorithm registration was unsuccessful - */ -static bool init_done; - -static inline void rng_unmap_buf(struct device *jrdev, struct buf_data *bd) -{ -	if (bd->addr) -		dma_unmap_single(jrdev, bd->addr, RN_BUF_SIZE, -				 DMA_FROM_DEVICE); -} +struct caam_rng_job_ctx { +	struct completion *done; +	int *err; +}; -static inline void rng_unmap_ctx(struct caam_rng_ctx *ctx) +static struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r)  { -	struct device *jrdev = ctx->jrdev; - -	if (ctx->sh_desc_dma) -		dma_unmap_single(jrdev, ctx->sh_desc_dma, -				 desc_bytes(ctx->sh_desc), DMA_TO_DEVICE); -	rng_unmap_buf(jrdev, &ctx->bufs[0]); -	rng_unmap_buf(jrdev, &ctx->bufs[1]); +	return (struct caam_rng_ctx *)r->priv;  } -static void rng_done(struct device *jrdev, u32 *desc, u32 err, void *context) +static void caam_rng_done(struct device *jrdev, u32 *desc, u32 err, +			  void *context)  { -	struct buf_data *bd; - -	bd = container_of(desc, struct buf_data, hw_desc[0]); +	struct caam_rng_job_ctx *jctx = context;  	if (err) -		caam_jr_strstatus(jrdev, err); +		*jctx->err = caam_jr_strstatus(jrdev, err); -	atomic_set(&bd->empty, BUF_NOT_EMPTY); -	complete(&bd->filled); - -	/* Buffer refilled, invalidate cache */ -	dma_sync_single_for_cpu(jrdev, bd->addr, RN_BUF_SIZE, DMA_FROM_DEVICE); - -	print_hex_dump_debug("rng refreshed buf@: ", DUMP_PREFIX_ADDRESS, 16, 4, -			     bd->buf, RN_BUF_SIZE, 1); +	complete(jctx->done);  } -static inline int submit_job(struct caam_rng_ctx *ctx, int to_current) +static u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma)  { -	struct buf_data *bd = &ctx->bufs[!(to_current ^ ctx->current_buf)]; -	struct device *jrdev = ctx->jrdev; -	u32 *desc = bd->hw_desc; -	int err; - -	dev_dbg(jrdev, "submitting job %d\n", !(to_current ^ ctx->current_buf)); -	init_completion(&bd->filled); -	err = caam_jr_enqueue(jrdev, desc, rng_done, ctx); -	if (err) -		complete(&bd->filled); /* don't wait on failed job*/ -	else -		atomic_inc(&bd->empty); /* note if pending */ - -	return err; +	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 | +			 OP_ALG_PR_ON); +	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */ +	append_fifo_store(desc, dst_dma, +			  CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE); + +	print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS, +			     16, 4, desc, desc_bytes(desc), 1); + +	return desc;  } -static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait) +static int caam_rng_read_one(struct device *jrdev, +			     void *dst, int len, +			     void *desc, +			     struct completion *done)  { -	struct caam_rng_ctx *ctx = rng_ctx; -	struct buf_data *bd = &ctx->bufs[ctx->current_buf]; -	int next_buf_idx, copied_idx; -	int err; - -	if (atomic_read(&bd->empty)) { -		/* try to submit job if there wasn't one */ -		if (atomic_read(&bd->empty) == BUF_EMPTY) { -			err = submit_job(ctx, 1); -			/* if can't submit job, can't even wait */ -			if (err) -				return 0; -		} -		/* no immediate data, so exit if not waiting */ -		if (!wait) -			return 0; - -		/* waiting for pending job */ -		if (atomic_read(&bd->empty)) -			wait_for_completion(&bd->filled); +	dma_addr_t dst_dma; +	int err, ret = 0; +	struct caam_rng_job_ctx jctx = { +		.done = done, +		.err  = &ret, +	}; + +	len = CAAM_RNG_MAX_FIFO_STORE_SIZE; + +	dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE); +	if (dma_mapping_error(jrdev, dst_dma)) { +		dev_err(jrdev, "unable to map destination memory\n"); +		return -ENOMEM;  	} -	next_buf_idx = ctx->cur_buf_idx + max; -	dev_dbg(ctx->jrdev, "%s: start reading at buffer %d, idx %d\n", -		 __func__, ctx->current_buf, ctx->cur_buf_idx); - -	/* if enough data in current buffer */ -	if (next_buf_idx < RN_BUF_SIZE) { -		memcpy(data, bd->buf + ctx->cur_buf_idx, max); -		ctx->cur_buf_idx = next_buf_idx; -		return max; +	init_completion(done); +	err = caam_jr_enqueue(jrdev, +			      caam_init_desc(desc, dst_dma), +			      caam_rng_done, &jctx); +	if (err == -EINPROGRESS) { +		wait_for_completion(done); +		err = 0;  	} -	/* else, copy what's left... */ -	copied_idx = RN_BUF_SIZE - ctx->cur_buf_idx; -	memcpy(data, bd->buf + ctx->cur_buf_idx, copied_idx); -	ctx->cur_buf_idx = 0; -	atomic_set(&bd->empty, BUF_EMPTY); - -	/* ...refill... */ -	submit_job(ctx, 1); +	dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE); -	/* and use next buffer */ -	ctx->current_buf = !ctx->current_buf; -	dev_dbg(ctx->jrdev, "switched to buffer %d\n", ctx->current_buf); - -	/* since there already is some data read, don't wait */ -	return copied_idx + caam_read(rng, data + copied_idx, -				      max - copied_idx, false); +	return err ?: (ret ?: len);  } -static inline int rng_create_sh_desc(struct caam_rng_ctx *ctx) +static void caam_rng_fill_async(struct caam_rng_ctx *ctx)  { -	struct device *jrdev = ctx->jrdev; -	u32 *desc = ctx->sh_desc; - -	init_sh_desc(desc, HDR_SHARE_SERIAL); - -	/* Generate random bytes */ -	append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG); - -	/* Store bytes */ -	append_seq_fifo_store(desc, RN_BUF_SIZE, FIFOST_TYPE_RNGSTORE); +	struct scatterlist sg[1]; +	struct completion done; +	int len, nents; + +	sg_init_table(sg, ARRAY_SIZE(sg)); +	nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg), +				     CAAM_RNG_MAX_FIFO_STORE_SIZE); +	if (!nents) +		return; -	ctx->sh_desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), -					  DMA_TO_DEVICE); -	if (dma_mapping_error(jrdev, ctx->sh_desc_dma)) { -		dev_err(jrdev, "unable to map shared descriptor\n"); -		return -ENOMEM; -	} +	len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]), +				sg[0].length, +				ctx->desc_async, +				&done); +	if (len < 0) +		return; -	print_hex_dump_debug("rng shdesc@: ", DUMP_PREFIX_ADDRESS, 16, 4, -			     desc, desc_bytes(desc), 1); +	kfifo_dma_in_finish(&ctx->fifo, len); +} -	return 0; +static void caam_rng_worker(struct work_struct *work) +{ +	struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx, +						worker); +	caam_rng_fill_async(ctx);  } -static inline int rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) +static int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)  { -	struct device *jrdev = ctx->jrdev; -	struct buf_data *bd = &ctx->bufs[buf_id]; -	u32 *desc = bd->hw_desc; -	int sh_len = desc_len(ctx->sh_desc); +	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng); +	int out; -	init_job_desc_shared(desc, ctx->sh_desc_dma, sh_len, HDR_SHARE_DEFER | -			     HDR_REVERSE); +	if (wait) { +		struct completion done; -	bd->addr = dma_map_single(jrdev, bd->buf, RN_BUF_SIZE, DMA_FROM_DEVICE); -	if (dma_mapping_error(jrdev, bd->addr)) { -		dev_err(jrdev, "unable to map dst\n"); -		return -ENOMEM; +		return caam_rng_read_one(ctx->jrdev, dst, max, +					 ctx->desc_sync, &done);  	} -	append_seq_out_ptr_intlen(desc, bd->addr, RN_BUF_SIZE, 0); - -	print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS, 16, 4, -			     desc, desc_bytes(desc), 1); +	out = kfifo_out(&ctx->fifo, dst, max); +	if (kfifo_is_empty(&ctx->fifo)) +		schedule_work(&ctx->worker); -	return 0; +	return out;  }  static void caam_cleanup(struct hwrng *rng)  { -	int i; -	struct buf_data *bd; - -	for (i = 0; i < 2; i++) { -		bd = &rng_ctx->bufs[i]; -		if (atomic_read(&bd->empty) == BUF_PENDING) -			wait_for_completion(&bd->filled); -	} +	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng); -	rng_unmap_ctx(rng_ctx); +	flush_work(&ctx->worker); +	caam_jr_free(ctx->jrdev); +	kfifo_free(&ctx->fifo);  } -static int caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) +static int caam_init(struct hwrng *rng)  { -	struct buf_data *bd = &ctx->bufs[buf_id]; +	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);  	int err; -	err = rng_create_job_desc(ctx, buf_id); -	if (err) -		return err; - -	atomic_set(&bd->empty, BUF_EMPTY); -	submit_job(ctx, buf_id == ctx->current_buf); -	wait_for_completion(&bd->filled); +	ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN, +				      GFP_DMA | GFP_KERNEL); +	if (!ctx->desc_sync) +		return -ENOMEM; -	return 0; -} +	ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN, +				       GFP_DMA | GFP_KERNEL); +	if (!ctx->desc_async) +		return -ENOMEM; -static int caam_init_rng(struct caam_rng_ctx *ctx, struct device *jrdev) -{ -	int err; +	if (kfifo_alloc(&ctx->fifo, CAAM_RNG_MAX_FIFO_STORE_SIZE, +			GFP_DMA | GFP_KERNEL)) +		return -ENOMEM; -	ctx->jrdev = jrdev; +	INIT_WORK(&ctx->worker, caam_rng_worker); -	err = rng_create_sh_desc(ctx); -	if (err) +	ctx->jrdev = caam_jr_alloc(); +	err = PTR_ERR_OR_ZERO(ctx->jrdev); +	if (err) { +		kfifo_free(&ctx->fifo); +		pr_err("Job Ring Device allocation for transform failed\n");  		return err; +	} -	ctx->current_buf = 0; -	ctx->cur_buf_idx = 0; +	/* +	 * Fill async buffer to have early randomness data for +	 * hw_random +	 */ +	caam_rng_fill_async(ctx); -	err = caam_init_buf(ctx, 0); -	if (err) -		return err; - -	return caam_init_buf(ctx, 1); +	return 0;  } -static struct hwrng caam_rng = { -	.name		= "rng-caam", -	.cleanup	= caam_cleanup, -	.read		= caam_read, -}; +int caam_rng_init(struct device *ctrldev); -void caam_rng_exit(void) +void caam_rng_exit(struct device *ctrldev)  { -	if (!init_done) -		return; - -	caam_jr_free(rng_ctx->jrdev); -	hwrng_unregister(&caam_rng); -	kfree(rng_ctx); +	devres_release_group(ctrldev, caam_rng_init);  }  int caam_rng_init(struct device *ctrldev)  { -	struct device *dev; +	struct caam_rng_ctx *ctx;  	u32 rng_inst;  	struct caam_drv_private *priv = dev_get_drvdata(ctrldev); -	int err; -	init_done = false; +	int ret;  	/* Check for an instantiated RNG before registration */  	if (priv->era < 10) @@ -328,31 +232,30 @@ int caam_rng_init(struct device *ctrldev)  	if (!rng_inst)  		return 0; -	dev = caam_jr_alloc(); -	if (IS_ERR(dev)) { -		pr_err("Job Ring Device allocation for transform failed\n"); -		return PTR_ERR(dev); -	} -	rng_ctx = kmalloc(sizeof(*rng_ctx), GFP_DMA | GFP_KERNEL); -	if (!rng_ctx) { -		err = -ENOMEM; -		goto free_caam_alloc; -	} -	err = caam_init_rng(rng_ctx, dev); -	if (err) -		goto free_rng_ctx; +	if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL)) +		return -ENOMEM; -	dev_info(dev, "registering rng-caam\n"); +	ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL); +	if (!ctx) +		return -ENOMEM; -	err = hwrng_register(&caam_rng); -	if (!err) { -		init_done = true; -		return err; +	ctx->ctrldev = ctrldev; + +	ctx->rng.name    = "rng-caam"; +	ctx->rng.init    = caam_init; +	ctx->rng.cleanup = caam_cleanup; +	ctx->rng.read    = caam_read; +	ctx->rng.priv    = (unsigned long)ctx; +	ctx->rng.quality = 1024; + +	dev_info(ctrldev, "registering rng-caam\n"); + +	ret = devm_hwrng_register(ctrldev, &ctx->rng); +	if (ret) { +		caam_rng_exit(ctrldev); +		return ret;  	} -free_rng_ctx: -	kfree(rng_ctx); -free_caam_alloc: -	caam_jr_free(dev); -	return err; +	devres_close_group(ctrldev, caam_rng_init); +	return 0;  } |