1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * caam - Freescale FSL CAAM support for hw_random |
4 | * |
5 | * Copyright 2011 Freescale Semiconductor, Inc. |
6 | * Copyright 2018-2019, 2023 NXP |
7 | * |
8 | * Based on caamalg.c crypto API driver. |
9 | * |
10 | */ |
11 | |
12 | #include <linux/hw_random.h> |
13 | #include <linux/completion.h> |
14 | #include <linux/atomic.h> |
15 | #include <linux/dma-mapping.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/kfifo.h> |
18 | |
19 | #include "compat.h" |
20 | |
21 | #include "regs.h" |
22 | #include "intern.h" |
23 | #include "desc_constr.h" |
24 | #include "jr.h" |
25 | #include "error.h" |
26 | |
27 | #define CAAM_RNG_MAX_FIFO_STORE_SIZE 16 |
28 | |
29 | /* |
30 | * Length of used descriptors, see caam_init_desc() |
31 | */ |
32 | #define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ + \ |
33 | CAAM_CMD_SZ + \ |
34 | CAAM_CMD_SZ + CAAM_PTR_SZ_MAX) |
35 | |
36 | /* rng per-device context */ |
37 | struct caam_rng_ctx { |
38 | struct hwrng rng; |
39 | struct device *jrdev; |
40 | struct device *ctrldev; |
41 | void *desc_async; |
42 | void *desc_sync; |
43 | struct work_struct worker; |
44 | struct kfifo fifo; |
45 | }; |
46 | |
47 | struct caam_rng_job_ctx { |
48 | struct completion *done; |
49 | int *err; |
50 | }; |
51 | |
52 | static struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r) |
53 | { |
54 | return (struct caam_rng_ctx *)r->priv; |
55 | } |
56 | |
57 | static void caam_rng_done(struct device *jrdev, u32 *desc, u32 err, |
58 | void *context) |
59 | { |
60 | struct caam_rng_job_ctx *jctx = context; |
61 | |
62 | if (err) |
63 | *jctx->err = caam_jr_strstatus(jrdev, err); |
64 | |
65 | complete(jctx->done); |
66 | } |
67 | |
68 | static u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma) |
69 | { |
70 | init_job_desc(desc, options: 0); /* + 1 cmd_sz */ |
71 | /* Generate random bytes: + 1 cmd_sz */ |
72 | append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG | |
73 | OP_ALG_PR_ON); |
74 | /* Store bytes: + 1 cmd_sz + caam_ptr_sz */ |
75 | append_fifo_store(desc, ptr: dst_dma, |
76 | CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE); |
77 | |
78 | print_hex_dump_debug("rng job desc@: " , DUMP_PREFIX_ADDRESS, |
79 | 16, 4, desc, desc_bytes(desc), 1); |
80 | |
81 | return desc; |
82 | } |
83 | |
84 | static int caam_rng_read_one(struct device *jrdev, |
85 | void *dst, int len, |
86 | void *desc, |
87 | struct completion *done) |
88 | { |
89 | dma_addr_t dst_dma; |
90 | int err, ret = 0; |
91 | struct caam_rng_job_ctx jctx = { |
92 | .done = done, |
93 | .err = &ret, |
94 | }; |
95 | |
96 | len = CAAM_RNG_MAX_FIFO_STORE_SIZE; |
97 | |
98 | dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE); |
99 | if (dma_mapping_error(dev: jrdev, dma_addr: dst_dma)) { |
100 | dev_err(jrdev, "unable to map destination memory\n" ); |
101 | return -ENOMEM; |
102 | } |
103 | |
104 | init_completion(x: done); |
105 | err = caam_jr_enqueue(dev: jrdev, |
106 | desc: caam_init_desc(desc, dst_dma), |
107 | cbk: caam_rng_done, areq: &jctx); |
108 | if (err == -EINPROGRESS) { |
109 | wait_for_completion(done); |
110 | err = 0; |
111 | } |
112 | |
113 | dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE); |
114 | |
115 | return err ?: (ret ?: len); |
116 | } |
117 | |
118 | static void caam_rng_fill_async(struct caam_rng_ctx *ctx) |
119 | { |
120 | struct scatterlist sg[1]; |
121 | struct completion done; |
122 | int len, nents; |
123 | |
124 | sg_init_table(sg, ARRAY_SIZE(sg)); |
125 | nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg), |
126 | CAAM_RNG_MAX_FIFO_STORE_SIZE); |
127 | if (!nents) |
128 | return; |
129 | |
130 | len = caam_rng_read_one(jrdev: ctx->jrdev, dst: sg_virt(sg: &sg[0]), |
131 | len: sg[0].length, |
132 | desc: ctx->desc_async, |
133 | done: &done); |
134 | if (len < 0) |
135 | return; |
136 | |
137 | kfifo_dma_in_finish(&ctx->fifo, len); |
138 | } |
139 | |
140 | static void caam_rng_worker(struct work_struct *work) |
141 | { |
142 | struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx, |
143 | worker); |
144 | caam_rng_fill_async(ctx); |
145 | } |
146 | |
147 | static int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait) |
148 | { |
149 | struct caam_rng_ctx *ctx = to_caam_rng_ctx(r: rng); |
150 | int out; |
151 | |
152 | if (wait) { |
153 | struct completion done; |
154 | |
155 | return caam_rng_read_one(jrdev: ctx->jrdev, dst, len: max, |
156 | desc: ctx->desc_sync, done: &done); |
157 | } |
158 | |
159 | out = kfifo_out(&ctx->fifo, dst, max); |
160 | if (kfifo_is_empty(&ctx->fifo)) |
161 | schedule_work(work: &ctx->worker); |
162 | |
163 | return out; |
164 | } |
165 | |
166 | static void caam_cleanup(struct hwrng *rng) |
167 | { |
168 | struct caam_rng_ctx *ctx = to_caam_rng_ctx(r: rng); |
169 | |
170 | flush_work(work: &ctx->worker); |
171 | caam_jr_free(rdev: ctx->jrdev); |
172 | kfifo_free(&ctx->fifo); |
173 | } |
174 | |
175 | #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST |
176 | static inline void test_len(struct hwrng *rng, size_t len, bool wait) |
177 | { |
178 | u8 *buf; |
179 | int read_len; |
180 | struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng); |
181 | struct device *dev = ctx->ctrldev; |
182 | |
183 | buf = kcalloc(CAAM_RNG_MAX_FIFO_STORE_SIZE, sizeof(u8), GFP_KERNEL); |
184 | |
185 | while (len > 0) { |
186 | read_len = rng->read(rng, buf, len, wait); |
187 | |
188 | if (read_len < 0 || (read_len == 0 && wait)) { |
189 | dev_err(dev, "RNG Read FAILED received %d bytes\n" , |
190 | read_len); |
191 | kfree(buf); |
192 | return; |
193 | } |
194 | |
195 | print_hex_dump_debug("random bytes@: " , |
196 | DUMP_PREFIX_ADDRESS, 16, 4, |
197 | buf, read_len, 1); |
198 | |
199 | len = len - read_len; |
200 | } |
201 | |
202 | kfree(buf); |
203 | } |
204 | |
205 | static inline void test_mode_once(struct hwrng *rng, bool wait) |
206 | { |
207 | test_len(rng, 32, wait); |
208 | test_len(rng, 64, wait); |
209 | test_len(rng, 128, wait); |
210 | } |
211 | |
212 | static void self_test(struct hwrng *rng) |
213 | { |
214 | pr_info("Executing RNG SELF-TEST with wait\n" ); |
215 | test_mode_once(rng, true); |
216 | } |
217 | #endif |
218 | |
219 | static int caam_init(struct hwrng *rng) |
220 | { |
221 | struct caam_rng_ctx *ctx = to_caam_rng_ctx(r: rng); |
222 | int err; |
223 | |
224 | ctx->desc_sync = devm_kzalloc(dev: ctx->ctrldev, CAAM_RNG_DESC_LEN, |
225 | GFP_KERNEL); |
226 | if (!ctx->desc_sync) |
227 | return -ENOMEM; |
228 | |
229 | ctx->desc_async = devm_kzalloc(dev: ctx->ctrldev, CAAM_RNG_DESC_LEN, |
230 | GFP_KERNEL); |
231 | if (!ctx->desc_async) |
232 | return -ENOMEM; |
233 | |
234 | if (kfifo_alloc(&ctx->fifo, ALIGN(CAAM_RNG_MAX_FIFO_STORE_SIZE, |
235 | dma_get_cache_alignment()), |
236 | GFP_KERNEL)) |
237 | return -ENOMEM; |
238 | |
239 | INIT_WORK(&ctx->worker, caam_rng_worker); |
240 | |
241 | ctx->jrdev = caam_jr_alloc(); |
242 | err = PTR_ERR_OR_ZERO(ptr: ctx->jrdev); |
243 | if (err) { |
244 | kfifo_free(&ctx->fifo); |
245 | pr_err("Job Ring Device allocation for transform failed\n" ); |
246 | return err; |
247 | } |
248 | |
249 | /* |
250 | * Fill async buffer to have early randomness data for |
251 | * hw_random |
252 | */ |
253 | caam_rng_fill_async(ctx); |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | int caam_rng_init(struct device *ctrldev); |
259 | |
260 | void caam_rng_exit(struct device *ctrldev) |
261 | { |
262 | devres_release_group(dev: ctrldev, id: caam_rng_init); |
263 | } |
264 | |
265 | int caam_rng_init(struct device *ctrldev) |
266 | { |
267 | struct caam_rng_ctx *ctx; |
268 | u32 rng_inst; |
269 | struct caam_drv_private *priv = dev_get_drvdata(dev: ctrldev); |
270 | int ret; |
271 | |
272 | /* Check for an instantiated RNG before registration */ |
273 | if (priv->era < 10) |
274 | rng_inst = (rd_reg32(reg: &priv->jr[0]->perfmon.cha_num_ls) & |
275 | CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT; |
276 | else |
277 | rng_inst = rd_reg32(reg: &priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK; |
278 | |
279 | if (!rng_inst) |
280 | return 0; |
281 | |
282 | if (!devres_open_group(dev: ctrldev, id: caam_rng_init, GFP_KERNEL)) |
283 | return -ENOMEM; |
284 | |
285 | ctx = devm_kzalloc(dev: ctrldev, size: sizeof(*ctx), GFP_KERNEL); |
286 | if (!ctx) |
287 | return -ENOMEM; |
288 | |
289 | ctx->ctrldev = ctrldev; |
290 | |
291 | ctx->rng.name = "rng-caam" ; |
292 | ctx->rng.init = caam_init; |
293 | ctx->rng.cleanup = caam_cleanup; |
294 | ctx->rng.read = caam_read; |
295 | ctx->rng.priv = (unsigned long)ctx; |
296 | |
297 | dev_info(ctrldev, "registering rng-caam\n" ); |
298 | |
299 | ret = devm_hwrng_register(dev: ctrldev, rng: &ctx->rng); |
300 | if (ret) { |
301 | caam_rng_exit(ctrldev); |
302 | return ret; |
303 | } |
304 | |
305 | #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST |
306 | self_test(&ctx->rng); |
307 | #endif |
308 | |
309 | devres_close_group(dev: ctrldev, id: caam_rng_init); |
310 | return 0; |
311 | } |
312 | |