crypto: hash: use DM providers from hash command
The hash command currently always uses the software implementation for the selected algorithm, even when driver-model hash providers are available. Add a hash_digest_wd_lookup() helper which probes UCLASS_HASH devices in order and uses the first provider supporting the requested algorithm. Continue past unavailable providers and unsupported operations, but propagate a hard digest failure once a provider accepts the operation. Remember probe failures so they are not silently hidden by software fallback when no later provider succeeds. Use the helper from the hash command and retain its software fallback when no usable provider is present. Add sandbox tests covering provider fallback and hard-error propagation. Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
|
||||
#ifndef USE_HOSTCC
|
||||
#include <command.h>
|
||||
#include <dm.h>
|
||||
#include <env.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include <asm/global_data.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <u-boot/hash.h>
|
||||
#else
|
||||
#include "mkimage.h"
|
||||
#include <linux/compiler_attributes.h>
|
||||
@@ -614,7 +616,26 @@ int hash_command(const char *algo_name, int flags, struct cmd_tbl *cmdtp,
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
buf = map_sysmem(addr, len);
|
||||
if (CONFIG_IS_ENABLED(DM_HASH)) {
|
||||
enum HASH_ALGO hash_algo;
|
||||
int ret;
|
||||
|
||||
hash_algo = hash_algo_lookup_by_name(algo_name);
|
||||
if (hash_algo != HASH_ALGO_INVALID) {
|
||||
ret = hash_digest_wd_lookup(hash_algo, buf, len,
|
||||
output,
|
||||
algo->chunk_size);
|
||||
if (ret && ret != -ENODEV && ret != -EOPNOTSUPP) {
|
||||
unmap_sysmem(buf);
|
||||
free(output);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
if (!ret)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
algo->hash_func_ws(buf, len, output, algo->chunk_size);
|
||||
done:
|
||||
unmap_sysmem(buf);
|
||||
|
||||
/* Try to avoid code bloat when verify is not needed */
|
||||
|
||||
@@ -160,7 +160,7 @@ static int aspeed_hace_init(struct udevice *dev, enum HASH_ALGO algo, void **ctx
|
||||
free_n_out:
|
||||
free(hace_ctx);
|
||||
|
||||
return -EINVAL;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int aspeed_hace_update(struct udevice *dev, void *ctx, const void *ibuf, uint32_t ilen)
|
||||
|
||||
@@ -68,7 +68,7 @@ static int cptra_sha_init(struct udevice *dev, enum HASH_ALGO algo, void **ctxp)
|
||||
cs_ctx->dgst_len = 64;
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
rc = -EOPNOTSUPP;
|
||||
goto free_n_out;
|
||||
};
|
||||
|
||||
|
||||
@@ -84,6 +84,41 @@ int hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
|
||||
return ops->hash_digest_wd(dev, algo, ibuf, ilen, obuf, chunk_sz);
|
||||
}
|
||||
|
||||
static bool hash_op_unsupported(int ret)
|
||||
{
|
||||
return ret == -ENOSYS || ret == -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int hash_digest_wd_lookup(enum HASH_ALGO algo, const void *ibuf,
|
||||
const u32 ilen, void *obuf, u32 chunk_sz)
|
||||
{
|
||||
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_digest_wd(dev, algo, ibuf, ilen, obuf, chunk_sz);
|
||||
if (!ret)
|
||||
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);
|
||||
|
||||
@@ -19,6 +19,8 @@ enum HASH_ALGO {
|
||||
HASH_ALGO_INVALID = 0xffffffff,
|
||||
};
|
||||
|
||||
struct udevice;
|
||||
|
||||
/* general APIs for hash algo information */
|
||||
enum HASH_ALGO hash_algo_lookup_by_name(const char *name);
|
||||
ssize_t hash_algo_digest_size(enum HASH_ALGO algo);
|
||||
@@ -31,6 +33,24 @@ int hash_digest(struct udevice *dev, enum HASH_ALGO algo,
|
||||
int hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
|
||||
const void *ibuf, const uint32_t ilen,
|
||||
void *obuf, uint32_t chunk_sz);
|
||||
/**
|
||||
* hash_digest_wd_lookup() - Hash with the first provider supporting an algorithm
|
||||
*
|
||||
* Probe each hash device in order and use the first one which supports the
|
||||
* requested algorithm. Probe failures are remembered while later providers are
|
||||
* tried. Once a provider accepts an operation, hard failures are returned
|
||||
* without trying another provider.
|
||||
*
|
||||
* @algo: Hash algorithm
|
||||
* @ibuf: Input buffer
|
||||
* @ilen: Input buffer length
|
||||
* @obuf: Output buffer
|
||||
* @chunk_sz: Watchdog scheduling interval
|
||||
* 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_digest_wd_lookup(enum HASH_ALGO algo, const void *ibuf,
|
||||
const u32 ilen, void *obuf, u32 chunk_sz);
|
||||
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);
|
||||
|
||||
@@ -46,6 +46,7 @@ obj-$(CONFIG_DMA) += dma.o
|
||||
obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi_host.o
|
||||
obj-$(CONFIG_DM_DSA) += dsa.o
|
||||
obj-$(CONFIG_ECDSA_VERIFY) += ecdsa.o
|
||||
obj-$(CONFIG_DM_HASH) += hash.o
|
||||
obj-$(CONFIG_EFI_MEDIA_SANDBOX) += efi_media.o
|
||||
obj-$(CONFIG_DM_ETH) += eth.o
|
||||
obj-$(CONFIG_EXTCON) += extcon.o
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Tests for driver-model hash-provider selection
|
||||
*
|
||||
* Copyright (C) 2026 James Hilliard
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/test.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <u-boot/hash.h>
|
||||
#include <test/test.h>
|
||||
#include <test/ut.h>
|
||||
|
||||
static int unsupported_calls;
|
||||
static int success_calls;
|
||||
static int hard_error_calls;
|
||||
|
||||
static int hash_test_unsupported(struct udevice *dev, enum HASH_ALGO algo,
|
||||
const void *ibuf, const uint32_t ilen,
|
||||
void *obuf, uint32_t chunk_sz)
|
||||
{
|
||||
unsupported_calls++;
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int hash_test_success(struct udevice *dev, enum HASH_ALGO algo,
|
||||
const void *ibuf, const uint32_t ilen,
|
||||
void *obuf, uint32_t chunk_sz)
|
||||
{
|
||||
success_calls++;
|
||||
memset(obuf, 0x5a, hash_algo_digest_size(algo));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hash_test_hard_error(struct udevice *dev, enum HASH_ALGO algo,
|
||||
const void *ibuf, const uint32_t ilen,
|
||||
void *obuf, uint32_t chunk_sz)
|
||||
{
|
||||
hard_error_calls++;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct hash_ops hash_test_unsupported_ops = {
|
||||
.hash_digest_wd = hash_test_unsupported,
|
||||
};
|
||||
|
||||
static const struct hash_ops hash_test_success_ops = {
|
||||
.hash_digest_wd = hash_test_success,
|
||||
};
|
||||
|
||||
static const struct hash_ops hash_test_hard_error_ops = {
|
||||
.hash_digest_wd = hash_test_hard_error,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(hash_test_unsupported_drv) = {
|
||||
.name = "hash_test_unsupported",
|
||||
.id = UCLASS_HASH,
|
||||
.ops = &hash_test_unsupported_ops,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(hash_test_success_drv) = {
|
||||
.name = "hash_test_success",
|
||||
.id = UCLASS_HASH,
|
||||
.ops = &hash_test_success_ops,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(hash_test_hard_error_drv) = {
|
||||
.name = "hash_test_hard_error",
|
||||
.id = UCLASS_HASH,
|
||||
.ops = &hash_test_hard_error_ops,
|
||||
};
|
||||
|
||||
static int hash_test_unbind_all(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
for (;;) {
|
||||
ret = uclass_find_first_device(UCLASS_HASH, &dev);
|
||||
if (ret || !dev)
|
||||
return ret;
|
||||
if (device_active(dev)) {
|
||||
ret = device_remove(dev, DM_REMOVE_NORMAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = device_unbind(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static int hash_test_bind(const struct driver *drv, const char *name)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
return device_bind(dm_root(), drv, name, 0, ofnode_null(), &dev);
|
||||
}
|
||||
|
||||
static int dm_test_hash_provider_selection(struct unit_test_state *uts)
|
||||
{
|
||||
u8 digest[32];
|
||||
int ret;
|
||||
|
||||
ut_assertok(hash_test_unbind_all());
|
||||
ut_assertok(hash_test_bind(DM_DRIVER_GET(hash_test_unsupported_drv),
|
||||
"hash-unsupported"));
|
||||
ut_assertok(hash_test_bind(DM_DRIVER_GET(hash_test_success_drv),
|
||||
"hash-success"));
|
||||
|
||||
unsupported_calls = 0;
|
||||
success_calls = 0;
|
||||
memset(digest, 0, sizeof(digest));
|
||||
ret = hash_digest_wd_lookup(HASH_ALGO_SHA256, "test", 4, digest, 4);
|
||||
ut_assertok(ret);
|
||||
ut_asserteq(1, unsupported_calls);
|
||||
ut_asserteq(1, success_calls);
|
||||
for (int i = 0; i < sizeof(digest); i++)
|
||||
ut_asserteq(0x5a, digest[i]);
|
||||
|
||||
ut_assertok(hash_test_unbind_all());
|
||||
ut_assertok(hash_test_bind(DM_DRIVER_GET(hash_test_hard_error_drv),
|
||||
"hash-hard-error"));
|
||||
ut_assertok(hash_test_bind(DM_DRIVER_GET(hash_test_success_drv),
|
||||
"hash-success"));
|
||||
|
||||
hard_error_calls = 0;
|
||||
success_calls = 0;
|
||||
ret = hash_digest_wd_lookup(HASH_ALGO_SHA256, "test", 4, digest, 4);
|
||||
ut_asserteq(-EINVAL, ret);
|
||||
ut_asserteq(1, hard_error_calls);
|
||||
ut_asserteq(0, success_calls);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_hash_provider_selection, UTF_SCAN_FDT);
|
||||
Reference in New Issue
Block a user