Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Driver to expose SEC4 PRNG via crypto RNG API
  4 *
  5 * Copyright 2022 NXP
  6 *
  7 */
  8
  9#include <linux/completion.h>
 10#include <crypto/internal/rng.h>
 11#include <linux/dma-mapping.h>
 12#include <linux/kernel.h>
 13#include "compat.h"
 14#include "regs.h"
 15#include "intern.h"
 16#include "desc_constr.h"
 17#include "jr.h"
 18#include "error.h"
 19
 20/*
 21 * Length of used descriptors, see caam_init_desc()
 22 */
 23#define CAAM_PRNG_MAX_DESC_LEN (CAAM_CMD_SZ +				\
 24			    CAAM_CMD_SZ +				\
 25			    CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
 26
 27/* prng per-device context */
 28struct caam_prng_ctx {
 29	int err;
 30	struct completion done;
 31};
 32
 33struct caam_prng_alg {
 34	struct rng_alg rng;
 35	bool registered;
 36};
 37
 38static void caam_prng_done(struct device *jrdev, u32 *desc, u32 err,
 39			  void *context)
 40{
 41	struct caam_prng_ctx *jctx = context;
 42
 43	jctx->err = err ? caam_jr_strstatus(jrdev, err) : 0;
 44
 45	complete(&jctx->done);
 46}
 47
 48static u32 *caam_init_reseed_desc(u32 *desc)
 49{
 50	init_job_desc(desc, 0);	/* + 1 cmd_sz */
 51	/* Generate random bytes: + 1 cmd_sz */
 52	append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
 53			OP_ALG_AS_FINALIZE);
 54
 55	print_hex_dump_debug("prng reseed desc@: ", DUMP_PREFIX_ADDRESS,
 56			     16, 4, desc, desc_bytes(desc), 1);
 57
 58	return desc;
 59}
 60
 61static u32 *caam_init_prng_desc(u32 *desc, dma_addr_t dst_dma, u32 len)
 62{
 63	init_job_desc(desc, 0);	/* + 1 cmd_sz */
 64	/* Generate random bytes: + 1 cmd_sz */
 65	append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG);
 66	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
 67	append_fifo_store(desc, dst_dma,
 68			  len, FIFOST_TYPE_RNGSTORE);
 69
 70	print_hex_dump_debug("prng job desc@: ", DUMP_PREFIX_ADDRESS,
 71			     16, 4, desc, desc_bytes(desc), 1);
 72
 73	return desc;
 74}
 75
 76static int caam_prng_generate(struct crypto_rng *tfm,
 77			     const u8 *src, unsigned int slen,
 78			     u8 *dst, unsigned int dlen)
 79{
 80	unsigned int aligned_dlen = ALIGN(dlen, dma_get_cache_alignment());
 81	struct caam_prng_ctx ctx;
 82	struct device *jrdev;
 83	dma_addr_t dst_dma;
 84	u32 *desc;
 85	u8 *buf;
 86	int ret;
 87
 88	if (aligned_dlen < dlen)
 89		return -EOVERFLOW;
 90
 91	buf = kzalloc(aligned_dlen, GFP_KERNEL);
 92	if (!buf)
 93		return -ENOMEM;
 94
 95	jrdev = caam_jr_alloc();
 96	ret = PTR_ERR_OR_ZERO(jrdev);
 97	if (ret) {
 98		pr_err("Job Ring Device allocation failed\n");
 99		kfree(buf);
100		return ret;
101	}
102
103	desc = kzalloc(CAAM_PRNG_MAX_DESC_LEN, GFP_KERNEL);
104	if (!desc) {
105		ret = -ENOMEM;
106		goto out1;
107	}
108
109	dst_dma = dma_map_single(jrdev, buf, dlen, DMA_FROM_DEVICE);
110	if (dma_mapping_error(jrdev, dst_dma)) {
111		dev_err(jrdev, "Failed to map destination buffer memory\n");
112		ret = -ENOMEM;
113		goto out;
114	}
115
116	init_completion(&ctx.done);
117	ret = caam_jr_enqueue(jrdev,
118			      caam_init_prng_desc(desc, dst_dma, dlen),
119			      caam_prng_done, &ctx);
120
121	if (ret == -EINPROGRESS) {
122		wait_for_completion(&ctx.done);
123		ret = ctx.err;
124	}
125
126	dma_unmap_single(jrdev, dst_dma, dlen, DMA_FROM_DEVICE);
127
128	if (!ret)
129		memcpy(dst, buf, dlen);
130out:
131	kfree(desc);
132out1:
133	caam_jr_free(jrdev);
134	kfree(buf);
135	return ret;
136}
137
138static void caam_prng_exit(struct crypto_tfm *tfm) {}
139
140static int caam_prng_init(struct crypto_tfm *tfm)
141{
142	return 0;
143}
144
145static int caam_prng_seed(struct crypto_rng *tfm,
146			 const u8 *seed, unsigned int slen)
147{
148	struct caam_prng_ctx ctx;
149	struct device *jrdev;
150	u32 *desc;
151	int ret;
152
153	if (slen) {
154		pr_err("Seed length should be zero\n");
155		return -EINVAL;
156	}
157
158	jrdev = caam_jr_alloc();
159	ret = PTR_ERR_OR_ZERO(jrdev);
160	if (ret) {
161		pr_err("Job Ring Device allocation failed\n");
162		return ret;
163	}
164
165	desc = kzalloc(CAAM_PRNG_MAX_DESC_LEN, GFP_KERNEL);
166	if (!desc) {
167		caam_jr_free(jrdev);
168		return -ENOMEM;
169	}
170
171	init_completion(&ctx.done);
172	ret = caam_jr_enqueue(jrdev,
173			      caam_init_reseed_desc(desc),
174			      caam_prng_done, &ctx);
175
176	if (ret == -EINPROGRESS) {
177		wait_for_completion(&ctx.done);
178		ret = ctx.err;
179	}
180
181	kfree(desc);
182	caam_jr_free(jrdev);
183	return ret;
184}
185
186static struct caam_prng_alg caam_prng_alg = {
187	.rng = {
188		.generate = caam_prng_generate,
189		.seed = caam_prng_seed,
190		.seedsize = 0,
191		.base = {
192			.cra_name = "stdrng",
193			.cra_driver_name = "prng-caam",
194			.cra_priority = 500,
195			.cra_ctxsize = sizeof(struct caam_prng_ctx),
196			.cra_module = THIS_MODULE,
197			.cra_init = caam_prng_init,
198			.cra_exit = caam_prng_exit,
199		},
200	}
201};
202
203void caam_prng_unregister(void *data)
204{
205	if (caam_prng_alg.registered)
206		crypto_unregister_rng(&caam_prng_alg.rng);
207}
208
209int caam_prng_register(struct device *ctrldev)
210{
211	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
212	u32 rng_inst;
213	int ret = 0;
214
215	/* Check for available RNG blocks before registration */
216	if (priv->era < 10)
217		rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
218			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
219	else
220		rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK;
221
222	if (!rng_inst) {
223		dev_dbg(ctrldev, "RNG block is not available... skipping registering algorithm\n");
224		return ret;
225	}
226
227	ret = crypto_register_rng(&caam_prng_alg.rng);
228	if (ret) {
229		dev_err(ctrldev,
230			"couldn't register rng crypto alg: %d\n",
231			ret);
232		return ret;
233	}
234
235	caam_prng_alg.registered = true;
236
237	dev_info(ctrldev,
238		 "rng crypto API alg registered %s\n", caam_prng_alg.rng.base.cra_driver_name);
239
240	return 0;
241}