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
