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.

Extend the hash provider-selection test to cover progressive
initialization.

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

Signed-off-by: James Hilliard <[email protected]>
---
Changes v6 -> v7:
  - New patch
---
 boot/image-fit.c                  |  2 +-
 common/hash.c                     | 21 +++++++++++++++--
 drivers/crypto/hash/hash-uclass.c | 31 +++++++++++++++++++++++++
 include/u-boot/hash.h             | 14 +++++++++++
 lib/Makefile                      |  4 ++--
 lib/hash-checksum.c               | 28 +++++++++++++++++++++-
 lib/mbedtls/Kconfig               |  2 +-
 test/dm/hash.c                    | 49 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 144 insertions(+), 7 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/drivers/crypto/hash/hash-uclass.c 
b/drivers/crypto/hash/hash-uclass.c
index ffca19af2de..81828e45f69 100644
--- a/drivers/crypto/hash/hash-uclass.c
+++ b/drivers/crypto/hash/hash-uclass.c
@@ -119,6 +119,37 @@ 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);
diff --git a/include/u-boot/hash.h b/include/u-boot/hash.h
index 7dba66047f5..065c6f96830 100644
--- a/include/u-boot/hash.h
+++ b/include/u-boot/hash.h
@@ -51,6 +51,20 @@ 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);
 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);
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..afd4dbfc343 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,37 @@ 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)
+                                       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..fe0a617ef3a 100644
--- a/test/dm/hash.c
+++ b/test/dm/hash.c
@@ -17,6 +17,34 @@
 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 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_unsupported(struct udevice *dev, enum HASH_ALGO algo,
                                 const void *ibuf, const uint32_t ilen,
@@ -47,14 +75,17 @@ 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_digest_wd = hash_test_unsupported,
 };
 
 static const struct hash_ops hash_test_success_ops = {
+       .hash_init = hash_test_success_init,
        .hash_digest_wd = hash_test_success,
 };
 
 static const struct hash_ops hash_test_hard_error_ops = {
+       .hash_init = hash_test_hard_error_init,
        .hash_digest_wd = hash_test_hard_error,
 };
 
@@ -105,7 +136,9 @@ static int hash_test_bind(const struct driver *drv, const 
char *name)
 
 static int dm_test_hash_provider_selection(struct unit_test_state *uts)
 {
+       struct udevice *dev;
        u8 digest[32];
+       void *ctx;
        int ret;
 
        ut_assertok(hash_test_unbind_all());
@@ -124,6 +157,15 @@ 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);
+
        ut_assertok(hash_test_unbind_all());
        ut_assertok(hash_test_bind(DM_DRIVER_GET(hash_test_hard_error_drv),
                                   "hash-hard-error"));
@@ -137,6 +179,13 @@ 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);
+
        return 0;
 }
 

-- 
2.53.0

Reply via email to