diff options
author | Yuan Kang <Yuan.Kang@freescale.com> | 2012-06-22 20:48:50 -0400 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2012-06-27 02:42:06 -0400 |
commit | e24f7c9e87d46fad06bf1097d48f9923acd8e61c (patch) | |
tree | 15a2f57fd7e43acd51c95779e00f75038ffd8d88 /drivers/crypto/caam | |
parent | 643b39b031f546c7c3c60ef360b8260aa2b32762 (diff) |
crypto: caam - hwrng support
caam_read copies random bytes from two buffers into output.
caam rng can fill empty buffer 0xffff bytes at a time,
but the buffer sizes are rounded down to multiple of cacheline size.
Signed-off-by: Yuan Kang <Yuan.Kang@freescale.com>
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/crypto/caam')
-rw-r--r-- | drivers/crypto/caam/Kconfig | 13 | ||||
-rw-r--r-- | drivers/crypto/caam/Makefile | 1 | ||||
-rw-r--r-- | drivers/crypto/caam/caamrng.c | 309 |
3 files changed, 323 insertions, 0 deletions
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig index e61b7f50785e..502a7f3cca5f 100644 --- a/drivers/crypto/caam/Kconfig +++ b/drivers/crypto/caam/Kconfig | |||
@@ -82,3 +82,16 @@ config CRYPTO_DEV_FSL_CAAM_AHASH_API | |||
82 | 82 | ||
83 | To compile this as a module, choose M here: the module | 83 | To compile this as a module, choose M here: the module |
84 | will be called caamhash. | 84 | will be called caamhash. |
85 | |||
86 | config CRYPTO_DEV_FSL_CAAM_RNG_API | ||
87 | tristate "Register caam device for hwrng API" | ||
88 | depends on CRYPTO_DEV_FSL_CAAM | ||
89 | default y | ||
90 | select CRYPTO_RNG | ||
91 | select HW_RANDOM | ||
92 | help | ||
93 | Selecting this will register the SEC4 hardware rng to | ||
94 | the hw_random API for suppying the kernel entropy pool. | ||
95 | |||
96 | To compile this as a module, choose M here: the module | ||
97 | will be called caamrng. | ||
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile index 9ef1cb00c342..b1eb44838db5 100644 --- a/drivers/crypto/caam/Makefile +++ b/drivers/crypto/caam/Makefile | |||
@@ -5,5 +5,6 @@ | |||
5 | obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam.o | 5 | obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam.o |
6 | obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o | 6 | obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o |
7 | obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o | 7 | obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o |
8 | obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o | ||
8 | 9 | ||
9 | caam-objs := ctrl.o jr.o error.o key_gen.o | 10 | caam-objs := ctrl.o jr.o error.o key_gen.o |
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c new file mode 100644 index 000000000000..e2bfe161dece --- /dev/null +++ b/drivers/crypto/caam/caamrng.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * caam - Freescale FSL CAAM support for hw_random | ||
3 | * | ||
4 | * Copyright 2011 Freescale Semiconductor, Inc. | ||
5 | * | ||
6 | * Based on caamalg.c crypto API driver. | ||
7 | * | ||
8 | * relationship between job descriptors to shared descriptors: | ||
9 | * | ||
10 | * --------------- -------------- | ||
11 | * | JobDesc #0 |-------------------->| ShareDesc | | ||
12 | * | *(buffer 0) | |------------->| (generate) | | ||
13 | * --------------- | | (move) | | ||
14 | * | | (store) | | ||
15 | * --------------- | -------------- | ||
16 | * | JobDesc #1 |------| | ||
17 | * | *(buffer 1) | | ||
18 | * --------------- | ||
19 | * | ||
20 | * A job desc looks like this: | ||
21 | * | ||
22 | * --------------------- | ||
23 | * | Header | | ||
24 | * | ShareDesc Pointer | | ||
25 | * | SEQ_OUT_PTR | | ||
26 | * | (output buffer) | | ||
27 | * --------------------- | ||
28 | * | ||
29 | * The SharedDesc never changes, and each job descriptor points to one of two | ||
30 | * buffers for each device, from which the data will be copied into the | ||
31 | * requested destination | ||
32 | */ | ||
33 | |||
34 | #include <linux/hw_random.h> | ||
35 | #include <linux/completion.h> | ||
36 | #include <linux/atomic.h> | ||
37 | |||
38 | #include "compat.h" | ||
39 | |||
40 | #include "regs.h" | ||
41 | #include "intern.h" | ||
42 | #include "desc_constr.h" | ||
43 | #include "jr.h" | ||
44 | #include "error.h" | ||
45 | |||
46 | /* | ||
47 | * Maximum buffer size: maximum number of random, cache-aligned bytes that | ||
48 | * will be generated and moved to seq out ptr (extlen not allowed) | ||
49 | */ | ||
50 | #define RN_BUF_SIZE (0xffff / L1_CACHE_BYTES * \ | ||
51 | L1_CACHE_BYTES) | ||
52 | |||
53 | /* length of descriptors */ | ||
54 | #define DESC_JOB_O_LEN (CAAM_CMD_SZ * 2 + CAAM_PTR_SZ * 2) | ||
55 | #define DESC_RNG_LEN (10 * CAAM_CMD_SZ) | ||
56 | |||
57 | /* Buffer, its dma address and lock */ | ||
58 | struct buf_data { | ||
59 | u8 buf[RN_BUF_SIZE]; | ||
60 | dma_addr_t addr; | ||
61 | struct completion filled; | ||
62 | u32 hw_desc[DESC_JOB_O_LEN]; | ||
63 | #define BUF_NOT_EMPTY 0 | ||
64 | #define BUF_EMPTY 1 | ||
65 | #define BUF_PENDING 2 /* Empty, but with job pending --don't submit another */ | ||
66 | atomic_t empty; | ||
67 | }; | ||
68 | |||
69 | /* rng per-device context */ | ||
70 | struct caam_rng_ctx { | ||
71 | struct device *jrdev; | ||
72 | dma_addr_t sh_desc_dma; | ||
73 | u32 sh_desc[DESC_RNG_LEN]; | ||
74 | unsigned int cur_buf_idx; | ||
75 | int current_buf; | ||
76 | struct buf_data bufs[2]; | ||
77 | }; | ||
78 | |||
79 | static struct caam_rng_ctx rng_ctx; | ||
80 | |||
81 | static inline void rng_unmap_buf(struct device *jrdev, struct buf_data *bd) | ||
82 | { | ||
83 | if (bd->addr) | ||
84 | dma_unmap_single(jrdev, bd->addr, RN_BUF_SIZE, | ||
85 | DMA_FROM_DEVICE); | ||
86 | } | ||
87 | |||
88 | static inline void rng_unmap_ctx(struct caam_rng_ctx *ctx) | ||
89 | { | ||
90 | struct device *jrdev = ctx->jrdev; | ||
91 | |||
92 | if (ctx->sh_desc_dma) | ||
93 | dma_unmap_single(jrdev, ctx->sh_desc_dma, DESC_RNG_LEN, | ||
94 | DMA_TO_DEVICE); | ||
95 | rng_unmap_buf(jrdev, &ctx->bufs[0]); | ||
96 | rng_unmap_buf(jrdev, &ctx->bufs[1]); | ||
97 | } | ||
98 | |||
99 | static void rng_done(struct device *jrdev, u32 *desc, u32 err, void *context) | ||
100 | { | ||
101 | struct buf_data *bd; | ||
102 | |||
103 | bd = (struct buf_data *)((char *)desc - | ||
104 | offsetof(struct buf_data, hw_desc)); | ||
105 | |||
106 | if (err) { | ||
107 | char tmp[CAAM_ERROR_STR_MAX]; | ||
108 | |||
109 | dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); | ||
110 | } | ||
111 | |||
112 | atomic_set(&bd->empty, BUF_NOT_EMPTY); | ||
113 | complete(&bd->filled); | ||
114 | #ifdef DEBUG | ||
115 | print_hex_dump(KERN_ERR, "rng refreshed buf@: ", | ||
116 | DUMP_PREFIX_ADDRESS, 16, 4, bd->buf, RN_BUF_SIZE, 1); | ||
117 | #endif | ||
118 | } | ||
119 | |||
120 | static inline int submit_job(struct caam_rng_ctx *ctx, int to_current) | ||
121 | { | ||
122 | struct buf_data *bd = &ctx->bufs[!(to_current ^ ctx->current_buf)]; | ||
123 | struct device *jrdev = ctx->jrdev; | ||
124 | u32 *desc = bd->hw_desc; | ||
125 | int err; | ||
126 | |||
127 | dev_dbg(jrdev, "submitting job %d\n", !(to_current ^ ctx->current_buf)); | ||
128 | init_completion(&bd->filled); | ||
129 | err = caam_jr_enqueue(jrdev, desc, rng_done, ctx); | ||
130 | if (err) | ||
131 | complete(&bd->filled); /* don't wait on failed job*/ | ||
132 | else | ||
133 | atomic_inc(&bd->empty); /* note if pending */ | ||
134 | |||
135 | return err; | ||
136 | } | ||
137 | |||
138 | static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait) | ||
139 | { | ||
140 | struct caam_rng_ctx *ctx = &rng_ctx; | ||
141 | struct buf_data *bd = &ctx->bufs[ctx->current_buf]; | ||
142 | int next_buf_idx, copied_idx; | ||
143 | int err; | ||
144 | |||
145 | if (atomic_read(&bd->empty)) { | ||
146 | /* try to submit job if there wasn't one */ | ||
147 | if (atomic_read(&bd->empty) == BUF_EMPTY) { | ||
148 | err = submit_job(ctx, 1); | ||
149 | /* if can't submit job, can't even wait */ | ||
150 | if (err) | ||
151 | return 0; | ||
152 | } | ||
153 | /* no immediate data, so exit if not waiting */ | ||
154 | if (!wait) | ||
155 | return 0; | ||
156 | |||
157 | /* waiting for pending job */ | ||
158 | if (atomic_read(&bd->empty)) | ||
159 | wait_for_completion(&bd->filled); | ||
160 | } | ||
161 | |||
162 | next_buf_idx = ctx->cur_buf_idx + max; | ||
163 | dev_dbg(ctx->jrdev, "%s: start reading at buffer %d, idx %d\n", | ||
164 | __func__, ctx->current_buf, ctx->cur_buf_idx); | ||
165 | |||
166 | /* if enough data in current buffer */ | ||
167 | if (next_buf_idx < RN_BUF_SIZE) { | ||
168 | memcpy(data, bd->buf + ctx->cur_buf_idx, max); | ||
169 | ctx->cur_buf_idx = next_buf_idx; | ||
170 | return max; | ||
171 | } | ||
172 | |||
173 | /* else, copy what's left... */ | ||
174 | copied_idx = RN_BUF_SIZE - ctx->cur_buf_idx; | ||
175 | memcpy(data, bd->buf + ctx->cur_buf_idx, copied_idx); | ||
176 | ctx->cur_buf_idx = 0; | ||
177 | atomic_set(&bd->empty, BUF_EMPTY); | ||
178 | |||
179 | /* ...refill... */ | ||
180 | submit_job(ctx, 1); | ||
181 | |||
182 | /* and use next buffer */ | ||
183 | ctx->current_buf = !ctx->current_buf; | ||
184 | dev_dbg(ctx->jrdev, "switched to buffer %d\n", ctx->current_buf); | ||
185 | |||
186 | /* since there already is some data read, don't wait */ | ||
187 | return copied_idx + caam_read(rng, data + copied_idx, | ||
188 | max - copied_idx, false); | ||
189 | } | ||
190 | |||
191 | static inline void rng_create_sh_desc(struct caam_rng_ctx *ctx) | ||
192 | { | ||
193 | struct device *jrdev = ctx->jrdev; | ||
194 | u32 *desc = ctx->sh_desc; | ||
195 | |||
196 | init_sh_desc(desc, HDR_SHARE_WAIT); | ||
197 | |||
198 | /* Propagate errors from shared to job descriptor */ | ||
199 | append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD); | ||
200 | |||
201 | /* Generate random bytes */ | ||
202 | append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG); | ||
203 | |||
204 | /* Store bytes */ | ||
205 | append_seq_fifo_store(desc, RN_BUF_SIZE, FIFOST_TYPE_RNGSTORE); | ||
206 | |||
207 | ctx->sh_desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), | ||
208 | DMA_TO_DEVICE); | ||
209 | #ifdef DEBUG | ||
210 | print_hex_dump(KERN_ERR, "rng shdesc@: ", DUMP_PREFIX_ADDRESS, 16, 4, | ||
211 | desc, desc_bytes(desc), 1); | ||
212 | #endif | ||
213 | } | ||
214 | |||
215 | static inline void rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) | ||
216 | { | ||
217 | struct device *jrdev = ctx->jrdev; | ||
218 | struct buf_data *bd = &ctx->bufs[buf_id]; | ||
219 | u32 *desc = bd->hw_desc; | ||
220 | int sh_len = desc_len(ctx->sh_desc); | ||
221 | |||
222 | init_job_desc_shared(desc, ctx->sh_desc_dma, sh_len, HDR_SHARE_DEFER | | ||
223 | HDR_REVERSE); | ||
224 | |||
225 | bd->addr = dma_map_single(jrdev, bd->buf, RN_BUF_SIZE, DMA_FROM_DEVICE); | ||
226 | |||
227 | append_seq_out_ptr_intlen(desc, bd->addr, RN_BUF_SIZE, 0); | ||
228 | #ifdef DEBUG | ||
229 | print_hex_dump(KERN_ERR, "rng job desc@: ", DUMP_PREFIX_ADDRESS, 16, 4, | ||
230 | desc, desc_bytes(desc), 1); | ||
231 | #endif | ||
232 | } | ||
233 | |||
234 | static void caam_cleanup(struct hwrng *rng) | ||
235 | { | ||
236 | int i; | ||
237 | struct buf_data *bd; | ||
238 | |||
239 | for (i = 0; i < 2; i++) { | ||
240 | bd = &rng_ctx.bufs[i]; | ||
241 | if (atomic_read(&bd->empty) == BUF_PENDING) | ||
242 | wait_for_completion(&bd->filled); | ||
243 | } | ||
244 | |||
245 | rng_unmap_ctx(&rng_ctx); | ||
246 | } | ||
247 | |||
248 | static void caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) | ||
249 | { | ||
250 | struct buf_data *bd = &ctx->bufs[buf_id]; | ||
251 | |||
252 | rng_create_job_desc(ctx, buf_id); | ||
253 | atomic_set(&bd->empty, BUF_EMPTY); | ||
254 | submit_job(ctx, buf_id == ctx->current_buf); | ||
255 | wait_for_completion(&bd->filled); | ||
256 | } | ||
257 | |||
258 | static void caam_init_rng(struct caam_rng_ctx *ctx, struct device *jrdev) | ||
259 | { | ||
260 | ctx->jrdev = jrdev; | ||
261 | rng_create_sh_desc(ctx); | ||
262 | ctx->current_buf = 0; | ||
263 | ctx->cur_buf_idx = 0; | ||
264 | caam_init_buf(ctx, 0); | ||
265 | caam_init_buf(ctx, 1); | ||
266 | } | ||
267 | |||
268 | static struct hwrng caam_rng = { | ||
269 | .name = "rng-caam", | ||
270 | .cleanup = caam_cleanup, | ||
271 | .read = caam_read, | ||
272 | }; | ||
273 | |||
274 | static void __exit caam_rng_exit(void) | ||
275 | { | ||
276 | hwrng_unregister(&caam_rng); | ||
277 | } | ||
278 | |||
279 | static int __init caam_rng_init(void) | ||
280 | { | ||
281 | struct device_node *dev_node; | ||
282 | struct platform_device *pdev; | ||
283 | struct device *ctrldev; | ||
284 | struct caam_drv_private *priv; | ||
285 | |||
286 | dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); | ||
287 | if (!dev_node) | ||
288 | return -ENODEV; | ||
289 | |||
290 | pdev = of_find_device_by_node(dev_node); | ||
291 | if (!pdev) | ||
292 | return -ENODEV; | ||
293 | |||
294 | ctrldev = &pdev->dev; | ||
295 | priv = dev_get_drvdata(ctrldev); | ||
296 | of_node_put(dev_node); | ||
297 | |||
298 | caam_init_rng(&rng_ctx, priv->jrdev[0]); | ||
299 | |||
300 | dev_info(priv->jrdev[0], "registering rng-caam\n"); | ||
301 | return hwrng_register(&caam_rng); | ||
302 | } | ||
303 | |||
304 | module_init(caam_rng_init); | ||
305 | module_exit(caam_rng_exit); | ||
306 | |||
307 | MODULE_LICENSE("GPL"); | ||
308 | MODULE_DESCRIPTION("FSL CAAM support for hw_random API"); | ||
309 | MODULE_AUTHOR("Freescale Semiconductor - NMG"); | ||