diff options
author | Krzysztof Kozlowski <krzk@kernel.org> | 2016-03-21 21:58:24 -0400 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2016-04-05 08:35:46 -0400 |
commit | 9e4a1100a445671dd55ff74dce859221cc1464fa (patch) | |
tree | ec8bc3b1694e62490d7f590ea4b8ecdc696065e5 /drivers/crypto/s5p-sss.c | |
parent | 119c3ab4ed33c04782f02040a4bc686216788c53 (diff) |
crypto: s5p-sss - Handle unaligned buffers
During crypto selftests on Odroid XU3 (Exynos5422) some of the
algorithms failed because of passing AES-block unaligned source and
destination buffers:
alg: skcipher: encryption failed on chunk test 1 for ecb-aes-s5p: ret=22
Handle such case by copying the buffers to a new aligned and contiguous
space.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Vladimir Zapolskiy <vz@mleia.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/crypto/s5p-sss.c')
-rw-r--r-- | drivers/crypto/s5p-sss.c | 150 |
1 files changed, 138 insertions, 12 deletions
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c index 60f835455a41..3730fb0af4d8 100644 --- a/drivers/crypto/s5p-sss.c +++ b/drivers/crypto/s5p-sss.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <crypto/algapi.h> | 28 | #include <crypto/algapi.h> |
29 | #include <crypto/aes.h> | 29 | #include <crypto/aes.h> |
30 | #include <crypto/ctr.h> | 30 | #include <crypto/ctr.h> |
31 | #include <crypto/scatterwalk.h> | ||
31 | 32 | ||
32 | #define _SBF(s, v) ((v) << (s)) | 33 | #define _SBF(s, v) ((v) << (s)) |
33 | #define _BIT(b) _SBF(b, 1) | 34 | #define _BIT(b) _SBF(b, 1) |
@@ -185,6 +186,10 @@ struct s5p_aes_dev { | |||
185 | struct scatterlist *sg_src; | 186 | struct scatterlist *sg_src; |
186 | struct scatterlist *sg_dst; | 187 | struct scatterlist *sg_dst; |
187 | 188 | ||
189 | /* In case of unaligned access: */ | ||
190 | struct scatterlist *sg_src_cpy; | ||
191 | struct scatterlist *sg_dst_cpy; | ||
192 | |||
188 | struct tasklet_struct tasklet; | 193 | struct tasklet_struct tasklet; |
189 | struct crypto_queue queue; | 194 | struct crypto_queue queue; |
190 | bool busy; | 195 | bool busy; |
@@ -244,8 +249,45 @@ static void s5p_set_dma_outdata(struct s5p_aes_dev *dev, struct scatterlist *sg) | |||
244 | SSS_WRITE(dev, FCBTDMAL, sg_dma_len(sg)); | 249 | SSS_WRITE(dev, FCBTDMAL, sg_dma_len(sg)); |
245 | } | 250 | } |
246 | 251 | ||
252 | static void s5p_free_sg_cpy(struct s5p_aes_dev *dev, struct scatterlist **sg) | ||
253 | { | ||
254 | int len; | ||
255 | |||
256 | if (!*sg) | ||
257 | return; | ||
258 | |||
259 | len = ALIGN(dev->req->nbytes, AES_BLOCK_SIZE); | ||
260 | free_pages((unsigned long)sg_virt(*sg), get_order(len)); | ||
261 | |||
262 | kfree(*sg); | ||
263 | *sg = NULL; | ||
264 | } | ||
265 | |||
266 | static void s5p_sg_copy_buf(void *buf, struct scatterlist *sg, | ||
267 | unsigned int nbytes, int out) | ||
268 | { | ||
269 | struct scatter_walk walk; | ||
270 | |||
271 | if (!nbytes) | ||
272 | return; | ||
273 | |||
274 | scatterwalk_start(&walk, sg); | ||
275 | scatterwalk_copychunks(buf, &walk, nbytes, out); | ||
276 | scatterwalk_done(&walk, out, 0); | ||
277 | } | ||
278 | |||
247 | static void s5p_aes_complete(struct s5p_aes_dev *dev, int err) | 279 | static void s5p_aes_complete(struct s5p_aes_dev *dev, int err) |
248 | { | 280 | { |
281 | if (dev->sg_dst_cpy) { | ||
282 | dev_dbg(dev->dev, | ||
283 | "Copying %d bytes of output data back to original place\n", | ||
284 | dev->req->nbytes); | ||
285 | s5p_sg_copy_buf(sg_virt(dev->sg_dst_cpy), dev->req->dst, | ||
286 | dev->req->nbytes, 1); | ||
287 | } | ||
288 | s5p_free_sg_cpy(dev, &dev->sg_src_cpy); | ||
289 | s5p_free_sg_cpy(dev, &dev->sg_dst_cpy); | ||
290 | |||
249 | /* holding a lock outside */ | 291 | /* holding a lock outside */ |
250 | dev->req->base.complete(&dev->req->base, err); | 292 | dev->req->base.complete(&dev->req->base, err); |
251 | dev->busy = false; | 293 | dev->busy = false; |
@@ -261,14 +303,36 @@ static void s5p_unset_indata(struct s5p_aes_dev *dev) | |||
261 | dma_unmap_sg(dev->dev, dev->sg_src, 1, DMA_TO_DEVICE); | 303 | dma_unmap_sg(dev->dev, dev->sg_src, 1, DMA_TO_DEVICE); |
262 | } | 304 | } |
263 | 305 | ||
306 | static int s5p_make_sg_cpy(struct s5p_aes_dev *dev, struct scatterlist *src, | ||
307 | struct scatterlist **dst) | ||
308 | { | ||
309 | void *pages; | ||
310 | int len; | ||
311 | |||
312 | *dst = kmalloc(sizeof(**dst), GFP_ATOMIC); | ||
313 | if (!*dst) | ||
314 | return -ENOMEM; | ||
315 | |||
316 | len = ALIGN(dev->req->nbytes, AES_BLOCK_SIZE); | ||
317 | pages = (void *)__get_free_pages(GFP_ATOMIC, get_order(len)); | ||
318 | if (!pages) { | ||
319 | kfree(*dst); | ||
320 | *dst = NULL; | ||
321 | return -ENOMEM; | ||
322 | } | ||
323 | |||
324 | s5p_sg_copy_buf(pages, src, dev->req->nbytes, 0); | ||
325 | |||
326 | sg_init_table(*dst, 1); | ||
327 | sg_set_buf(*dst, pages, len); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
264 | static int s5p_set_outdata(struct s5p_aes_dev *dev, struct scatterlist *sg) | 332 | static int s5p_set_outdata(struct s5p_aes_dev *dev, struct scatterlist *sg) |
265 | { | 333 | { |
266 | int err; | 334 | int err; |
267 | 335 | ||
268 | if (!IS_ALIGNED(sg_dma_len(sg), AES_BLOCK_SIZE)) { | ||
269 | err = -EINVAL; | ||
270 | goto exit; | ||
271 | } | ||
272 | if (!sg_dma_len(sg)) { | 336 | if (!sg_dma_len(sg)) { |
273 | err = -EINVAL; | 337 | err = -EINVAL; |
274 | goto exit; | 338 | goto exit; |
@@ -291,10 +355,6 @@ static int s5p_set_indata(struct s5p_aes_dev *dev, struct scatterlist *sg) | |||
291 | { | 355 | { |
292 | int err; | 356 | int err; |
293 | 357 | ||
294 | if (!IS_ALIGNED(sg_dma_len(sg), AES_BLOCK_SIZE)) { | ||
295 | err = -EINVAL; | ||
296 | goto exit; | ||
297 | } | ||
298 | if (!sg_dma_len(sg)) { | 358 | if (!sg_dma_len(sg)) { |
299 | err = -EINVAL; | 359 | err = -EINVAL; |
300 | goto exit; | 360 | goto exit; |
@@ -394,6 +454,71 @@ static void s5p_set_aes(struct s5p_aes_dev *dev, | |||
394 | memcpy_toio(keystart, key, keylen); | 454 | memcpy_toio(keystart, key, keylen); |
395 | } | 455 | } |
396 | 456 | ||
457 | static bool s5p_is_sg_aligned(struct scatterlist *sg) | ||
458 | { | ||
459 | while (sg) { | ||
460 | if (!IS_ALIGNED(sg_dma_len(sg), AES_BLOCK_SIZE)) | ||
461 | return false; | ||
462 | sg = sg_next(sg); | ||
463 | } | ||
464 | |||
465 | return true; | ||
466 | } | ||
467 | |||
468 | static int s5p_set_indata_start(struct s5p_aes_dev *dev, | ||
469 | struct ablkcipher_request *req) | ||
470 | { | ||
471 | struct scatterlist *sg; | ||
472 | int err; | ||
473 | |||
474 | dev->sg_src_cpy = NULL; | ||
475 | sg = req->src; | ||
476 | if (!s5p_is_sg_aligned(sg)) { | ||
477 | dev_dbg(dev->dev, | ||
478 | "At least one unaligned source scatter list, making a copy\n"); | ||
479 | err = s5p_make_sg_cpy(dev, sg, &dev->sg_src_cpy); | ||
480 | if (err) | ||
481 | return err; | ||
482 | |||
483 | sg = dev->sg_src_cpy; | ||
484 | } | ||
485 | |||
486 | err = s5p_set_indata(dev, sg); | ||
487 | if (err) { | ||
488 | s5p_free_sg_cpy(dev, &dev->sg_src_cpy); | ||
489 | return err; | ||
490 | } | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int s5p_set_outdata_start(struct s5p_aes_dev *dev, | ||
496 | struct ablkcipher_request *req) | ||
497 | { | ||
498 | struct scatterlist *sg; | ||
499 | int err; | ||
500 | |||
501 | dev->sg_dst_cpy = NULL; | ||
502 | sg = req->dst; | ||
503 | if (!s5p_is_sg_aligned(sg)) { | ||
504 | dev_dbg(dev->dev, | ||
505 | "At least one unaligned dest scatter list, making a copy\n"); | ||
506 | err = s5p_make_sg_cpy(dev, sg, &dev->sg_dst_cpy); | ||
507 | if (err) | ||
508 | return err; | ||
509 | |||
510 | sg = dev->sg_dst_cpy; | ||
511 | } | ||
512 | |||
513 | err = s5p_set_outdata(dev, sg); | ||
514 | if (err) { | ||
515 | s5p_free_sg_cpy(dev, &dev->sg_dst_cpy); | ||
516 | return err; | ||
517 | } | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
397 | static void s5p_aes_crypt_start(struct s5p_aes_dev *dev, unsigned long mode) | 522 | static void s5p_aes_crypt_start(struct s5p_aes_dev *dev, unsigned long mode) |
398 | { | 523 | { |
399 | struct ablkcipher_request *req = dev->req; | 524 | struct ablkcipher_request *req = dev->req; |
@@ -430,19 +555,19 @@ static void s5p_aes_crypt_start(struct s5p_aes_dev *dev, unsigned long mode) | |||
430 | SSS_FCINTENCLR_BTDMAINTENCLR | SSS_FCINTENCLR_BRDMAINTENCLR); | 555 | SSS_FCINTENCLR_BTDMAINTENCLR | SSS_FCINTENCLR_BRDMAINTENCLR); |
431 | SSS_WRITE(dev, FCFIFOCTRL, 0x00); | 556 | SSS_WRITE(dev, FCFIFOCTRL, 0x00); |
432 | 557 | ||
433 | err = s5p_set_indata(dev, req->src); | 558 | err = s5p_set_indata_start(dev, req); |
434 | if (err) | 559 | if (err) |
435 | goto indata_error; | 560 | goto indata_error; |
436 | 561 | ||
437 | err = s5p_set_outdata(dev, req->dst); | 562 | err = s5p_set_outdata_start(dev, req); |
438 | if (err) | 563 | if (err) |
439 | goto outdata_error; | 564 | goto outdata_error; |
440 | 565 | ||
441 | SSS_AES_WRITE(dev, AES_CONTROL, aes_control); | 566 | SSS_AES_WRITE(dev, AES_CONTROL, aes_control); |
442 | s5p_set_aes(dev, dev->ctx->aes_key, req->info, dev->ctx->keylen); | 567 | s5p_set_aes(dev, dev->ctx->aes_key, req->info, dev->ctx->keylen); |
443 | 568 | ||
444 | s5p_set_dma_indata(dev, req->src); | 569 | s5p_set_dma_indata(dev, dev->sg_src); |
445 | s5p_set_dma_outdata(dev, req->dst); | 570 | s5p_set_dma_outdata(dev, dev->sg_dst); |
446 | 571 | ||
447 | SSS_WRITE(dev, FCINTENSET, | 572 | SSS_WRITE(dev, FCINTENSET, |
448 | SSS_FCINTENSET_BTDMAINTENSET | SSS_FCINTENSET_BRDMAINTENSET); | 573 | SSS_FCINTENSET_BTDMAINTENSET | SSS_FCINTENSET_BRDMAINTENSET); |
@@ -452,6 +577,7 @@ static void s5p_aes_crypt_start(struct s5p_aes_dev *dev, unsigned long mode) | |||
452 | return; | 577 | return; |
453 | 578 | ||
454 | outdata_error: | 579 | outdata_error: |
580 | s5p_free_sg_cpy(dev, &dev->sg_src_cpy); | ||
455 | s5p_unset_indata(dev); | 581 | s5p_unset_indata(dev); |
456 | 582 | ||
457 | indata_error: | 583 | indata_error: |