crypto: atmel - fix checks of error code returned by devm_ioremap_resource()
[cascardo/linux.git] / drivers / crypto / atmel-sha.c
index 20de861..4b2dd95 100644 (file)
@@ -53,6 +53,7 @@
 
 #define SHA_FLAGS_FINUP                BIT(16)
 #define SHA_FLAGS_SG           BIT(17)
+#define SHA_FLAGS_ALGO_MASK    GENMASK(22, 18)
 #define SHA_FLAGS_SHA1         BIT(18)
 #define SHA_FLAGS_SHA224       BIT(19)
 #define SHA_FLAGS_SHA256       BIT(20)
 #define SHA_FLAGS_SHA512       BIT(22)
 #define SHA_FLAGS_ERROR                BIT(23)
 #define SHA_FLAGS_PAD          BIT(24)
+#define SHA_FLAGS_RESTORE      BIT(25)
 
 #define SHA_OP_UPDATE  1
 #define SHA_OP_FINAL   2
 
-#define SHA_BUFFER_LEN         PAGE_SIZE
+#define SHA_BUFFER_LEN         (PAGE_SIZE / 16)
 
 #define ATMEL_SHA_DMA_THRESHOLD                56
 
@@ -73,10 +75,15 @@ struct atmel_sha_caps {
        bool    has_dualbuff;
        bool    has_sha224;
        bool    has_sha_384_512;
+       bool    has_uihv;
 };
 
 struct atmel_sha_dev;
 
+/*
+ * .statesize = sizeof(struct atmel_sha_reqctx) must be <= PAGE_SIZE / 8 as
+ * tested by the ahash_prepare_alg() function.
+ */
 struct atmel_sha_reqctx {
        struct atmel_sha_dev    *dd;
        unsigned long   flags;
@@ -95,7 +102,7 @@ struct atmel_sha_reqctx {
 
        size_t block_size;
 
-       u8      buffer[0] __aligned(sizeof(u32));
+       u8 buffer[SHA_BUFFER_LEN + SHA512_BLOCK_SIZE] __aligned(sizeof(u32));
 };
 
 struct atmel_sha_ctx {
@@ -122,6 +129,7 @@ struct atmel_sha_dev {
        spinlock_t              lock;
        int                     err;
        struct tasklet_struct   done_task;
+       struct tasklet_struct   queue_task;
 
        unsigned long           flags;
        struct crypto_queue     queue;
@@ -317,7 +325,8 @@ static int atmel_sha_init(struct ahash_request *req)
 static void atmel_sha_write_ctrl(struct atmel_sha_dev *dd, int dma)
 {
        struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req);
-       u32 valcr = 0, valmr = SHA_MR_MODE_AUTO;
+       u32 valmr = SHA_MR_MODE_AUTO;
+       unsigned int i, hashsize = 0;
 
        if (likely(dma)) {
                if (!dd->caps.has_dma)
@@ -329,22 +338,62 @@ static void atmel_sha_write_ctrl(struct atmel_sha_dev *dd, int dma)
                atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY);
        }
 
-       if (ctx->flags & SHA_FLAGS_SHA1)
+       switch (ctx->flags & SHA_FLAGS_ALGO_MASK) {
+       case SHA_FLAGS_SHA1:
                valmr |= SHA_MR_ALGO_SHA1;
-       else if (ctx->flags & SHA_FLAGS_SHA224)
+               hashsize = SHA1_DIGEST_SIZE;
+               break;
+
+       case SHA_FLAGS_SHA224:
                valmr |= SHA_MR_ALGO_SHA224;
-       else if (ctx->flags & SHA_FLAGS_SHA256)
+               hashsize = SHA256_DIGEST_SIZE;
+               break;
+
+       case SHA_FLAGS_SHA256:
                valmr |= SHA_MR_ALGO_SHA256;
-       else if (ctx->flags & SHA_FLAGS_SHA384)
+               hashsize = SHA256_DIGEST_SIZE;
+               break;
+
+       case SHA_FLAGS_SHA384:
                valmr |= SHA_MR_ALGO_SHA384;
-       else if (ctx->flags & SHA_FLAGS_SHA512)
+               hashsize = SHA512_DIGEST_SIZE;
+               break;
+
+       case SHA_FLAGS_SHA512:
                valmr |= SHA_MR_ALGO_SHA512;
+               hashsize = SHA512_DIGEST_SIZE;
+               break;
+
+       default:
+               break;
+       }
 
        /* Setting CR_FIRST only for the first iteration */
-       if (!(ctx->digcnt[0] || ctx->digcnt[1]))
-               valcr = SHA_CR_FIRST;
+       if (!(ctx->digcnt[0] || ctx->digcnt[1])) {
+               atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST);
+       } else if (dd->caps.has_uihv && (ctx->flags & SHA_FLAGS_RESTORE)) {
+               const u32 *hash = (const u32 *)ctx->digest;
+
+               /*
+                * Restore the hardware context: update the User Initialize
+                * Hash Value (UIHV) with the value saved when the latest
+                * 'update' operation completed on this very same crypto
+                * request.
+                */
+               ctx->flags &= ~SHA_FLAGS_RESTORE;
+               atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV);
+               for (i = 0; i < hashsize / sizeof(u32); ++i)
+                       atmel_sha_write(dd, SHA_REG_DIN(i), hash[i]);
+               atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST);
+               valmr |= SHA_MR_UIHV;
+       }
+       /*
+        * WARNING: If the UIHV feature is not available, the hardware CANNOT
+        * process concurrent requests: the internal registers used to store
+        * the hash/digest are still set to the partial digest output values
+        * computed during the latest round.
+        */
 
-       atmel_sha_write(dd, SHA_CR, valcr);
        atmel_sha_write(dd, SHA_MR, valmr);
 }
 
@@ -713,23 +762,31 @@ static void atmel_sha_copy_hash(struct ahash_request *req)
 {
        struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
        u32 *hash = (u32 *)ctx->digest;
-       int i;
+       unsigned int i, hashsize;
 
-       if (ctx->flags & SHA_FLAGS_SHA1)
-               for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++)
-                       hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
-       else if (ctx->flags & SHA_FLAGS_SHA224)
-               for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(u32); i++)
-                       hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
-       else if (ctx->flags & SHA_FLAGS_SHA256)
-               for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(u32); i++)
-                       hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
-       else if (ctx->flags & SHA_FLAGS_SHA384)
-               for (i = 0; i < SHA384_DIGEST_SIZE / sizeof(u32); i++)
-                       hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
-       else
-               for (i = 0; i < SHA512_DIGEST_SIZE / sizeof(u32); i++)
-                       hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
+       switch (ctx->flags & SHA_FLAGS_ALGO_MASK) {
+       case SHA_FLAGS_SHA1:
+               hashsize = SHA1_DIGEST_SIZE;
+               break;
+
+       case SHA_FLAGS_SHA224:
+       case SHA_FLAGS_SHA256:
+               hashsize = SHA256_DIGEST_SIZE;
+               break;
+
+       case SHA_FLAGS_SHA384:
+       case SHA_FLAGS_SHA512:
+               hashsize = SHA512_DIGEST_SIZE;
+               break;
+
+       default:
+               /* Should not happen... */
+               return;
+       }
+
+       for (i = 0; i < hashsize / sizeof(u32); ++i)
+               hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
+       ctx->flags |= SHA_FLAGS_RESTORE;
 }
 
 static void atmel_sha_copy_ready_hash(struct ahash_request *req)
@@ -788,7 +845,7 @@ static void atmel_sha_finish_req(struct ahash_request *req, int err)
                req->base.complete(&req->base, err);
 
        /* handle new request */
-       tasklet_schedule(&dd->done_task);
+       tasklet_schedule(&dd->queue_task);
 }
 
 static int atmel_sha_hw_init(struct atmel_sha_dev *dd)
@@ -922,36 +979,17 @@ static int atmel_sha_update(struct ahash_request *req)
 static int atmel_sha_final(struct ahash_request *req)
 {
        struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
-       struct atmel_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
-       struct atmel_sha_dev *dd = tctx->dd;
-
-       int err = 0;
 
        ctx->flags |= SHA_FLAGS_FINUP;
 
        if (ctx->flags & SHA_FLAGS_ERROR)
                return 0; /* uncompleted hash is not needed */
 
-       if (ctx->bufcnt) {
-               return atmel_sha_enqueue(req, SHA_OP_FINAL);
-       } else if (!(ctx->flags & SHA_FLAGS_PAD)) { /* add padding */
-               err = atmel_sha_hw_init(dd);
-               if (err)
-                       goto err1;
-
-               dd->flags |= SHA_FLAGS_BUSY;
-               err = atmel_sha_final_req(dd);
-       } else {
+       if (ctx->flags & SHA_FLAGS_PAD)
                /* copy ready hash (+ finalize hmac) */
                return atmel_sha_finish(req);
-       }
-
-err1:
-       if (err != -EINPROGRESS)
-               /* done_task will not finish it, so do it here */
-               atmel_sha_finish_req(req, err);
 
-       return err;
+       return atmel_sha_enqueue(req, SHA_OP_FINAL);
 }
 
 static int atmel_sha_finup(struct ahash_request *req)
@@ -979,11 +1017,27 @@ static int atmel_sha_digest(struct ahash_request *req)
        return atmel_sha_init(req) ?: atmel_sha_finup(req);
 }
 
+
+static int atmel_sha_export(struct ahash_request *req, void *out)
+{
+       const struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
+
+       memcpy(out, ctx, sizeof(*ctx));
+       return 0;
+}
+
+static int atmel_sha_import(struct ahash_request *req, const void *in)
+{
+       struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
+
+       memcpy(ctx, in, sizeof(*ctx));
+       return 0;
+}
+
 static int atmel_sha_cra_init(struct crypto_tfm *tfm)
 {
        crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
-                                sizeof(struct atmel_sha_reqctx) +
-                                SHA_BUFFER_LEN + SHA512_BLOCK_SIZE);
+                                sizeof(struct atmel_sha_reqctx));
 
        return 0;
 }
@@ -995,8 +1049,11 @@ static struct ahash_alg sha_1_256_algs[] = {
        .final          = atmel_sha_final,
        .finup          = atmel_sha_finup,
        .digest         = atmel_sha_digest,
+       .export         = atmel_sha_export,
+       .import         = atmel_sha_import,
        .halg = {
                .digestsize     = SHA1_DIGEST_SIZE,
+               .statesize      = sizeof(struct atmel_sha_reqctx),
                .base   = {
                        .cra_name               = "sha1",
                        .cra_driver_name        = "atmel-sha1",
@@ -1016,8 +1073,11 @@ static struct ahash_alg sha_1_256_algs[] = {
        .final          = atmel_sha_final,
        .finup          = atmel_sha_finup,
        .digest         = atmel_sha_digest,
+       .export         = atmel_sha_export,
+       .import         = atmel_sha_import,
        .halg = {
                .digestsize     = SHA256_DIGEST_SIZE,
+               .statesize      = sizeof(struct atmel_sha_reqctx),
                .base   = {
                        .cra_name               = "sha256",
                        .cra_driver_name        = "atmel-sha256",
@@ -1039,8 +1099,11 @@ static struct ahash_alg sha_224_alg = {
        .final          = atmel_sha_final,
        .finup          = atmel_sha_finup,
        .digest         = atmel_sha_digest,
+       .export         = atmel_sha_export,
+       .import         = atmel_sha_import,
        .halg = {
                .digestsize     = SHA224_DIGEST_SIZE,
+               .statesize      = sizeof(struct atmel_sha_reqctx),
                .base   = {
                        .cra_name               = "sha224",
                        .cra_driver_name        = "atmel-sha224",
@@ -1062,8 +1125,11 @@ static struct ahash_alg sha_384_512_algs[] = {
        .final          = atmel_sha_final,
        .finup          = atmel_sha_finup,
        .digest         = atmel_sha_digest,
+       .export         = atmel_sha_export,
+       .import         = atmel_sha_import,
        .halg = {
                .digestsize     = SHA384_DIGEST_SIZE,
+               .statesize      = sizeof(struct atmel_sha_reqctx),
                .base   = {
                        .cra_name               = "sha384",
                        .cra_driver_name        = "atmel-sha384",
@@ -1083,8 +1149,11 @@ static struct ahash_alg sha_384_512_algs[] = {
        .final          = atmel_sha_final,
        .finup          = atmel_sha_finup,
        .digest         = atmel_sha_digest,
+       .export         = atmel_sha_export,
+       .import         = atmel_sha_import,
        .halg = {
                .digestsize     = SHA512_DIGEST_SIZE,
+               .statesize      = sizeof(struct atmel_sha_reqctx),
                .base   = {
                        .cra_name               = "sha512",
                        .cra_driver_name        = "atmel-sha512",
@@ -1100,16 +1169,18 @@ static struct ahash_alg sha_384_512_algs[] = {
 },
 };
 
+static void atmel_sha_queue_task(unsigned long data)
+{
+       struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data;
+
+       atmel_sha_handle_queue(dd, NULL);
+}
+
 static void atmel_sha_done_task(unsigned long data)
 {
        struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data;
        int err = 0;
 
-       if (!(SHA_FLAGS_BUSY & dd->flags)) {
-               atmel_sha_handle_queue(dd, NULL);
-               return;
-       }
-
        if (SHA_FLAGS_CPU & dd->flags) {
                if (SHA_FLAGS_OUTPUT_READY & dd->flags) {
                        dd->flags &= ~SHA_FLAGS_OUTPUT_READY;
@@ -1272,14 +1343,23 @@ static void atmel_sha_get_cap(struct atmel_sha_dev *dd)
        dd->caps.has_dualbuff = 0;
        dd->caps.has_sha224 = 0;
        dd->caps.has_sha_384_512 = 0;
+       dd->caps.has_uihv = 0;
 
        /* keep only major version number */
        switch (dd->hw_version & 0xff0) {
+       case 0x510:
+               dd->caps.has_dma = 1;
+               dd->caps.has_dualbuff = 1;
+               dd->caps.has_sha224 = 1;
+               dd->caps.has_sha_384_512 = 1;
+               dd->caps.has_uihv = 1;
+               break;
        case 0x420:
                dd->caps.has_dma = 1;
                dd->caps.has_dualbuff = 1;
                dd->caps.has_sha224 = 1;
                dd->caps.has_sha_384_512 = 1;
+               dd->caps.has_uihv = 1;
                break;
        case 0x410:
                dd->caps.has_dma = 1;
@@ -1366,6 +1446,8 @@ static int atmel_sha_probe(struct platform_device *pdev)
 
        tasklet_init(&sha_dd->done_task, atmel_sha_done_task,
                                        (unsigned long)sha_dd);
+       tasklet_init(&sha_dd->queue_task, atmel_sha_queue_task,
+                                       (unsigned long)sha_dd);
 
        crypto_init_queue(&sha_dd->queue, ATMEL_SHA_QUEUE_LENGTH);
 
@@ -1404,9 +1486,9 @@ static int atmel_sha_probe(struct platform_device *pdev)
        }
 
        sha_dd->io_base = devm_ioremap_resource(&pdev->dev, sha_res);
-       if (!sha_dd->io_base) {
+       if (IS_ERR(sha_dd->io_base)) {
                dev_err(dev, "can't ioremap\n");
-               err = -ENOMEM;
+               err = PTR_ERR(sha_dd->io_base);
                goto res_err;
        }
 
@@ -1458,6 +1540,7 @@ err_algs:
                atmel_sha_dma_cleanup(sha_dd);
 err_sha_dma:
 res_err:
+       tasklet_kill(&sha_dd->queue_task);
        tasklet_kill(&sha_dd->done_task);
 sha_dd_err:
        dev_err(dev, "initialization failed.\n");
@@ -1478,6 +1561,7 @@ static int atmel_sha_remove(struct platform_device *pdev)
 
        atmel_sha_unregister_algs(sha_dd);
 
+       tasklet_kill(&sha_dd->queue_task);
        tasklet_kill(&sha_dd->done_task);
 
        if (sha_dd->caps.has_dma)