FIT configuration signatures hash multiple discontiguous regions through
the legacy progressive hash interface. This still requires a software
implementation even when image hashes use a driver-model hardware
provider.

Add provider selection for progressive driver-model hashing and use it
from hash_calculate(). Allow SPL_SHA256_LEGACY to be disabled explicitly,
link the SHA-256 software support only for a selected software backend,
and make legacy fallback paths reject an algorithm whose software
callbacks are absent.

Make a successful driver-model initialization require the complete init,
update, finish and abort lifecycle. Add the abort operation to existing
providers and use it to release a provider context after any update
failure.

Enable DM_HASH in the sandbox configuration so these driver-model
paths are built and exercised by the standard sandbox tests. Extend
the hash provider-selection test to cover progressive initialization
and error cleanup.

This permits SPL to retain SHA-256 FIT support while relying exclusively
on a hardware hash provider.

Reviewed-by: Simon Glass <[email protected]>
Signed-off-by: James Hilliard <[email protected]>
---
Changes v7 -> v8:
  - Add an explicit abort operation and release provider contexts after
    update failures  (suggested by Simon Glass)
  - Add the progressive provider-selection and error-cleanup test omitted
    from v7
  - Enable DM_HASH in sandbox so the hash provider tests are built and run
    by normal sandbox CI

Changes v6 -> v7:
  - New patch
---
 boot/image-fit.c                    |   2 +-
 common/hash.c                       |  21 +++++-
 configs/sandbox_defconfig           |   1 +
 drivers/crypto/aspeed/aspeed_hace.c |  18 ++++-
 drivers/crypto/aspeed/cptra_sha.c   |  34 +++++++---
 drivers/crypto/hash/hash-uclass.c   |  44 +++++++++++-
 drivers/crypto/hash/hash_sw.c       |  20 ++++--
 include/u-boot/hash.h               |  30 ++++++++
 lib/Makefile                        |   4 +-
 lib/hash-checksum.c                 |  30 +++++++-
 lib/mbedtls/Kconfig                 |   2 +-
 test/dm/hash.c                      | 132 ++++++++++++++++++++++++++++++++++++
 12 files changed, 312 insertions(+), 26 deletions(-)

diff --git a/boot/image-fit.c b/boot/image-fit.c
index 9b39696de2d..86ebf58680d 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -1342,7 +1342,7 @@ int calculate_hash(const void *data, int data_len, const 
char *name,
 #endif
 
        ret = hash_lookup_algo(name, &algo);
-       if (ret < 0) {
+       if (ret < 0 || !algo->hash_func_ws) {
                debug("Unsupported hash algorithm\n");
                return -1;
        }
diff --git a/common/hash.c b/common/hash.c
index 5cbb4926c1d..8bbf9aa4825 100644
--- a/common/hash.c
+++ b/common/hash.c
@@ -38,6 +38,14 @@
 #include <u-boot/md5.h>
 #include <u-boot/sm3.h>
 
+#ifdef USE_HOSTCC
+#define SHA256_SOFTWARE_ENABLED 1
+#else
+#define SHA256_SOFTWARE_ENABLED \
+       (CONFIG_IS_ENABLED(SHA256_LEGACY) || \
+        CONFIG_IS_ENABLED(SHA256_MBEDTLS))
+#endif
+
 static int __maybe_unused hash_init_sha1(struct hash_algo *algo, void **ctxp)
 {
        sha1_context *ctx = malloc(sizeof(sha1_context));
@@ -65,6 +73,7 @@ static int __maybe_unused hash_finish_sha1(struct hash_algo 
*algo, void *ctx,
        return 0;
 }
 
+#if SHA256_SOFTWARE_ENABLED
 static int __maybe_unused hash_init_sha256(struct hash_algo *algo, void **ctxp)
 {
        sha256_context *ctx = malloc(sizeof(sha256_context));
@@ -91,6 +100,7 @@ static int __maybe_unused hash_finish_sha256(struct 
hash_algo *algo, void *ctx,
        free(ctx);
        return 0;
 }
+#endif
 
 static int __maybe_unused hash_init_sha384(struct hash_algo *algo, void **ctxp)
 {
@@ -273,14 +283,14 @@ static struct hash_algo hash_algo[] = {
                .chunk_size     = CHUNKSZ_SHA256,
 #if CONFIG_IS_ENABLED(SHA_HW_ACCEL)
                .hash_func_ws   = hw_sha256,
-#else
+#elif SHA256_SOFTWARE_ENABLED
                .hash_func_ws   = sha256_csum_wd,
 #endif
 #if CONFIG_IS_ENABLED(SHA_PROG_HW_ACCEL)
                .hash_init      = hw_sha_init,
                .hash_update    = hw_sha_update,
                .hash_finish    = hw_sha_finish,
-#else
+#elif SHA256_SOFTWARE_ENABLED
                .hash_init      = hash_init_sha256,
                .hash_update    = hash_update_sha256,
                .hash_finish    = hash_finish_sha256,
@@ -634,6 +644,13 @@ int hash_command(const char *algo_name, int flags, struct 
cmd_tbl *cmdtp,
                                        goto done;
                        }
                }
+               if (!algo->hash_func_ws) {
+                       printf("Hash algorithm '%s' has no available 
provider\n",
+                              algo_name);
+                       unmap_sysmem(buf);
+                       free(output);
+                       return CMD_RET_FAILURE;
+               }
                algo->hash_func_ws(buf, len, output, algo->chunk_size);
 done:
                unmap_sysmem(buf);
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 79f46317e45..1d9542f815d 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -198,6 +198,7 @@ CONFIG_CLK_K210_SET_RATE=y
 CONFIG_SANDBOX_CLK_CCF=y
 CONFIG_CLK_SCMI=y
 CONFIG_CPU=y
+CONFIG_DM_HASH=y
 CONFIG_DM_AES=y
 CONFIG_AES_SOFTWARE=y
 CONFIG_DM_DEMO=y
diff --git a/drivers/crypto/aspeed/aspeed_hace.c 
b/drivers/crypto/aspeed/aspeed_hace.c
index 2469f53472f..3be1809c89e 100644
--- a/drivers/crypto/aspeed/aspeed_hace.c
+++ b/drivers/crypto/aspeed/aspeed_hace.c
@@ -205,6 +205,13 @@ static int aspeed_hace_update(struct udevice *dev, void 
*ctx, const void *ibuf,
        return 0;
 }
 
+static int aspeed_hace_abort(struct udevice *dev, void *ctx)
+{
+       free(ctx);
+
+       return 0;
+}
+
 static int aspeed_hace_finish(struct udevice *dev, void *ctx, void *obuf)
 {
        int rc = 0;
@@ -269,7 +276,7 @@ static int aspeed_hace_finish(struct udevice *dev, void 
*ctx, void *obuf)
        memcpy(obuf, hace_ctx->digest, hash_algo_digest_size(hace_ctx->algo));
 
 free_n_out:
-       free(ctx);
+       aspeed_hace_abort(dev, ctx);
 
        return rc;
 }
@@ -297,16 +304,20 @@ static int aspeed_hace_digest_wd(struct udevice *dev, 
enum HASH_ALGO algo,
                                chunk = chunk_sz;
 
                        rc = aspeed_hace_update(dev, ctx, cur, chunk);
-                       if (rc)
+                       if (rc) {
+                               aspeed_hace_abort(dev, ctx);
                                return rc;
+                       }
 
                        cur += chunk;
                        schedule();
                }
        } else {
                rc = aspeed_hace_update(dev, ctx, ibuf, ilen);
-               if (rc)
+               if (rc) {
+                       aspeed_hace_abort(dev, ctx);
                        return rc;
+               }
        }
 
        rc = aspeed_hace_finish(dev, ctx, obuf);
@@ -359,6 +370,7 @@ static const struct hash_ops aspeed_hace_ops = {
        .hash_init = aspeed_hace_init,
        .hash_update = aspeed_hace_update,
        .hash_finish = aspeed_hace_finish,
+       .hash_abort = aspeed_hace_abort,
        .hash_digest_wd = aspeed_hace_digest_wd,
        .hash_digest = aspeed_hace_digest,
 };
diff --git a/drivers/crypto/aspeed/cptra_sha.c 
b/drivers/crypto/aspeed/cptra_sha.c
index 0dc00f306f1..72ea3860eb5 100644
--- a/drivers/crypto/aspeed/cptra_sha.c
+++ b/drivers/crypto/aspeed/cptra_sha.c
@@ -75,8 +75,11 @@ static int cptra_sha_init(struct udevice *dev, enum 
HASH_ALGO algo, void **ctxp)
        cs = dev_get_priv(dev);
 
        /* get CPTRA SHA lock */
-       if (readl_poll_timeout(cs->regs + CPTRA_SHA_LOCK, reg, reg == 0, 
1000000))
-               return -EBUSY;
+       if (readl_poll_timeout(cs->regs + CPTRA_SHA_LOCK, reg, reg == 0,
+                              1000000)) {
+               rc = -EBUSY;
+               goto free_n_out;
+       }
 
        /* zero clear SHA */
        writel(CPTRA_SHA_CTRL_ZEROIZE, cs->regs + CPTRA_SHA_CTRL);
@@ -133,6 +136,17 @@ static int cptra_sha_update(struct udevice *dev, void 
*ctx, const void *ibuf, ui
        return 0;
 }
 
+static int cptra_sha_abort(struct udevice *dev, void *ctx)
+{
+       struct cptra_sha *cs = dev_get_priv(dev);
+
+       writel(CPTRA_SHA_CTRL_ZEROIZE, cs->regs + CPTRA_SHA_CTRL);
+       writel(0x1, cs->regs + CPTRA_SHA_LOCK);
+       free(ctx);
+
+       return 0;
+}
+
 static int cptra_sha_finish(struct udevice *dev, void *ctx, void *obuf)
 {
        struct cptra_sha_ctx *cs_ctx;
@@ -158,12 +172,7 @@ static int cptra_sha_finish(struct udevice *dev, void 
*ctx, void *obuf)
        for (i = 0; i < (cs_ctx->dgst_len / sizeof(*p32)); ++i, p32++)
                *p32 = be32_to_cpu(readl(cs->regs + CPTRA_SHA_DIGEST(i)));
 
-       /* release CPTRA SHA lock */
-       writel(0x1, cs->regs + CPTRA_SHA_LOCK);
-
-       free(cs_ctx);
-
-       return 0;
+       return cptra_sha_abort(dev, ctx);
 }
 
 static int cptra_sha_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
@@ -189,16 +198,20 @@ static int cptra_sha_digest_wd(struct udevice *dev, enum 
HASH_ALGO algo,
                                chunk = chunk_sz;
 
                        rc = cptra_sha_update(dev, ctx, cur, chunk);
-                       if (rc)
+                       if (rc) {
+                               cptra_sha_abort(dev, ctx);
                                return rc;
+                       }
 
                        cur += chunk;
                        schedule();
                }
        } else {
                rc = cptra_sha_update(dev, ctx, ibuf, ilen);
-               if (rc)
+               if (rc) {
+                       cptra_sha_abort(dev, ctx);
                        return rc;
+               }
        }
 
        rc = cptra_sha_finish(dev, ctx, obuf);
@@ -237,6 +250,7 @@ static const struct hash_ops cptra_sha_ops = {
        .hash_init = cptra_sha_init,
        .hash_update = cptra_sha_update,
        .hash_finish = cptra_sha_finish,
+       .hash_abort = cptra_sha_abort,
        .hash_digest_wd = cptra_sha_digest_wd,
        .hash_digest = cptra_sha_digest,
 };
diff --git a/drivers/crypto/hash/hash-uclass.c 
b/drivers/crypto/hash/hash-uclass.c
index ffca19af2de..0340eecf3f6 100644
--- a/drivers/crypto/hash/hash-uclass.c
+++ b/drivers/crypto/hash/hash-uclass.c
@@ -119,11 +119,43 @@ int hash_digest_wd_lookup(enum HASH_ALGO algo, const void 
*ibuf,
        return found ? -EOPNOTSUPP : -ENODEV;
 }
 
+int hash_init_lookup(enum HASH_ALGO algo, struct udevice **devp, void **ctxp)
+{
+       struct udevice *dev;
+       int first_probe_err = 0;
+       bool found = false;
+       int ret;
+
+       for (ret = uclass_first_device_check(UCLASS_HASH, &dev); dev;
+            ret = uclass_next_device_check(&dev)) {
+               found = true;
+               if (ret) {
+                       if (!first_probe_err)
+                               first_probe_err = ret;
+                       continue;
+               }
+
+               ret = hash_init(dev, algo, ctxp);
+               if (!ret) {
+                       *devp = dev;
+                       return 0;
+               }
+               if (!hash_op_unsupported(ret))
+                       return ret;
+       }
+
+       if (first_probe_err)
+               return first_probe_err;
+
+       return found ? -EOPNOTSUPP : -ENODEV;
+}
+
 int hash_init(struct udevice *dev, enum HASH_ALGO algo, void **ctxp)
 {
        struct hash_ops *ops = (struct hash_ops *)device_get_ops(dev);
 
-       if (!ops->hash_init)
+       if (!ops->hash_init || !ops->hash_update || !ops->hash_finish ||
+           !ops->hash_abort)
                return -ENOSYS;
 
        return ops->hash_init(dev, algo, ctxp);
@@ -149,6 +181,16 @@ int hash_finish(struct udevice *dev, void *ctx, void *obuf)
        return ops->hash_finish(dev, ctx, obuf);
 }
 
+int hash_abort(struct udevice *dev, void *ctx)
+{
+       struct hash_ops *ops = (struct hash_ops *)device_get_ops(dev);
+
+       if (!ops->hash_abort)
+               return -ENOSYS;
+
+       return ops->hash_abort(dev, ctx);
+}
+
 UCLASS_DRIVER(hash) = {
        .id     = UCLASS_HASH,
        .name   = "hash",
diff --git a/drivers/crypto/hash/hash_sw.c b/drivers/crypto/hash/hash_sw.c
index 4590e225481..63a775d904f 100644
--- a/drivers/crypto/hash/hash_sw.c
+++ b/drivers/crypto/hash/hash_sw.c
@@ -218,6 +218,13 @@ static int sw_hash_update(struct udevice *dev, void *ctx, 
const void *ibuf, uint
        return 0;
 }
 
+static int sw_hash_abort(struct udevice *dev, void *ctx)
+{
+       free(ctx);
+
+       return 0;
+}
+
 static int sw_hash_finish(struct udevice *dev, void *ctx, void *obuf)
 {
        struct sw_hash_ctx *hash_ctx = ctx;
@@ -225,9 +232,7 @@ static int sw_hash_finish(struct udevice *dev, void *ctx, 
void *obuf)
 
        hash_impl->finish(hash_ctx->algo_ctx, obuf);
 
-       free(ctx);
-
-       return 0;
+       return sw_hash_abort(dev, ctx);
 }
 
 static int sw_hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
@@ -253,16 +258,20 @@ static int sw_hash_digest_wd(struct udevice *dev, enum 
HASH_ALGO algo,
                                chunk = chunk_sz;
 
                        rc = sw_hash_update(dev, ctx, cur, chunk);
-                       if (rc)
+                       if (rc) {
+                               sw_hash_abort(dev, ctx);
                                return rc;
+                       }
 
                        cur += chunk;
                        schedule();
                }
        } else {
                rc = sw_hash_update(dev, ctx, ibuf, ilen);
-               if (rc)
+               if (rc) {
+                       sw_hash_abort(dev, ctx);
                        return rc;
+               }
        }
 
        rc = sw_hash_finish(dev, ctx, obuf);
@@ -284,6 +293,7 @@ static const struct hash_ops hash_ops_sw = {
        .hash_init = sw_hash_init,
        .hash_update = sw_hash_update,
        .hash_finish = sw_hash_finish,
+       .hash_abort = sw_hash_abort,
        .hash_digest_wd = sw_hash_digest_wd,
        .hash_digest = sw_hash_digest,
 };
diff --git a/include/u-boot/hash.h b/include/u-boot/hash.h
index 7dba66047f5..f07363845e3 100644
--- a/include/u-boot/hash.h
+++ b/include/u-boot/hash.h
@@ -51,10 +51,39 @@ int hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
  */
 int hash_digest_wd_lookup(enum HASH_ALGO algo, const void *ibuf,
                          const u32 ilen, void *obuf, u32 chunk_sz);
+/**
+ * hash_init_lookup() - Start hashing with the first supporting provider
+ *
+ * Probe each hash device in order and initialize the first one which supports
+ * @algo. The selected device is returned for the matching update and finish
+ * operations.
+ *
+ * @algo: Hash algorithm
+ * @devp: Returns the selected hash device
+ * @ctxp: Returns the provider's progressive-hash context
+ * Return: 0 on success, -ENODEV if there are no providers, -EOPNOTSUPP if no
+ * provider supports @algo, or another negative error from a provider
+ */
+int hash_init_lookup(enum HASH_ALGO algo, struct udevice **devp, void **ctxp);
+
+/*
+ * A successful hash_init() returns a context which must be consumed by
+ * exactly one hash_finish() or hash_abort() call. Both operations release all
+ * provider resources, including when they return an error.
+ */
 int hash_init(struct udevice *dev, enum HASH_ALGO algo, void **ctxp);
 int hash_update(struct udevice *dev, void *ctx, const void *ibuf, const 
uint32_t ilen);
 int hash_finish(struct udevice *dev, void *ctx, void *obuf);
 
+/**
+ * hash_abort() - Discard a progressive hash operation
+ *
+ * @dev: Hash device selected by hash_init() or hash_init_lookup()
+ * @ctx: Progressive-hash context to release
+ * Return: 0 on success, or a negative provider error
+ */
+int hash_abort(struct udevice *dev, void *ctx);
+
 /*
  * struct hash_ops - Driver model for Hash operations
  *
@@ -66,6 +95,7 @@ struct hash_ops {
        int (*hash_init)(struct udevice *dev, enum HASH_ALGO algo, void **ctxp);
        int (*hash_update)(struct udevice *dev, void *ctx, const void *ibuf, 
const uint32_t ilen);
        int (*hash_finish)(struct udevice *dev, void *ctx, void *obuf);
+       int (*hash_abort)(struct udevice *dev, void *ctx);
 
        /* all-in-one operation */
        int (*hash_digest)(struct udevice *dev, enum HASH_ALGO algo,
diff --git a/lib/Makefile b/lib/Makefile
index 222378a8531..777c48a5728 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -79,8 +79,8 @@ obj-$(CONFIG_BLAKE2) += blake2/blake2b.o
 
 obj-$(CONFIG_$(PHASE_)MD5_LEGACY) += md5.o
 obj-$(CONFIG_$(PHASE_)SHA1_LEGACY) += sha1.o
-obj-$(CONFIG_$(PHASE_)SHA256) += sha256_common.o
-obj-$(CONFIG_$(PHASE_)SHA256_LEGACY) += sha256.o
+obj-$(CONFIG_$(PHASE_)SHA256_LEGACY) += sha256_common.o sha256.o
+obj-$(CONFIG_$(PHASE_)SHA256_MBEDTLS) += sha256_common.o
 obj-$(CONFIG_$(PHASE_)SHA512_LEGACY) += sha512.o
 obj-$(CONFIG_$(PHASE_)SM3) += sm3.o
 
diff --git a/lib/hash-checksum.c b/lib/hash-checksum.c
index 1970a741294..e3e7f6adb7f 100644
--- a/lib/hash-checksum.c
+++ b/lib/hash-checksum.c
@@ -4,11 +4,13 @@
  */
 
 #ifndef USE_HOSTCC
+#include <dm.h>
 #include <fdtdec.h>
 #include <asm/byteorder.h>
 #include <linux/errno.h>
 #include <asm/unaligned.h>
 #include <hash.h>
+#include <u-boot/hash.h>
 #else
 #include "fdt_host.h"
 #endif
@@ -20,13 +22,39 @@ int hash_calculate(const char *name,
                    int region_count, uint8_t *checksum)
 {
        struct hash_algo *algo;
-       int ret = 0;
+       int ret;
        void *ctx;
        int i;
 
        if (region_count < 1)
                return -EINVAL;
 
+#ifndef USE_HOSTCC
+       if (CONFIG_IS_ENABLED(DM_HASH)) {
+               enum HASH_ALGO hash_algo = hash_algo_lookup_by_name(name);
+               struct udevice *dev;
+
+               if (hash_algo != HASH_ALGO_INVALID)
+                       ret = hash_init_lookup(hash_algo, &dev, &ctx);
+               else
+                       ret = -EOPNOTSUPP;
+               if (!ret) {
+                       for (i = 0; i < region_count; i++) {
+                               ret = hash_update(dev, ctx, region[i].data,
+                                                 region[i].size);
+                               if (ret) {
+                                       hash_abort(dev, ctx);
+                                       return ret;
+                               }
+                       }
+
+                       return hash_finish(dev, ctx, checksum);
+               }
+               if (ret != -ENODEV && ret != -EOPNOTSUPP)
+                       return ret;
+       }
+#endif
+
        ret = hash_progressive_lookup_algo(name, &algo);
        if (ret)
                return ret;
diff --git a/lib/mbedtls/Kconfig b/lib/mbedtls/Kconfig
index 789721ee6cd..a805b1d4a48 100644
--- a/lib/mbedtls/Kconfig
+++ b/lib/mbedtls/Kconfig
@@ -335,7 +335,7 @@ config SPL_LEGACY_HASHING
        bool "Use U-Boot legacy hashing libraries (SPL)"
        select SPL_MD5_LEGACY if SPL_MD5
        select SPL_SHA1_LEGACY if SPL_SHA1
-       select SPL_SHA256_LEGACY if SPL_SHA256
+       imply SPL_SHA256_LEGACY if SPL_SHA256
        select SPL_SHA512_LEGACY if SPL_SHA512
        select SPL_SHA384_LEGACY if SPL_SHA384
        help
diff --git a/test/dm/hash.c b/test/dm/hash.c
index fe949e33de5..b5ab80d0525 100644
--- a/test/dm/hash.c
+++ b/test/dm/hash.c
@@ -10,13 +10,83 @@
 #include <dm/root.h>
 #include <dm/test.h>
 #include <dm/uclass-internal.h>
+#include <image.h>
 #include <u-boot/hash.h>
+#include <u-boot/hash-checksum.h>
 #include <test/test.h>
 #include <test/ut.h>
 
 static int unsupported_calls;
 static int success_calls;
 static int hard_error_calls;
+static int unsupported_init_calls;
+static int success_init_calls;
+static int hard_error_init_calls;
+static int update_error_calls;
+static int finish_calls;
+static int abort_calls;
+
+static int hash_test_unsupported_init(struct udevice *dev,
+                                     enum HASH_ALGO algo, void **ctxp)
+{
+       unsupported_init_calls++;
+
+       return -EOPNOTSUPP;
+}
+
+static int hash_test_success_init(struct udevice *dev, enum HASH_ALGO algo,
+                                 void **ctxp)
+{
+       success_init_calls++;
+       *ctxp = dev;
+
+       return 0;
+}
+
+static int hash_test_hard_error_init(struct udevice *dev,
+                                    enum HASH_ALGO algo, void **ctxp)
+{
+       hard_error_init_calls++;
+
+       return -EINVAL;
+}
+
+static int hash_test_update_error_init(struct udevice *dev,
+                                      enum HASH_ALGO algo, void **ctxp)
+{
+       *ctxp = dev;
+
+       return 0;
+}
+
+static int hash_test_progressive_update(struct udevice *dev, void *ctx,
+                                       const void *ibuf, u32 ilen)
+{
+       return 0;
+}
+
+static int hash_test_update_error(struct udevice *dev, void *ctx,
+                                 const void *ibuf, u32 ilen)
+{
+       update_error_calls++;
+
+       return -EIO;
+}
+
+static int hash_test_progressive_finish(struct udevice *dev, void *ctx,
+                                       void *obuf)
+{
+       finish_calls++;
+
+       return 0;
+}
+
+static int hash_test_progressive_abort(struct udevice *dev, void *ctx)
+{
+       abort_calls++;
+
+       return 0;
+}
 
 static int hash_test_unsupported(struct udevice *dev, enum HASH_ALGO algo,
                                 const void *ibuf, const uint32_t ilen,
@@ -47,17 +117,36 @@ static int hash_test_hard_error(struct udevice *dev, enum 
HASH_ALGO algo,
 }
 
 static const struct hash_ops hash_test_unsupported_ops = {
+       .hash_init = hash_test_unsupported_init,
+       .hash_update = hash_test_progressive_update,
+       .hash_finish = hash_test_progressive_finish,
+       .hash_abort = hash_test_progressive_abort,
        .hash_digest_wd = hash_test_unsupported,
 };
 
 static const struct hash_ops hash_test_success_ops = {
+       .hash_init = hash_test_success_init,
+       .hash_update = hash_test_progressive_update,
+       .hash_finish = hash_test_progressive_finish,
+       .hash_abort = hash_test_progressive_abort,
        .hash_digest_wd = hash_test_success,
 };
 
 static const struct hash_ops hash_test_hard_error_ops = {
+       .hash_init = hash_test_hard_error_init,
+       .hash_update = hash_test_progressive_update,
+       .hash_finish = hash_test_progressive_finish,
+       .hash_abort = hash_test_progressive_abort,
        .hash_digest_wd = hash_test_hard_error,
 };
 
+static const struct hash_ops hash_test_update_error_ops = {
+       .hash_init = hash_test_update_error_init,
+       .hash_update = hash_test_update_error,
+       .hash_finish = hash_test_progressive_finish,
+       .hash_abort = hash_test_progressive_abort,
+};
+
 U_BOOT_DRIVER(hash_test_unsupported_drv) = {
        .name = "hash_test_unsupported",
        .id = UCLASS_HASH,
@@ -76,6 +165,12 @@ U_BOOT_DRIVER(hash_test_hard_error_drv) = {
        .ops = &hash_test_hard_error_ops,
 };
 
+U_BOOT_DRIVER(hash_test_update_error_drv) = {
+       .name = "hash_test_update_error",
+       .id = UCLASS_HASH,
+       .ops = &hash_test_update_error_ops,
+};
+
 static int hash_test_unbind_all(void)
 {
        struct udevice *dev;
@@ -105,7 +200,13 @@ static int hash_test_bind(const struct driver *drv, const 
char *name)
 
 static int dm_test_hash_provider_selection(struct unit_test_state *uts)
 {
+       const struct image_region region = {
+               .data = "test",
+               .size = 4,
+       };
+       struct udevice *dev;
        u8 digest[32];
+       void *ctx;
        int ret;
 
        ut_assertok(hash_test_unbind_all());
@@ -124,6 +225,18 @@ static int dm_test_hash_provider_selection(struct 
unit_test_state *uts)
        for (int i = 0; i < sizeof(digest); i++)
                ut_asserteq(0x5a, digest[i]);
 
+       unsupported_init_calls = 0;
+       success_init_calls = 0;
+       ret = hash_init_lookup(HASH_ALGO_SHA256, &dev, &ctx);
+       ut_assertok(ret);
+       ut_asserteq(1, unsupported_init_calls);
+       ut_asserteq(1, success_init_calls);
+       ut_asserteq_str("hash-success", dev->name);
+       ut_asserteq_ptr(dev, ctx);
+       abort_calls = 0;
+       ut_assertok(hash_abort(dev, ctx));
+       ut_asserteq(1, abort_calls);
+
        ut_assertok(hash_test_unbind_all());
        ut_assertok(hash_test_bind(DM_DRIVER_GET(hash_test_hard_error_drv),
                                   "hash-hard-error"));
@@ -137,6 +250,25 @@ static int dm_test_hash_provider_selection(struct 
unit_test_state *uts)
        ut_asserteq(1, hard_error_calls);
        ut_asserteq(0, success_calls);
 
+       hard_error_init_calls = 0;
+       success_init_calls = 0;
+       ret = hash_init_lookup(HASH_ALGO_SHA256, &dev, &ctx);
+       ut_asserteq(-EINVAL, ret);
+       ut_asserteq(1, hard_error_init_calls);
+       ut_asserteq(0, success_init_calls);
+
+       ut_assertok(hash_test_unbind_all());
+       ut_assertok(hash_test_bind(DM_DRIVER_GET(hash_test_update_error_drv),
+                                  "hash-update-error"));
+       update_error_calls = 0;
+       finish_calls = 0;
+       abort_calls = 0;
+       ret = hash_calculate("sha256", &region, 1, digest);
+       ut_asserteq(-EIO, ret);
+       ut_asserteq(1, update_error_calls);
+       ut_asserteq(0, finish_calls);
+       ut_asserteq(1, abort_calls);
+
        return 0;
 }
 

-- 
2.53.0

Reply via email to