xref: /linux/drivers/crypto/caam/caamrng.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
1618b5dc4SHoria Geantă // SPDX-License-Identifier: GPL-2.0+
2e24f7c9eSYuan Kang /*
3e24f7c9eSYuan Kang  * caam - Freescale FSL CAAM support for hw_random
4e24f7c9eSYuan Kang  *
5e24f7c9eSYuan Kang  * Copyright 2011 Freescale Semiconductor, Inc.
6ae1dd17dSHoria GeantA  * Copyright 2018-2019, 2023 NXP
7e24f7c9eSYuan Kang  *
8e24f7c9eSYuan Kang  * Based on caamalg.c crypto API driver.
9e24f7c9eSYuan Kang  *
10e24f7c9eSYuan Kang  */
11e24f7c9eSYuan Kang 
12e24f7c9eSYuan Kang #include <linux/hw_random.h>
13e24f7c9eSYuan Kang #include <linux/completion.h>
14e24f7c9eSYuan Kang #include <linux/atomic.h>
15199354d7SHerbert Xu #include <linux/dma-mapping.h>
16199354d7SHerbert Xu #include <linux/kernel.h>
172c5e88dcSAndrey Smirnov #include <linux/kfifo.h>
18e24f7c9eSYuan Kang 
19e24f7c9eSYuan Kang #include "compat.h"
20e24f7c9eSYuan Kang 
21e24f7c9eSYuan Kang #include "regs.h"
22e24f7c9eSYuan Kang #include "intern.h"
23e24f7c9eSYuan Kang #include "desc_constr.h"
24e24f7c9eSYuan Kang #include "jr.h"
25e24f7c9eSYuan Kang #include "error.h"
26e24f7c9eSYuan Kang 
27ea53756dSAndrey Smirnov #define CAAM_RNG_MAX_FIFO_STORE_SIZE	16
282c5e88dcSAndrey Smirnov 
29e24f7c9eSYuan Kang /*
302c5e88dcSAndrey Smirnov  * Length of used descriptors, see caam_init_desc()
31e24f7c9eSYuan Kang  */
322c5e88dcSAndrey Smirnov #define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ +				\
332c5e88dcSAndrey Smirnov 			   CAAM_CMD_SZ +				\
342c5e88dcSAndrey Smirnov 			   CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
35e24f7c9eSYuan Kang 
36e24f7c9eSYuan Kang /* rng per-device context */
37e24f7c9eSYuan Kang struct caam_rng_ctx {
381517f63cSAndrey Smirnov 	struct hwrng rng;
39e24f7c9eSYuan Kang 	struct device *jrdev;
402c5e88dcSAndrey Smirnov 	struct device *ctrldev;
412c5e88dcSAndrey Smirnov 	void *desc_async;
422c5e88dcSAndrey Smirnov 	void *desc_sync;
432c5e88dcSAndrey Smirnov 	struct work_struct worker;
442c5e88dcSAndrey Smirnov 	struct kfifo fifo;
45e24f7c9eSYuan Kang };
46e24f7c9eSYuan Kang 
4732107e43SAndrey Smirnov struct caam_rng_job_ctx {
4832107e43SAndrey Smirnov 	struct completion *done;
4932107e43SAndrey Smirnov 	int *err;
5032107e43SAndrey Smirnov };
5132107e43SAndrey Smirnov 
to_caam_rng_ctx(struct hwrng * r)521517f63cSAndrey Smirnov static struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r)
531517f63cSAndrey Smirnov {
541517f63cSAndrey Smirnov 	return (struct caam_rng_ctx *)r->priv;
551517f63cSAndrey Smirnov }
564e3a61c5SIuliana Prodan 
caam_rng_done(struct device * jrdev,u32 * desc,u32 err,void * context)572c5e88dcSAndrey Smirnov static void caam_rng_done(struct device *jrdev, u32 *desc, u32 err,
582c5e88dcSAndrey Smirnov 			  void *context)
59e24f7c9eSYuan Kang {
6032107e43SAndrey Smirnov 	struct caam_rng_job_ctx *jctx = context;
61e24f7c9eSYuan Kang 
62fa9659cdSMarek Vasut 	if (err)
6332107e43SAndrey Smirnov 		*jctx->err = caam_jr_strstatus(jrdev, err);
64e24f7c9eSYuan Kang 
6532107e43SAndrey Smirnov 	complete(jctx->done);
66e24f7c9eSYuan Kang }
67e24f7c9eSYuan Kang 
caam_init_desc(u32 * desc,dma_addr_t dst_dma)68ea53756dSAndrey Smirnov static u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma)
69e24f7c9eSYuan Kang {
702c5e88dcSAndrey Smirnov 	init_job_desc(desc, 0);	/* + 1 cmd_sz */
712c5e88dcSAndrey Smirnov 	/* Generate random bytes: + 1 cmd_sz */
72358ba762SAndrey Smirnov 	append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG |
73358ba762SAndrey Smirnov 			 OP_ALG_PR_ON);
742c5e88dcSAndrey Smirnov 	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
75ea53756dSAndrey Smirnov 	append_fifo_store(desc, dst_dma,
76ea53756dSAndrey Smirnov 			  CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE);
772c5e88dcSAndrey Smirnov 
782c5e88dcSAndrey Smirnov 	print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS,
792c5e88dcSAndrey Smirnov 			     16, 4, desc, desc_bytes(desc), 1);
802c5e88dcSAndrey Smirnov 
812c5e88dcSAndrey Smirnov 	return desc;
822c5e88dcSAndrey Smirnov }
832c5e88dcSAndrey Smirnov 
caam_rng_read_one(struct device * jrdev,void * dst,int len,void * desc,struct completion * done)842c5e88dcSAndrey Smirnov static int caam_rng_read_one(struct device *jrdev,
852c5e88dcSAndrey Smirnov 			     void *dst, int len,
862c5e88dcSAndrey Smirnov 			     void *desc,
872c5e88dcSAndrey Smirnov 			     struct completion *done)
882c5e88dcSAndrey Smirnov {
892c5e88dcSAndrey Smirnov 	dma_addr_t dst_dma;
9032107e43SAndrey Smirnov 	int err, ret = 0;
9132107e43SAndrey Smirnov 	struct caam_rng_job_ctx jctx = {
9232107e43SAndrey Smirnov 		.done = done,
9332107e43SAndrey Smirnov 		.err  = &ret,
9432107e43SAndrey Smirnov 	};
95e24f7c9eSYuan Kang 
96ea53756dSAndrey Smirnov 	len = CAAM_RNG_MAX_FIFO_STORE_SIZE;
97e24f7c9eSYuan Kang 
982c5e88dcSAndrey Smirnov 	dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE);
992c5e88dcSAndrey Smirnov 	if (dma_mapping_error(jrdev, dst_dma)) {
1002c5e88dcSAndrey Smirnov 		dev_err(jrdev, "unable to map destination memory\n");
1012c5e88dcSAndrey Smirnov 		return -ENOMEM;
102e24f7c9eSYuan Kang 	}
103e24f7c9eSYuan Kang 
1042c5e88dcSAndrey Smirnov 	init_completion(done);
1052c5e88dcSAndrey Smirnov 	err = caam_jr_enqueue(jrdev,
106ea53756dSAndrey Smirnov 			      caam_init_desc(desc, dst_dma),
10732107e43SAndrey Smirnov 			      caam_rng_done, &jctx);
1082c5e88dcSAndrey Smirnov 	if (err == -EINPROGRESS) {
1092c5e88dcSAndrey Smirnov 		wait_for_completion(done);
1102c5e88dcSAndrey Smirnov 		err = 0;
1112c5e88dcSAndrey Smirnov 	}
1122c5e88dcSAndrey Smirnov 
1132c5e88dcSAndrey Smirnov 	dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE);
1142c5e88dcSAndrey Smirnov 
11532107e43SAndrey Smirnov 	return err ?: (ret ?: len);
1162c5e88dcSAndrey Smirnov }
1172c5e88dcSAndrey Smirnov 
caam_rng_fill_async(struct caam_rng_ctx * ctx)1182c5e88dcSAndrey Smirnov static void caam_rng_fill_async(struct caam_rng_ctx *ctx)
1192c5e88dcSAndrey Smirnov {
1202c5e88dcSAndrey Smirnov 	struct scatterlist sg[1];
1212c5e88dcSAndrey Smirnov 	struct completion done;
1222c5e88dcSAndrey Smirnov 	int len, nents;
1232c5e88dcSAndrey Smirnov 
1242c5e88dcSAndrey Smirnov 	sg_init_table(sg, ARRAY_SIZE(sg));
1252c5e88dcSAndrey Smirnov 	nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg),
126ea53756dSAndrey Smirnov 				     CAAM_RNG_MAX_FIFO_STORE_SIZE);
1272c5e88dcSAndrey Smirnov 	if (!nents)
1282c5e88dcSAndrey Smirnov 		return;
1292c5e88dcSAndrey Smirnov 
1302c5e88dcSAndrey Smirnov 	len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]),
1312c5e88dcSAndrey Smirnov 				sg[0].length,
1322c5e88dcSAndrey Smirnov 				ctx->desc_async,
1332c5e88dcSAndrey Smirnov 				&done);
1342c5e88dcSAndrey Smirnov 	if (len < 0)
1352c5e88dcSAndrey Smirnov 		return;
1362c5e88dcSAndrey Smirnov 
1372c5e88dcSAndrey Smirnov 	kfifo_dma_in_finish(&ctx->fifo, len);
1382c5e88dcSAndrey Smirnov }
1392c5e88dcSAndrey Smirnov 
caam_rng_worker(struct work_struct * work)1402c5e88dcSAndrey Smirnov static void caam_rng_worker(struct work_struct *work)
1412c5e88dcSAndrey Smirnov {
1422c5e88dcSAndrey Smirnov 	struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx,
1432c5e88dcSAndrey Smirnov 						worker);
1442c5e88dcSAndrey Smirnov 	caam_rng_fill_async(ctx);
1452c5e88dcSAndrey Smirnov }
1462c5e88dcSAndrey Smirnov 
caam_read(struct hwrng * rng,void * dst,size_t max,bool wait)1472c5e88dcSAndrey Smirnov static int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)
148e24f7c9eSYuan Kang {
1491517f63cSAndrey Smirnov 	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
1502c5e88dcSAndrey Smirnov 	int out;
151e24f7c9eSYuan Kang 
1522c5e88dcSAndrey Smirnov 	if (wait) {
1532c5e88dcSAndrey Smirnov 		struct completion done;
154e24f7c9eSYuan Kang 
1552c5e88dcSAndrey Smirnov 		return caam_rng_read_one(ctx->jrdev, dst, max,
1562c5e88dcSAndrey Smirnov 					 ctx->desc_sync, &done);
157e24f7c9eSYuan Kang 	}
158e24f7c9eSYuan Kang 
1592c5e88dcSAndrey Smirnov 	out = kfifo_out(&ctx->fifo, dst, max);
160ea53756dSAndrey Smirnov 	if (kfifo_is_empty(&ctx->fifo))
1612c5e88dcSAndrey Smirnov 		schedule_work(&ctx->worker);
162e24f7c9eSYuan Kang 
1632c5e88dcSAndrey Smirnov 	return out;
164e24f7c9eSYuan Kang }
165e24f7c9eSYuan Kang 
caam_cleanup(struct hwrng * rng)166e24f7c9eSYuan Kang static void caam_cleanup(struct hwrng *rng)
167e24f7c9eSYuan Kang {
1681517f63cSAndrey Smirnov 	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
169e24f7c9eSYuan Kang 
1702c5e88dcSAndrey Smirnov 	flush_work(&ctx->worker);
1711517f63cSAndrey Smirnov 	caam_jr_free(ctx->jrdev);
1722c5e88dcSAndrey Smirnov 	kfifo_free(&ctx->fifo);
173e24f7c9eSYuan Kang }
174e24f7c9eSYuan Kang 
175*2be0d806SVictoria Milhoan (b42089) #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
test_len(struct hwrng * rng,size_t len,bool wait)176*2be0d806SVictoria Milhoan (b42089) static inline void test_len(struct hwrng *rng, size_t len, bool wait)
177*2be0d806SVictoria Milhoan (b42089) {
178*2be0d806SVictoria Milhoan (b42089) 	u8 *buf;
179*2be0d806SVictoria Milhoan (b42089) 	int read_len;
180*2be0d806SVictoria Milhoan (b42089) 	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
181*2be0d806SVictoria Milhoan (b42089) 	struct device *dev = ctx->ctrldev;
182*2be0d806SVictoria Milhoan (b42089) 
183*2be0d806SVictoria Milhoan (b42089) 	buf = kcalloc(CAAM_RNG_MAX_FIFO_STORE_SIZE, sizeof(u8), GFP_KERNEL);
184*2be0d806SVictoria Milhoan (b42089) 
185*2be0d806SVictoria Milhoan (b42089) 	while (len > 0) {
186*2be0d806SVictoria Milhoan (b42089) 		read_len = rng->read(rng, buf, len, wait);
187*2be0d806SVictoria Milhoan (b42089) 
188*2be0d806SVictoria Milhoan (b42089) 		if (read_len < 0 || (read_len == 0 && wait)) {
189*2be0d806SVictoria Milhoan (b42089) 			dev_err(dev, "RNG Read FAILED received %d bytes\n",
190*2be0d806SVictoria Milhoan (b42089) 				read_len);
191*2be0d806SVictoria Milhoan (b42089) 			kfree(buf);
192*2be0d806SVictoria Milhoan (b42089) 			return;
193*2be0d806SVictoria Milhoan (b42089) 		}
194*2be0d806SVictoria Milhoan (b42089) 
195*2be0d806SVictoria Milhoan (b42089) 		print_hex_dump_debug("random bytes@: ",
196*2be0d806SVictoria Milhoan (b42089) 			DUMP_PREFIX_ADDRESS, 16, 4,
197*2be0d806SVictoria Milhoan (b42089) 			buf, read_len, 1);
198*2be0d806SVictoria Milhoan (b42089) 
199*2be0d806SVictoria Milhoan (b42089) 		len = len - read_len;
200*2be0d806SVictoria Milhoan (b42089) 	}
201*2be0d806SVictoria Milhoan (b42089) 
202*2be0d806SVictoria Milhoan (b42089) 	kfree(buf);
203*2be0d806SVictoria Milhoan (b42089) }
204*2be0d806SVictoria Milhoan (b42089) 
test_mode_once(struct hwrng * rng,bool wait)205*2be0d806SVictoria Milhoan (b42089) static inline void test_mode_once(struct hwrng *rng, bool wait)
206*2be0d806SVictoria Milhoan (b42089) {
207*2be0d806SVictoria Milhoan (b42089) 	test_len(rng, 32, wait);
208*2be0d806SVictoria Milhoan (b42089) 	test_len(rng, 64, wait);
209*2be0d806SVictoria Milhoan (b42089) 	test_len(rng, 128, wait);
210*2be0d806SVictoria Milhoan (b42089) }
211*2be0d806SVictoria Milhoan (b42089) 
self_test(struct hwrng * rng)212*2be0d806SVictoria Milhoan (b42089) static void self_test(struct hwrng *rng)
213*2be0d806SVictoria Milhoan (b42089) {
214*2be0d806SVictoria Milhoan (b42089) 	pr_info("Executing RNG SELF-TEST with wait\n");
215*2be0d806SVictoria Milhoan (b42089) 	test_mode_once(rng, true);
216*2be0d806SVictoria Milhoan (b42089) }
217*2be0d806SVictoria Milhoan (b42089) #endif
218*2be0d806SVictoria Milhoan (b42089) 
caam_init(struct hwrng * rng)2198483c831SAndrey Smirnov static int caam_init(struct hwrng *rng)
220e24f7c9eSYuan Kang {
2211517f63cSAndrey Smirnov 	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
222ce572085SHoria Geanta 	int err;
223ce572085SHoria Geanta 
2242c5e88dcSAndrey Smirnov 	ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
225199354d7SHerbert Xu 				      GFP_KERNEL);
2262c5e88dcSAndrey Smirnov 	if (!ctx->desc_sync)
2272c5e88dcSAndrey Smirnov 		return -ENOMEM;
2282c5e88dcSAndrey Smirnov 
2292c5e88dcSAndrey Smirnov 	ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
230199354d7SHerbert Xu 				       GFP_KERNEL);
2312c5e88dcSAndrey Smirnov 	if (!ctx->desc_async)
2322c5e88dcSAndrey Smirnov 		return -ENOMEM;
2332c5e88dcSAndrey Smirnov 
234199354d7SHerbert Xu 	if (kfifo_alloc(&ctx->fifo, ALIGN(CAAM_RNG_MAX_FIFO_STORE_SIZE,
235199354d7SHerbert Xu 					  dma_get_cache_alignment()),
236199354d7SHerbert Xu 			GFP_KERNEL))
2372c5e88dcSAndrey Smirnov 		return -ENOMEM;
2382c5e88dcSAndrey Smirnov 
2392c5e88dcSAndrey Smirnov 	INIT_WORK(&ctx->worker, caam_rng_worker);
2402c5e88dcSAndrey Smirnov 
2418483c831SAndrey Smirnov 	ctx->jrdev = caam_jr_alloc();
2428483c831SAndrey Smirnov 	err = PTR_ERR_OR_ZERO(ctx->jrdev);
2438483c831SAndrey Smirnov 	if (err) {
2442c5e88dcSAndrey Smirnov 		kfifo_free(&ctx->fifo);
2458483c831SAndrey Smirnov 		pr_err("Job Ring Device allocation for transform failed\n");
2468483c831SAndrey Smirnov 		return err;
2478483c831SAndrey Smirnov 	}
248ce572085SHoria Geanta 
2492c5e88dcSAndrey Smirnov 	/*
2502c5e88dcSAndrey Smirnov 	 * Fill async buffer to have early randomness data for
2512c5e88dcSAndrey Smirnov 	 * hw_random
2522c5e88dcSAndrey Smirnov 	 */
2532c5e88dcSAndrey Smirnov 	caam_rng_fill_async(ctx);
2548483c831SAndrey Smirnov 
2558483c831SAndrey Smirnov 	return 0;
256e24f7c9eSYuan Kang }
257e24f7c9eSYuan Kang 
2581517f63cSAndrey Smirnov int caam_rng_init(struct device *ctrldev);
259e24f7c9eSYuan Kang 
caam_rng_exit(struct device * ctrldev)2601517f63cSAndrey Smirnov void caam_rng_exit(struct device *ctrldev)
261e24f7c9eSYuan Kang {
2621517f63cSAndrey Smirnov 	devres_release_group(ctrldev, caam_rng_init);
263e24f7c9eSYuan Kang }
264e24f7c9eSYuan Kang 
caam_rng_init(struct device * ctrldev)2651b46c90cSHoria Geantă int caam_rng_init(struct device *ctrldev)
266e24f7c9eSYuan Kang {
2671517f63cSAndrey Smirnov 	struct caam_rng_ctx *ctx;
268d239b10dSHoria Geantă 	u32 rng_inst;
2691b46c90cSHoria Geantă 	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
2701517f63cSAndrey Smirnov 	int ret;
27135af6403SRuchika Gupta 
272bf83490eSVictoria Milhoan 	/* Check for an instantiated RNG before registration */
273d239b10dSHoria Geantă 	if (priv->era < 10)
274ae1dd17dSHoria GeantA 		rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
275d239b10dSHoria Geantă 			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
276d239b10dSHoria Geantă 	else
277ae1dd17dSHoria GeantA 		rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK;
278d239b10dSHoria Geantă 
2791b46c90cSHoria Geantă 	if (!rng_inst)
2801b46c90cSHoria Geantă 		return 0;
281bf83490eSVictoria Milhoan 
2821517f63cSAndrey Smirnov 	if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL))
2838483c831SAndrey Smirnov 		return -ENOMEM;
284e24f7c9eSYuan Kang 
2852c5e88dcSAndrey Smirnov 	ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL);
2861517f63cSAndrey Smirnov 	if (!ctx)
2871517f63cSAndrey Smirnov 		return -ENOMEM;
2881517f63cSAndrey Smirnov 
2892c5e88dcSAndrey Smirnov 	ctx->ctrldev = ctrldev;
2902c5e88dcSAndrey Smirnov 
2911517f63cSAndrey Smirnov 	ctx->rng.name    = "rng-caam";
2921517f63cSAndrey Smirnov 	ctx->rng.init    = caam_init;
2931517f63cSAndrey Smirnov 	ctx->rng.cleanup = caam_cleanup;
2941517f63cSAndrey Smirnov 	ctx->rng.read    = caam_read;
2951517f63cSAndrey Smirnov 	ctx->rng.priv    = (unsigned long)ctx;
2961517f63cSAndrey Smirnov 
2978483c831SAndrey Smirnov 	dev_info(ctrldev, "registering rng-caam\n");
298c59a1d41SIuliana Prodan 
2991517f63cSAndrey Smirnov 	ret = devm_hwrng_register(ctrldev, &ctx->rng);
3001517f63cSAndrey Smirnov 	if (ret) {
3011517f63cSAndrey Smirnov 		caam_rng_exit(ctrldev);
3021517f63cSAndrey Smirnov 		return ret;
3034e3a61c5SIuliana Prodan 	}
304ac8ad307SFabio Estevam 
305*2be0d806SVictoria Milhoan (b42089) #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
306*2be0d806SVictoria Milhoan (b42089) 	self_test(&ctx->rng);
307*2be0d806SVictoria Milhoan (b42089) #endif
308*2be0d806SVictoria Milhoan (b42089) 
3091517f63cSAndrey Smirnov 	devres_close_group(ctrldev, caam_rng_init);
3101517f63cSAndrey Smirnov 	return 0;
311e24f7c9eSYuan Kang }
312