[PATCH v8 06/15] crypto: hash: support hardware-only progressive hashing

James Hilliard <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <20260814-submit-ce-series-v2-v8-6-fe9212431a94__4951.469977253$1786798916$gmane$org@gmail.com>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.