aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/crypto/s5p-sss.c
diff options
context:
space:
mode:
authorKrzysztof Kozlowski <krzk@kernel.org>2016-03-21 21:58:24 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2016-04-05 08:35:46 -0400
commit9e4a1100a445671dd55ff74dce859221cc1464fa (patch)
treeec8bc3b1694e62490d7f590ea4b8ecdc696065e5 /drivers/crypto/s5p-sss.c
parent119c3ab4ed33c04782f02040a4bc686216788c53 (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.c150
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
252static 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
266static 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
247static void s5p_aes_complete(struct s5p_aes_dev *dev, int err) 279static 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
306static 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
264static int s5p_set_outdata(struct s5p_aes_dev *dev, struct scatterlist *sg) 332static 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
457static 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
468static 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
495static 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
397static void s5p_aes_crypt_start(struct s5p_aes_dev *dev, unsigned long mode) 522static 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
454outdata_error: 579outdata_error:
580 s5p_free_sg_cpy(dev, &dev->sg_src_cpy);
455 s5p_unset_indata(dev); 581 s5p_unset_indata(dev);
456 582
457indata_error: 583indata_error: