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