* [PATCH v5 01/14] cmd: aes: fix DM operation handling
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 4:13 ` [PATCH v5 02/14] crypto: hash: use DM providers from hash command James Hilliard
` (12 subsequent siblings)
13 siblings, 0 replies; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
The DM AES command path takes operation-specific arguments after the
common arguments. argv[1] holds the mode, "ecb" or "cbc", while argv[2]
holds the requested operation, "enc" or "dec".
The DM path checked argv[1] for the operation, so the ecb and cbc
commands always returned CMD_RET_USAGE before running. Parse argv[2]
instead.
Fixes: b01444aa14cf ("cmd: aes: Add support for DM AES drivers")
Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v1 -> v2:
- Limit the patch to the argv parsing fix (suggested by Simon Glass)
- State that the commands always returned usage
(suggested by Simon Glass)
- Add a Fixes tag (suggested by Simon Glass)
---
cmd/aes.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/cmd/aes.c b/cmd/aes.c
index 3fd83013ffe..edcd87cf11b 100644
--- a/cmd/aes.c
+++ b/cmd/aes.c
@@ -175,9 +175,9 @@ int cmd_aes_ecb(int argc, char *const argv[], u32 key_len)
if (ret)
return ret;
- if (!strncmp(argv[1], "enc", 3))
+ if (!strncmp(argv[2], "enc", 3))
enc = 1;
- else if (!strncmp(argv[1], "dec", 3))
+ else if (!strncmp(argv[2], "dec", 3))
enc = 0;
else
return CMD_RET_USAGE;
@@ -223,9 +223,9 @@ int cmd_aes_cbc(int argc, char *const argv[], u32 key_len)
if (ret)
return ret;
- if (!strncmp(argv[1], "enc", 3))
+ if (!strncmp(argv[2], "enc", 3))
enc = 1;
- else if (!strncmp(argv[1], "dec", 3))
+ else if (!strncmp(argv[2], "dec", 3))
enc = 0;
else
return CMD_RET_USAGE;
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v5 02/14] crypto: hash: use DM providers from hash command
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
2026-07-20 4:13 ` [PATCH v5 01/14] cmd: aes: fix DM operation handling James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 23:24 ` Tom Rini via U-Boot
2026-07-20 4:13 ` [PATCH v5 03/14] crypto: aes: allow DM AES in SPL James Hilliard
` (11 subsequent siblings)
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
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>
---
Changes v3 -> v4:
- New patch
- Try all registered hash providers instead of only device zero
- Add provider-selection and error-propagation tests
- Reserve -EINVAL for hard errors
- Use -EOPNOTSUPP for unsupported algorithms
---
common/hash.c | 21 ++++++
drivers/crypto/aspeed/aspeed_hace.c | 2 +-
drivers/crypto/aspeed/cptra_sha.c | 2 +-
drivers/crypto/hash/hash-uclass.c | 39 +++++++++-
include/u-boot/hash.h | 24 +++++-
test/dm/Makefile | 1 +
test/dm/hash.c | 143 ++++++++++++++++++++++++++++++++++++
7 files changed, 226 insertions(+), 6 deletions(-)
diff --git a/common/hash.c b/common/hash.c
index 71c4bef5826..5cbb4926c1d 100644
--- a/common/hash.c
+++ b/common/hash.c
@@ -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 */
diff --git a/drivers/crypto/aspeed/aspeed_hace.c b/drivers/crypto/aspeed/aspeed_hace.c
index 22b5008a296..2469f53472f 100644
--- a/drivers/crypto/aspeed/aspeed_hace.c
+++ b/drivers/crypto/aspeed/aspeed_hace.c
@@ -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)
diff --git a/drivers/crypto/aspeed/cptra_sha.c b/drivers/crypto/aspeed/cptra_sha.c
index f57778e160d..0dc00f306f1 100644
--- a/drivers/crypto/aspeed/cptra_sha.c
+++ b/drivers/crypto/aspeed/cptra_sha.c
@@ -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;
};
diff --git a/drivers/crypto/hash/hash-uclass.c b/drivers/crypto/hash/hash-uclass.c
index 5d9f1e0d59b..30929412856 100644
--- a/drivers/crypto/hash/hash-uclass.c
+++ b/drivers/crypto/hash/hash-uclass.c
@@ -73,8 +73,8 @@ 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)
+ const void *ibuf, const uint32_t ilen,
+ void *obuf, uint32_t chunk_sz)
{
struct hash_ops *ops = (struct hash_ops *)device_get_ops(dev);
@@ -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);
diff --git a/include/u-boot/hash.h b/include/u-boot/hash.h
index f9d47a99a77..a6ba08a8591 100644
--- a/include/u-boot/hash.h
+++ b/include/u-boot/hash.h
@@ -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);
@@ -29,8 +31,26 @@ int hash_digest(struct udevice *dev, enum HASH_ALGO algo,
const void *ibuf, const uint32_t ilen,
void *obuf);
int hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
- const void *ibuf, const uint32_t ilen,
- void *obuf, uint32_t chunk_sz);
+ 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);
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 76aa1fff9ba..fb3e6a7008f 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -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
diff --git a/test/dm/hash.c b/test/dm/hash.c
new file mode 100644
index 00000000000..fe949e33de5
--- /dev/null
+++ b/test/dm/hash.c
@@ -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);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 02/14] crypto: hash: use DM providers from hash command
2026-07-20 4:13 ` [PATCH v5 02/14] crypto: hash: use DM providers from hash command James Hilliard
@ 2026-07-20 23:24 ` Tom Rini via U-Boot
2026-07-22 14:20 ` James Hilliard
2026-07-28 19:41 ` James Hilliard
0 siblings, 2 replies; 29+ messages in thread
From: Tom Rini via U-Boot @ 2026-07-20 23:24 UTC (permalink / raw)
To: James Hilliard
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
[-- Attachment #1: Type: text/plain, Size: 2722 bytes --]
On Sun, Jul 19, 2026 at 10:13:46PM -0600, James Hilliard wrote:
> 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>
> ---
> Changes v3 -> v4:
> - New patch
> - Try all registered hash providers instead of only device zero
> - Add provider-selection and error-propagation tests
> - Reserve -EINVAL for hard errors
> - Use -EOPNOTSUPP for unsupported algorithms
Putting new features in an unrelated patch series makes things harder to
merge. I don't know that Andre will be comfortable taking some generic
changes + sunxi support, but he might be. But it's even easier when
something like this is standalone and can be reviewed and picked up on
its own (I'm going to have some size questions about this, once I review
it globally..).
[snip]
> diff --git a/drivers/crypto/hash/hash-uclass.c b/drivers/crypto/hash/hash-uclass.c
> index 5d9f1e0d59b..30929412856 100644
> --- a/drivers/crypto/hash/hash-uclass.c
> +++ b/drivers/crypto/hash/hash-uclass.c
> @@ -73,8 +73,8 @@ 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)
> + const void *ibuf, const uint32_t ilen,
> + void *obuf, uint32_t chunk_sz)
> {
> struct hash_ops *ops = (struct hash_ops *)device_get_ops(dev);
>
This isn't correct spacing before, or after? So as part of sending this
standlone this part should just be dropped I believe.
[snip]
> @@ -29,8 +31,26 @@ int hash_digest(struct udevice *dev, enum HASH_ALGO algo,
> const void *ibuf, const uint32_t ilen,
> void *obuf);
> int hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
> - const void *ibuf, const uint32_t ilen,
> - void *obuf, uint32_t chunk_sz);
> + const void *ibuf, const uint32_t ilen,
> + void *obuf, uint32_t chunk_sz);
Same here.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v5 02/14] crypto: hash: use DM providers from hash command
2026-07-20 23:24 ` Tom Rini via U-Boot
@ 2026-07-22 14:20 ` James Hilliard
2026-07-28 19:41 ` James Hilliard
1 sibling, 0 replies; 29+ messages in thread
From: James Hilliard @ 2026-07-22 14:20 UTC (permalink / raw)
To: Tom Rini
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
On Mon, Jul 20, 2026 at 7:24 PM Tom Rini <trini@konsulko.com> wrote:
>
> On Sun, Jul 19, 2026 at 10:13:46PM -0600, James Hilliard wrote:
> > 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>
> > ---
> > Changes v3 -> v4:
> > - New patch
> > - Try all registered hash providers instead of only device zero
> > - Add provider-selection and error-propagation tests
> > - Reserve -EINVAL for hard errors
> > - Use -EOPNOTSUPP for unsupported algorithms
>
> Putting new features in an unrelated patch series makes things harder to
> merge. I don't know that Andre will be comfortable taking some generic
> changes + sunxi support, but he might be. But it's even easier when
> something like this is standalone and can be reviewed and picked up on
> its own (I'm going to have some size questions about this, once I review
> it globally..).
I went ahead and sent this separately here:
https://patchwork.ozlabs.org/project/uboot/patch/20260721031123.1949018-1-james.hilliard1@gmail.com/
>
> [snip]
> > diff --git a/drivers/crypto/hash/hash-uclass.c b/drivers/crypto/hash/hash-uclass.c
> > index 5d9f1e0d59b..30929412856 100644
> > --- a/drivers/crypto/hash/hash-uclass.c
> > +++ b/drivers/crypto/hash/hash-uclass.c
> > @@ -73,8 +73,8 @@ 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)
> > + const void *ibuf, const uint32_t ilen,
> > + void *obuf, uint32_t chunk_sz)
> > {
> > struct hash_ops *ops = (struct hash_ops *)device_get_ops(dev);
> >
>
> This isn't correct spacing before, or after? So as part of sending this
> standlone this part should just be dropped I believe.
Dropped this change in v6 standalone patch.
>
> [snip]
> > @@ -29,8 +31,26 @@ int hash_digest(struct udevice *dev, enum HASH_ALGO algo,
> > const void *ibuf, const uint32_t ilen,
> > void *obuf);
> > int hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
> > - const void *ibuf, const uint32_t ilen,
> > - void *obuf, uint32_t chunk_sz);
> > + const void *ibuf, const uint32_t ilen,
> > + void *obuf, uint32_t chunk_sz);
>
> Same here.
Dropped in v6 as well
> --
> Tom
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v5 02/14] crypto: hash: use DM providers from hash command
2026-07-20 23:24 ` Tom Rini via U-Boot
2026-07-22 14:20 ` James Hilliard
@ 2026-07-28 19:41 ` James Hilliard
2026-07-28 23:54 ` Tom Rini
1 sibling, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-28 19:41 UTC (permalink / raw)
To: Tom Rini
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
On Mon, Jul 20, 2026 at 7:24 PM Tom Rini <trini@konsulko.com> wrote:
>
> On Sun, Jul 19, 2026 at 10:13:46PM -0600, James Hilliard wrote:
> > 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>
> > ---
> > Changes v3 -> v4:
> > - New patch
> > - Try all registered hash providers instead of only device zero
> > - Add provider-selection and error-propagation tests
> > - Reserve -EINVAL for hard errors
> > - Use -EOPNOTSUPP for unsupported algorithms
>
> Putting new features in an unrelated patch series makes things harder to
> merge. I don't know that Andre will be comfortable taking some generic
> changes + sunxi support, but he might be. But it's even easier when
> something like this is standalone and can be reviewed and picked up on
> its own (I'm going to have some size questions about this, once I review
> it globally..).
Maybe it makes sense to merge all my patches prior to the sunxi specific
ones first? That way I can rebase my series and shrink the patchset
substantially for the remaining sunxi ce driver patches for Andre.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v5 02/14] crypto: hash: use DM providers from hash command
2026-07-28 19:41 ` James Hilliard
@ 2026-07-28 23:54 ` Tom Rini
0 siblings, 0 replies; 29+ messages in thread
From: Tom Rini @ 2026-07-28 23:54 UTC (permalink / raw)
To: James Hilliard
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
On Tue, Jul 28, 2026 at 03:41:50PM -0400, James Hilliard wrote:
> On Mon, Jul 20, 2026 at 7:24 PM Tom Rini <trini@konsulko.com> wrote:
> >
> > On Sun, Jul 19, 2026 at 10:13:46PM -0600, James Hilliard wrote:
> > > 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>
> > > ---
> > > Changes v3 -> v4:
> > > - New patch
> > > - Try all registered hash providers instead of only device zero
> > > - Add provider-selection and error-propagation tests
> > > - Reserve -EINVAL for hard errors
> > > - Use -EOPNOTSUPP for unsupported algorithms
> >
> > Putting new features in an unrelated patch series makes things harder to
> > merge. I don't know that Andre will be comfortable taking some generic
> > changes + sunxi support, but he might be. But it's even easier when
> > something like this is standalone and can be reviewed and picked up on
> > its own (I'm going to have some size questions about this, once I review
> > it globally..).
>
> Maybe it makes sense to merge all my patches prior to the sunxi specific
> ones first? That way I can rebase my series and shrink the patchset
> substantially for the remaining sunxi ce driver patches for Andre.
Yes. And it's fine to note in the cover letter than series B depends on
series A, so that it can be reviewed, and then picked up in time more
easily.
--
Tom
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 03/14] crypto: aes: allow DM AES in SPL
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
2026-07-20 4:13 ` [PATCH v5 01/14] cmd: aes: fix DM operation handling James Hilliard
2026-07-20 4:13 ` [PATCH v5 02/14] crypto: hash: use DM providers from hash command James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 23:26 ` Tom Rini via U-Boot
2026-07-20 4:13 ` [PATCH v5 04/14] crypto: hash: allow DM hash " James Hilliard
` (10 subsequent siblings)
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
The AES uclass Makefile is already keyed by CONFIG_$(PHASE_)DM_AES,
but there is no SPL_DM_AES symbol to build it for SPL.
Add the SPL variant so SPL code can use UCLASS_AES providers, for
example when decrypting FIT images before loading U-Boot proper. Select
SPL_CRYPTO from SPL_DM_AES as well, since drivers/Makefile only descends
into drivers/crypto/ for SPL when SPL_CRYPTO is enabled.
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v1 -> v2:
- Select SPL_CRYPTO from SPL_DM_AES (suggested by Simon Glass)
---
drivers/crypto/aes/Kconfig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/crypto/aes/Kconfig b/drivers/crypto/aes/Kconfig
index 7e1b1b2875d..254cb598568 100644
--- a/drivers/crypto/aes/Kconfig
+++ b/drivers/crypto/aes/Kconfig
@@ -4,6 +4,14 @@ config DM_AES
help
If you want to use driver model for AES crypto operations, say Y.
+config SPL_DM_AES
+ bool "Enable Driver Model for AES crypto operations in SPL"
+ depends on SPL_DM
+ select SPL_CRYPTO
+ help
+ If you want to use driver model for AES crypto operations in SPL,
+ say Y.
+
config AES_SOFTWARE
bool "Enable driver for AES in software"
depends on DM_AES && AES
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 03/14] crypto: aes: allow DM AES in SPL
2026-07-20 4:13 ` [PATCH v5 03/14] crypto: aes: allow DM AES in SPL James Hilliard
@ 2026-07-20 23:26 ` Tom Rini via U-Boot
0 siblings, 0 replies; 29+ messages in thread
From: Tom Rini via U-Boot @ 2026-07-20 23:26 UTC (permalink / raw)
To: James Hilliard
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
[-- Attachment #1: Type: text/plain, Size: 1528 bytes --]
On Sun, Jul 19, 2026 at 10:13:47PM -0600, James Hilliard wrote:
> The AES uclass Makefile is already keyed by CONFIG_$(PHASE_)DM_AES,
> but there is no SPL_DM_AES symbol to build it for SPL.
>
> Add the SPL variant so SPL code can use UCLASS_AES providers, for
> example when decrypting FIT images before loading U-Boot proper. Select
> SPL_CRYPTO from SPL_DM_AES as well, since drivers/Makefile only descends
> into drivers/crypto/ for SPL when SPL_CRYPTO is enabled.
>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
> ---
> Changes v1 -> v2:
> - Select SPL_CRYPTO from SPL_DM_AES (suggested by Simon Glass)
> ---
> drivers/crypto/aes/Kconfig | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/crypto/aes/Kconfig b/drivers/crypto/aes/Kconfig
> index 7e1b1b2875d..254cb598568 100644
> --- a/drivers/crypto/aes/Kconfig
> +++ b/drivers/crypto/aes/Kconfig
> @@ -4,6 +4,14 @@ config DM_AES
> help
> If you want to use driver model for AES crypto operations, say Y.
>
> +config SPL_DM_AES
> + bool "Enable Driver Model for AES crypto operations in SPL"
> + depends on SPL_DM
> + select SPL_CRYPTO
> + help
> + If you want to use driver model for AES crypto operations in SPL,
> + say Y.
> +
> config AES_SOFTWARE
> bool "Enable driver for AES in software"
> depends on DM_AES && AES
Since this gets used later on, this is fine.
Reviewed-by: Tom Rini <trini@konsulko.com>
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 04/14] crypto: hash: allow DM hash in SPL
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (2 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 03/14] crypto: aes: allow DM AES in SPL James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 23:30 ` Tom Rini via U-Boot
2026-07-20 4:13 ` [PATCH v5 05/14] boot: image: try all DM hash providers James Hilliard
` (9 subsequent siblings)
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
The hash uclass is currently keyed only by CONFIG_DM_HASH, so SPL cannot
enable UCLASS_HASH independently. Any SPL code using hash_digest*() has to
rely on U-Boot proper also enabling DM_HASH, and the FIT hash path selects
the driver-model implementation with a non-phase-aware preprocessor check.
Add SPL_DM_HASH, build the hash uclass from CONFIG_$(PHASE_)DM_HASH and use
CONFIG_IS_ENABLED(DM_HASH) when selecting the FIT hash implementation. This
lets SPL FIT verification use a UCLASS_HASH provider without requiring the
U-Boot proper hash uclass.
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v2 -> v3:
- Remove the bare software-hash fallback scope
(suggested by Simon Glass)
- Document that SPL_DM_HASH needs a hardware provider unless a
phase-aware software hash provider is added
(suggested by Simon Glass)
---
boot/image-fit.c | 50 +++++++++++++++++++++++---------------------
drivers/crypto/hash/Kconfig | 13 ++++++++++++
drivers/crypto/hash/Makefile | 2 +-
3 files changed, 40 insertions(+), 25 deletions(-)
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 044a40e1910..555cbf81348 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -32,10 +32,8 @@ extern void *aligned_alloc(size_t alignment, size_t size);
#include <malloc.h>
#include <memalign.h>
#include <asm/global_data.h>
-#ifdef CONFIG_DM_HASH
#include <dm.h>
#include <u-boot/hash.h>
-#endif
#define aligned_alloc(a, s) memalign((a), (s))
DECLARE_GLOBAL_DATA_PTR;
@@ -1318,43 +1316,47 @@ int fit_set_timestamp(void *fit, int noffset, time_t timestamp)
int calculate_hash(const void *data, int data_len, const char *name,
uint8_t *value, int *value_len)
{
-#if !defined(USE_HOSTCC) && defined(CONFIG_DM_HASH)
+ struct hash_algo *algo;
+ int ret;
+
+#ifndef USE_HOSTCC
int rc;
enum HASH_ALGO hash_algo;
struct udevice *dev;
- rc = uclass_get_device(UCLASS_HASH, 0, &dev);
- if (rc) {
- debug("failed to get hash device, rc=%d\n", rc);
- return -1;
- }
+ if (CONFIG_IS_ENABLED(DM_HASH)) {
+ rc = uclass_get_device(UCLASS_HASH, 0, &dev);
+ if (rc) {
+ debug("failed to get hash device, rc=%d\n", rc);
+ return -1;
+ }
- hash_algo = hash_algo_lookup_by_name(name);
- if (hash_algo == HASH_ALGO_INVALID) {
- debug("Unsupported hash algorithm\n");
- return -1;
- };
+ hash_algo = hash_algo_lookup_by_name(name);
+ if (hash_algo == HASH_ALGO_INVALID) {
+ debug("Unsupported hash algorithm\n");
+ return -1;
+ }
- rc = hash_digest_wd(dev, hash_algo, data, data_len, value, CHUNKSZ);
- if (rc) {
- debug("failed to get hash value, rc=%d\n", rc);
- return -1;
- }
+ rc = hash_digest_wd(dev, hash_algo, data, data_len, value,
+ CHUNKSZ);
+ if (rc) {
+ debug("failed to get hash value, rc=%d\n", rc);
+ return -1;
+ }
- *value_len = hash_algo_digest_size(hash_algo);
-#else
- struct hash_algo *algo;
- int ret;
+ *value_len = hash_algo_digest_size(hash_algo);
+ return 0;
+ }
+#endif
ret = hash_lookup_algo(name, &algo);
if (ret < 0) {
- debug("Unsupported hash alogrithm\n");
+ debug("Unsupported hash algorithm\n");
return -1;
}
algo->hash_func_ws(data, data_len, value, algo->chunk_size);
*value_len = algo->digest_size;
-#endif
return 0;
}
diff --git a/drivers/crypto/hash/Kconfig b/drivers/crypto/hash/Kconfig
index 72b955ac791..272af7bce18 100644
--- a/drivers/crypto/hash/Kconfig
+++ b/drivers/crypto/hash/Kconfig
@@ -4,6 +4,19 @@ config DM_HASH
help
If you want to use driver model for Hash, say Y.
+config SPL_DM_HASH
+ bool "Enable Driver Model for Hash in SPL"
+ depends on SPL_DM
+ select SPL_CRYPTO
+ help
+ Enable the hash uclass in SPL so SPL code can bind and use
+ UCLASS_HASH providers through the driver model. This is useful for
+ FIT verification paths that want to calculate image hashes through a
+ hardware hash accelerator before U-Boot proper is loaded.
+ HASH_SOFTWARE depends on DM_HASH, so SPL_DM_HASH alone does not
+ provide a software hash device. Enable a hardware hash provider for
+ SPL when selecting this option.
+
config HASH_SOFTWARE
bool "Enable driver for Hash in software"
depends on DM_HASH
diff --git a/drivers/crypto/hash/Makefile b/drivers/crypto/hash/Makefile
index 33d88161ed4..9f0d30f9be3 100644
--- a/drivers/crypto/hash/Makefile
+++ b/drivers/crypto/hash/Makefile
@@ -2,5 +2,5 @@
#
# Copyright (c) 2021 ASPEED Technology Inc.
-obj-$(CONFIG_DM_HASH) += hash-uclass.o
+obj-$(CONFIG_$(PHASE_)DM_HASH) += hash-uclass.o
obj-$(CONFIG_HASH_SOFTWARE) += hash_sw.o
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 04/14] crypto: hash: allow DM hash in SPL
2026-07-20 4:13 ` [PATCH v5 04/14] crypto: hash: allow DM hash " James Hilliard
@ 2026-07-20 23:30 ` Tom Rini via U-Boot
2026-07-22 14:21 ` James Hilliard
0 siblings, 1 reply; 29+ messages in thread
From: Tom Rini via U-Boot @ 2026-07-20 23:30 UTC (permalink / raw)
To: James Hilliard
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
[-- Attachment #1: Type: text/plain, Size: 1495 bytes --]
On Sun, Jul 19, 2026 at 10:13:48PM -0600, James Hilliard wrote:
> The hash uclass is currently keyed only by CONFIG_DM_HASH, so SPL cannot
> enable UCLASS_HASH independently. Any SPL code using hash_digest*() has to
> rely on U-Boot proper also enabling DM_HASH, and the FIT hash path selects
> the driver-model implementation with a non-phase-aware preprocessor check.
>
> Add SPL_DM_HASH, build the hash uclass from CONFIG_$(PHASE_)DM_HASH and use
> CONFIG_IS_ENABLED(DM_HASH) when selecting the FIT hash implementation. This
> lets SPL FIT verification use a UCLASS_HASH provider without requiring the
> U-Boot proper hash uclass.
>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
> ---
> Changes v2 -> v3:
> - Remove the bare software-hash fallback scope
> (suggested by Simon Glass)
> - Document that SPL_DM_HASH needs a hardware provider unless a
> phase-aware software hash provider is added
> (suggested by Simon Glass)
> ---
> boot/image-fit.c | 50 +++++++++++++++++++++++---------------------
> drivers/crypto/hash/Kconfig | 13 ++++++++++++
> drivers/crypto/hash/Makefile | 2 +-
> 3 files changed, 40 insertions(+), 25 deletions(-)
The changes in here make me worry a bit about platforms relying on the
current behavior (because of the Makefile change), did you put this
through CI? https://docs.u-boot-project.org/en/latest/develop/ci_testing.html
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v5 04/14] crypto: hash: allow DM hash in SPL
2026-07-20 23:30 ` Tom Rini via U-Boot
@ 2026-07-22 14:21 ` James Hilliard
2026-07-22 16:25 ` Tom Rini
0 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-22 14:21 UTC (permalink / raw)
To: Tom Rini
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
On Mon, Jul 20, 2026 at 7:30 PM Tom Rini <trini@konsulko.com> wrote:
>
> On Sun, Jul 19, 2026 at 10:13:48PM -0600, James Hilliard wrote:
> > The hash uclass is currently keyed only by CONFIG_DM_HASH, so SPL cannot
> > enable UCLASS_HASH independently. Any SPL code using hash_digest*() has to
> > rely on U-Boot proper also enabling DM_HASH, and the FIT hash path selects
> > the driver-model implementation with a non-phase-aware preprocessor check.
> >
> > Add SPL_DM_HASH, build the hash uclass from CONFIG_$(PHASE_)DM_HASH and use
> > CONFIG_IS_ENABLED(DM_HASH) when selecting the FIT hash implementation. This
> > lets SPL FIT verification use a UCLASS_HASH provider without requiring the
> > U-Boot proper hash uclass.
> >
> > Reviewed-by: Simon Glass <sjg@chromium.org>
> > Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
> > ---
> > Changes v2 -> v3:
> > - Remove the bare software-hash fallback scope
> > (suggested by Simon Glass)
> > - Document that SPL_DM_HASH needs a hardware provider unless a
> > phase-aware software hash provider is added
> > (suggested by Simon Glass)
> > ---
> > boot/image-fit.c | 50 +++++++++++++++++++++++---------------------
> > drivers/crypto/hash/Kconfig | 13 ++++++++++++
> > drivers/crypto/hash/Makefile | 2 +-
> > 3 files changed, 40 insertions(+), 25 deletions(-)
>
> The changes in here make me worry a bit about platforms relying on the
> current behavior (because of the Makefile change), did you put this
> through CI? https://docs.u-boot-project.org/en/latest/develop/ci_testing.html
Looks fine to me when I ran it through CI:
https://github.com/u-boot/u-boot/pull/1014/checks
>
> --
> Tom
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v5 04/14] crypto: hash: allow DM hash in SPL
2026-07-22 14:21 ` James Hilliard
@ 2026-07-22 16:25 ` Tom Rini
0 siblings, 0 replies; 29+ messages in thread
From: Tom Rini @ 2026-07-22 16:25 UTC (permalink / raw)
To: James Hilliard
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
[-- Attachment #1: Type: text/plain, Size: 1981 bytes --]
On Wed, Jul 22, 2026 at 08:21:17AM -0600, James Hilliard wrote:
> On Mon, Jul 20, 2026 at 7:30 PM Tom Rini <trini@konsulko.com> wrote:
> >
> > On Sun, Jul 19, 2026 at 10:13:48PM -0600, James Hilliard wrote:
> > > The hash uclass is currently keyed only by CONFIG_DM_HASH, so SPL cannot
> > > enable UCLASS_HASH independently. Any SPL code using hash_digest*() has to
> > > rely on U-Boot proper also enabling DM_HASH, and the FIT hash path selects
> > > the driver-model implementation with a non-phase-aware preprocessor check.
> > >
> > > Add SPL_DM_HASH, build the hash uclass from CONFIG_$(PHASE_)DM_HASH and use
> > > CONFIG_IS_ENABLED(DM_HASH) when selecting the FIT hash implementation. This
> > > lets SPL FIT verification use a UCLASS_HASH provider without requiring the
> > > U-Boot proper hash uclass.
> > >
> > > Reviewed-by: Simon Glass <sjg@chromium.org>
> > > Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
> > > ---
> > > Changes v2 -> v3:
> > > - Remove the bare software-hash fallback scope
> > > (suggested by Simon Glass)
> > > - Document that SPL_DM_HASH needs a hardware provider unless a
> > > phase-aware software hash provider is added
> > > (suggested by Simon Glass)
> > > ---
> > > boot/image-fit.c | 50 +++++++++++++++++++++++---------------------
> > > drivers/crypto/hash/Kconfig | 13 ++++++++++++
> > > drivers/crypto/hash/Makefile | 2 +-
> > > 3 files changed, 40 insertions(+), 25 deletions(-)
> >
> > The changes in here make me worry a bit about platforms relying on the
> > current behavior (because of the Makefile change), did you put this
> > through CI? https://docs.u-boot-project.org/en/latest/develop/ci_testing.html
>
> Looks fine to me when I ran it through CI:
> https://github.com/u-boot/u-boot/pull/1014/checks
Thanks. We have cases where SPL builds of FEATURE depend on FEATURE and
not SPL_FEATURE being enabled, hence the concern.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 05/14] boot: image: try all DM hash providers
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (3 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 04/14] crypto: hash: allow DM hash " James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-28 11:21 ` Simon Glass
2026-07-20 4:13 ` [PATCH v5 06/14] crypto: aes: fix software key-size handling James Hilliard
` (8 subsequent siblings)
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
FIT hash calculation currently selects UCLASS_HASH device zero directly,
so a provider which does not implement the requested algorithm prevents a
later capable provider from being used. It also turns every provider error
into the same generic FIT hash failure.
Use the common hash provider-selection helper. Fall back to the configured
software implementation only when no DM provider supports the algorithm,
and preserve probe and digest failures from providers which accept it.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v3 -> v4:
- New patch
---
boot/image-fit.c | 26 +++++++++-----------------
1 file changed, 9 insertions(+), 17 deletions(-)
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 555cbf81348..9af38ed8d82 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -1322,30 +1322,22 @@ int calculate_hash(const void *data, int data_len, const char *name,
#ifndef USE_HOSTCC
int rc;
enum HASH_ALGO hash_algo;
- struct udevice *dev;
if (CONFIG_IS_ENABLED(DM_HASH)) {
- rc = uclass_get_device(UCLASS_HASH, 0, &dev);
- if (rc) {
- debug("failed to get hash device, rc=%d\n", rc);
- return -1;
- }
-
hash_algo = hash_algo_lookup_by_name(name);
- if (hash_algo == HASH_ALGO_INVALID) {
- debug("Unsupported hash algorithm\n");
- return -1;
+ if (hash_algo != HASH_ALGO_INVALID)
+ rc = hash_digest_wd_lookup(hash_algo, data, data_len,
+ value, CHUNKSZ);
+ else
+ rc = -EOPNOTSUPP;
+ if (!rc) {
+ *value_len = hash_algo_digest_size(hash_algo);
+ return 0;
}
-
- rc = hash_digest_wd(dev, hash_algo, data, data_len, value,
- CHUNKSZ);
- if (rc) {
+ if (rc != -ENODEV && rc != -EOPNOTSUPP) {
debug("failed to get hash value, rc=%d\n", rc);
return -1;
}
-
- *value_len = hash_algo_digest_size(hash_algo);
- return 0;
}
#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 05/14] boot: image: try all DM hash providers
2026-07-20 4:13 ` [PATCH v5 05/14] boot: image: try all DM hash providers James Hilliard
@ 2026-07-28 11:21 ` Simon Glass
0 siblings, 0 replies; 29+ messages in thread
From: Simon Glass @ 2026-07-28 11:21 UTC (permalink / raw)
To: james.hilliard1
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin, u-boot
On 2026-07-20T04:13:44, James Hilliard <james.hilliard1@gmail.com> wrote:
> boot: image: try all DM hash providers
>
> FIT hash calculation currently selects UCLASS_HASH device zero directly,
> so a provider which does not implement the requested algorithm prevents a
> later capable provider from being used. It also turns every provider error
> into the same generic FIT hash failure.
>
> Use the common hash provider-selection helper. Fall back to the configured
> software implementation only when no DM provider supports the algorithm,
> and preserve probe and digest failures from providers which accept it.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
>
> boot/image-fit.c | 26 +++++++++-----------------
> 1 file changed, 9 insertions(+), 17 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 06/14] crypto: aes: fix software key-size handling
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (4 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 05/14] boot: image: try all DM hash providers James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-28 11:22 ` Simon Glass
2026-07-20 4:13 ` [PATCH v5 07/14] crypto: aes: add software-key provider dispatch James Hilliard
` (7 subsequent siblings)
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
The AES uclass API expresses key sizes in bits, while the common software
AES primitives take key lengths in bytes. The software provider passes the
uclass value through unchanged, so AES-192 and AES-256 select the AES-128
round count and key schedule shape. Key expansion also copies the bit count
as a byte count for every key size.
Validate the uclass key size, convert it to bytes once and retain that byte
length for the software operations. Correct the primitive API documentation
and add NIST ECB and CBC vectors for AES-128, AES-192 and AES-256.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v3 -> v4:
- New patch
---
drivers/crypto/aes/aes-sw.c | 43 +++++++++++++-----
include/uboot_aes.h | 20 ++++-----
test/dm/aes.c | 107 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 148 insertions(+), 22 deletions(-)
diff --git a/drivers/crypto/aes/aes-sw.c b/drivers/crypto/aes/aes-sw.c
index a65200fb79b..6bad343ea7d 100644
--- a/drivers/crypto/aes/aes-sw.c
+++ b/drivers/crypto/aes/aes-sw.c
@@ -12,13 +12,23 @@ struct sw_aes_priv {
u8 key_slots[SW_KEY_SLOTS][AES256_KEY_LENGTH];
u8 key_schedule[AES256_EXPAND_KEY_LENGTH];
u8 selected_slot;
- u32 selected_key_size;
+ u8 selected_key_len;
bool key_expanded;
};
+static int sw_aes_key_len(u32 key_size)
+{
+ if (key_size == AES128_KEY_LENGTH * 8 ||
+ key_size == AES192_KEY_LENGTH * 8 ||
+ key_size == AES256_KEY_LENGTH * 8)
+ return key_size / 8;
+
+ return -EINVAL;
+}
+
static int prepare_aes(struct sw_aes_priv *priv)
{
- if (!priv->selected_key_size) {
+ if (!priv->selected_key_len) {
log_debug("%s: AES key size not set, setup a slot first\n", __func__);
return 1;
}
@@ -28,7 +38,8 @@ static int prepare_aes(struct sw_aes_priv *priv)
priv->key_expanded = 1;
- aes_expand_key(priv->key_slots[priv->selected_slot], priv->selected_key_size,
+ aes_expand_key(priv->key_slots[priv->selected_slot],
+ priv->selected_key_len,
priv->key_schedule);
return 0;
@@ -42,12 +53,16 @@ static int sw_aes_ops_available_key_slots(struct udevice *dev)
static int sw_aes_ops_select_key_slot(struct udevice *dev, u32 key_size, u8 slot)
{
struct sw_aes_priv *priv = dev_get_priv(dev);
+ int key_len;
if (slot >= SW_KEY_SLOTS)
- return 1;
+ return -EINVAL;
+ key_len = sw_aes_key_len(key_size);
+ if (key_len < 0)
+ return key_len;
priv->selected_slot = slot;
- priv->selected_key_size = key_size;
+ priv->selected_key_len = key_len;
priv->key_expanded = 0;
return 0;
@@ -57,14 +72,18 @@ static int sw_aes_ops_set_key_for_key_slot(struct udevice *dev, u32 key_size,
u8 *key, u8 slot)
{
struct sw_aes_priv *priv = dev_get_priv(dev);
+ int key_len;
if (slot >= SW_KEY_SLOTS)
- return 1;
+ return -EINVAL;
+ key_len = sw_aes_key_len(key_size);
+ if (key_len < 0)
+ return key_len;
- memcpy(priv->key_slots[slot], key, key_size / 8);
+ memcpy(priv->key_slots[slot], key, key_len);
if (priv->selected_slot == slot)
- priv->selected_key_size = key_size;
+ priv->selected_key_len = key_len;
priv->key_expanded = 0;
@@ -82,7 +101,7 @@ static int sw_aes_ops_aes_ecb_encrypt(struct udevice *dev, u8 *src, u8 *dst,
return ret;
while (num_aes_blocks > 0) {
- aes_encrypt(priv->selected_key_size, src, priv->key_schedule, dst);
+ aes_encrypt(priv->selected_key_len, src, priv->key_schedule, dst);
num_aes_blocks -= 1;
src += AES_BLOCK_LENGTH;
dst += AES_BLOCK_LENGTH;
@@ -102,7 +121,7 @@ static int sw_aes_ops_aes_ecb_decrypt(struct udevice *dev, u8 *src, u8 *dst,
return ret;
while (num_aes_blocks > 0) {
- aes_decrypt(priv->selected_key_size, src, priv->key_schedule, dst);
+ aes_decrypt(priv->selected_key_len, src, priv->key_schedule, dst);
num_aes_blocks -= 1;
src += AES_BLOCK_LENGTH;
dst += AES_BLOCK_LENGTH;
@@ -121,7 +140,7 @@ static int sw_aes_ops_aes_cbc_encrypt(struct udevice *dev, u8 *iv, u8 *src,
if (ret)
return ret;
- aes_cbc_encrypt_blocks(priv->selected_key_size, priv->key_schedule, iv,
+ aes_cbc_encrypt_blocks(priv->selected_key_len, priv->key_schedule, iv,
src, dst, num_aes_blocks);
return 0;
@@ -137,7 +156,7 @@ static int sw_aes_ops_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src,
if (ret)
return ret;
- aes_cbc_decrypt_blocks(priv->selected_key_size, priv->key_schedule,
+ aes_cbc_decrypt_blocks(priv->selected_key_len, priv->key_schedule,
iv, src, dst, num_aes_blocks);
return 0;
diff --git a/include/uboot_aes.h b/include/uboot_aes.h
index 592b7dbee43..65a9b382843 100644
--- a/include/uboot_aes.h
+++ b/include/uboot_aes.h
@@ -47,30 +47,30 @@ enum {
* operations.
*
* @key Key
- * @key_size Size of the key (in bits)
+ * @key_len Size of the key in bytes
* @expkey Buffer to place expanded key, AES_EXPAND_KEY_LENGTH
*/
-void aes_expand_key(u8 *key, u32 key_size, u8 *expkey);
+void aes_expand_key(u8 *key, u32 key_len, u8 *expkey);
/**
* aes_encrypt() - Encrypt single block of data with AES 128
*
- * @key_size Size of the aes key (in bits)
+ * @key_len Size of the AES key in bytes
* @in Input data
* @expkey Expanded key to use for encryption (from aes_expand_key())
* @out Output data
*/
-void aes_encrypt(u32 key_size, u8 *in, u8 *expkey, u8 *out);
+void aes_encrypt(u32 key_len, u8 *in, u8 *expkey, u8 *out);
/**
* aes_decrypt() - Decrypt single block of data with AES 128
*
- * @key_size Size of the aes key (in bits)
+ * @key_len Size of the AES key in bytes
* @in Input data
* @expkey Expanded key to use for decryption (from aes_expand_key())
* @out Output data
*/
-void aes_decrypt(u32 key_size, u8 *in, u8 *expkey, u8 *out);
+void aes_decrypt(u32 key_len, u8 *in, u8 *expkey, u8 *out);
/**
* Apply chain data to the destination using EOR
@@ -86,27 +86,27 @@ void aes_apply_cbc_chain_data(u8 *cbc_chain_data, u8 *src, u8 *dst);
/**
* aes_cbc_encrypt_blocks() - Encrypt multiple blocks of data with AES CBC.
*
- * @key_size Size of the aes key (in bits)
+ * @key_len Size of the AES key in bytes
* @key_exp Expanded key to use
* @iv Initialization vector
* @src Source data to encrypt
* @dst Destination buffer
* @num_aes_blocks Number of AES blocks to encrypt
*/
-void aes_cbc_encrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
+void aes_cbc_encrypt_blocks(u32 key_len, u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
u32 num_aes_blocks);
/**
* Decrypt multiple blocks of data with AES CBC.
*
- * @key_size Size of the aes key (in bits)
+ * @key_len Size of the AES key in bytes
* @key_exp Expanded key to use
* @iv Initialization vector
* @src Source data to decrypt
* @dst Destination buffer
* @num_aes_blocks Number of AES blocks to decrypt
*/
-void aes_cbc_decrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
+void aes_cbc_decrypt_blocks(u32 key_len, u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
u32 num_aes_blocks);
/* An AES block filled with zeros */
diff --git a/test/dm/aes.c b/test/dm/aes.c
index 702e4db2b35..9c85fb1dac9 100644
--- a/test/dm/aes.c
+++ b/test/dm/aes.c
@@ -55,3 +55,110 @@ static int dm_test_aes(struct unit_test_state *uts)
}
DM_TEST(dm_test_aes, UTF_SCAN_FDT);
+
+struct aes_test_vector {
+ u32 key_size;
+ u8 key[AES256_KEY_LENGTH];
+ u8 ecb[AES_BLOCK_LENGTH];
+ u8 cbc[AES_BLOCK_LENGTH];
+};
+
+static const u8 aes_test_plaintext[AES_BLOCK_LENGTH] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+};
+
+static const u8 aes_test_iv[AES_BLOCK_LENGTH] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+};
+
+static const struct aes_test_vector aes_test_vectors[] = {
+ {
+ .key_size = 128,
+ .key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
+ },
+ .ecb = {
+ 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60,
+ 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,
+ },
+ .cbc = {
+ 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46,
+ 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
+ },
+ }, {
+ .key_size = 192,
+ .key = {
+ 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+ 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+ 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
+ },
+ .ecb = {
+ 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f,
+ 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc,
+ },
+ .cbc = {
+ 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d,
+ 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
+ },
+ }, {
+ .key_size = 256,
+ .key = {
+ 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+ 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+ 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
+ },
+ .ecb = {
+ 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c,
+ 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8,
+ },
+ .cbc = {
+ 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba,
+ 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
+ },
+ },
+};
+
+static int dm_test_aes_key_sizes(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u8 key[AES256_KEY_LENGTH];
+ u8 input[AES_BLOCK_LENGTH];
+ u8 iv[AES_BLOCK_LENGTH];
+ u8 buf[AES_BLOCK_LENGTH];
+ int i, ret;
+
+ ut_assertok(uclass_first_device_err(UCLASS_AES, &dev));
+
+ for (i = 0; i < ARRAY_SIZE(aes_test_vectors); i++) {
+ const struct aes_test_vector *vector = &aes_test_vectors[i];
+
+ memcpy(key, vector->key, vector->key_size / 8);
+ memcpy(input, aes_test_plaintext, sizeof(input));
+ memcpy(iv, aes_test_iv, sizeof(iv));
+ ut_assertok(dm_aes_select_key_slot(dev, vector->key_size, 0));
+ ret = dm_aes_set_key_for_key_slot(dev, vector->key_size, key, 0);
+ ut_assertok(ret);
+
+ ut_assertok(dm_aes_ecb_encrypt(dev, input, buf, 1));
+ ut_asserteq_mem(vector->ecb, buf, sizeof(buf));
+ ut_assertok(dm_aes_ecb_decrypt(dev, buf, buf, 1));
+ ut_asserteq_mem(aes_test_plaintext, buf, sizeof(buf));
+
+ ut_assertok(dm_aes_cbc_encrypt(dev, iv, input, buf, 1));
+ ut_asserteq_mem(vector->cbc, buf, sizeof(buf));
+ ut_assertok(dm_aes_cbc_decrypt(dev, iv, buf, buf, 1));
+ ut_asserteq_mem(aes_test_plaintext, buf, sizeof(buf));
+ }
+
+ ut_asserteq(-EINVAL, dm_aes_select_key_slot(dev, 64, 0));
+ ret = dm_aes_set_key_for_key_slot(dev, 64, key, 0);
+ ut_asserteq(-EINVAL, ret);
+
+ return 0;
+}
+
+DM_TEST(dm_test_aes_key_sizes, UTF_SCAN_FDT);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 06/14] crypto: aes: fix software key-size handling
2026-07-20 4:13 ` [PATCH v5 06/14] crypto: aes: fix software key-size handling James Hilliard
@ 2026-07-28 11:22 ` Simon Glass
0 siblings, 0 replies; 29+ messages in thread
From: Simon Glass @ 2026-07-28 11:22 UTC (permalink / raw)
To: james.hilliard1
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin, u-boot
Hi James,
On 2026-07-20T04:13:44, James Hilliard <james.hilliard1@gmail.com> wrote:
> crypto: aes: fix software key-size handling
>
> The AES uclass API expresses key sizes in bits, while the common software
> AES primitives take key lengths in bytes. The software provider passes the
> uclass value through unchanged, so AES-192 and AES-256 select the AES-128
> round count and key schedule shape. Key expansion also copies the bit count
> as a byte count for every key size.
>
> Validate the uclass key size, convert it to bytes once and retain that byte
> length for the software operations. Correct the primitive API documentation
> and add NIST ECB and CBC vectors for AES-128, AES-192 and AES-256.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
>
> drivers/crypto/aes/aes-sw.c | 43 +++++++++++++-----
> include/uboot_aes.h | 20 ++++-----
> test/dm/aes.c | 107 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 148 insertions(+), 22 deletions(-)
> diff --git a/include/uboot_aes.h b/include/uboot_aes.h
> @@ -47,30 +47,30 @@ enum {
> * aes_encrypt() - Encrypt single block of data with AES 128
> *
> - * @key_size Size of the aes key (in bits)
> + * @key_len Size of the AES key in bytes
I see stale 'with AES 128' wording in both the aes_encrypt() and
aes_decrypt() headers, though the round count now comes from the key
length. Please can you drop the '128' so the summary matches the
parameter you just corrected?
> diff --git a/drivers/crypto/aes/aes-sw.c b/drivers/crypto/aes/aes-sw.c
> @@ -12,13 +12,23 @@ struct sw_aes_priv {
> static int prepare_aes(struct sw_aes_priv *priv)
> {
> - if (!priv->selected_key_size) {
> + if (!priv->selected_key_len) {
> log_debug("%s: AES key size not set, setup a slot first\n", __func__);
> return 1;
> }
How about returning a proper -errno here - it would tidy things up
while you are respinning.
In any case:
Reviewed-by: Simon Glass <sjg@chromium.org>
Regards,
Simon
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 07/14] crypto: aes: add software-key provider dispatch
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (5 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 06/14] crypto: aes: fix software key-size handling James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-28 11:23 ` Simon Glass
2026-07-20 4:13 ` [PATCH v5 08/14] boot: image: add FIT decrypt-to-buffer helper James Hilliard
` (6 subsequent siblings)
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
Generic callers which supply an AES key need a provider-owned slot policy,
since hardware may reserve or preload particular key slots. Selecting only
UCLASS_AES device zero also prevents a later provider from handling an
operation unsupported by the first device.
Add a callback through which each provider exposes a slot for
software-provided keys. Add a CBC decrypt helper which probes providers in
order, continuing only when a provider explicitly reports an unsupported
operation and preserving hard probe, setup and decrypt failures.
Use slot zero for the software provider and avoid Tegra's preloaded SBK
slot. Add sandbox coverage for provider fallback and hard-error
propagation.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v3 -> v4:
- New patch
---
drivers/crypto/aes/aes-sw.c | 6 ++
drivers/crypto/aes/aes-uclass.c | 73 +++++++++++++++++++
drivers/crypto/tegra/tegra_aes.c | 7 ++
include/uboot_aes.h | 66 +++++++++++++++--
test/dm/aes.c | 149 +++++++++++++++++++++++++++++++++++++++
5 files changed, 297 insertions(+), 4 deletions(-)
diff --git a/drivers/crypto/aes/aes-sw.c b/drivers/crypto/aes/aes-sw.c
index 6bad343ea7d..fe06fd2d5ba 100644
--- a/drivers/crypto/aes/aes-sw.c
+++ b/drivers/crypto/aes/aes-sw.c
@@ -50,6 +50,11 @@ static int sw_aes_ops_available_key_slots(struct udevice *dev)
return SW_KEY_SLOTS;
}
+static int sw_aes_ops_get_software_key_slot(struct udevice *dev)
+{
+ return 0;
+}
+
static int sw_aes_ops_select_key_slot(struct udevice *dev, u32 key_size, u8 slot)
{
struct sw_aes_priv *priv = dev_get_priv(dev);
@@ -164,6 +169,7 @@ static int sw_aes_ops_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src,
static const struct aes_ops aes_ops_sw = {
.available_key_slots = sw_aes_ops_available_key_slots,
+ .get_software_key_slot = sw_aes_ops_get_software_key_slot,
.select_key_slot = sw_aes_ops_select_key_slot,
.set_key_for_key_slot = sw_aes_ops_set_key_for_key_slot,
.aes_ecb_encrypt = sw_aes_ops_aes_ecb_encrypt,
diff --git a/drivers/crypto/aes/aes-uclass.c b/drivers/crypto/aes/aes-uclass.c
index 5bdd3d736c4..ec1ea075367 100644
--- a/drivers/crypto/aes/aes-uclass.c
+++ b/drivers/crypto/aes/aes-uclass.c
@@ -23,6 +23,21 @@ int dm_aes_get_available_key_slots(struct udevice *dev)
return ops->available_key_slots(dev);
}
+int dm_aes_get_software_key_slot(struct udevice *dev)
+{
+ const struct aes_ops *ops;
+
+ if (!dev)
+ return -ENODEV;
+
+ ops = aes_get_ops(dev);
+
+ if (!ops->get_software_key_slot)
+ return -ENOSYS;
+
+ return ops->get_software_key_slot(dev);
+}
+
int dm_aes_select_key_slot(struct udevice *dev, u32 key_size, u8 slot)
{
const struct aes_ops *ops;
@@ -113,6 +128,64 @@ int dm_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src, u8 *dst, u32 num_ae
return ops->aes_cbc_decrypt(dev, iv, src, dst, num_aes_blocks);
}
+static bool aes_op_unsupported(int ret)
+{
+ return ret == -ENOSYS || ret == -EOPNOTSUPP;
+}
+
+int dm_aes_cbc_decrypt_with_key(u32 key_size, u8 *key, u8 *iv, u8 *src,
+ u8 *dst, u32 num_aes_blocks)
+{
+ struct udevice *dev;
+ int first_probe_err = 0;
+ bool found = false;
+ int ret;
+
+ for (ret = uclass_first_device_check(UCLASS_AES, &dev); dev;
+ ret = uclass_next_device_check(&dev)) {
+ u8 slot;
+
+ found = true;
+ if (ret) {
+ if (!first_probe_err)
+ first_probe_err = ret;
+ continue;
+ }
+
+ ret = dm_aes_get_software_key_slot(dev);
+ if (aes_op_unsupported(ret))
+ continue;
+ if (ret < 0)
+ return ret;
+ if (ret > U8_MAX)
+ return -ERANGE;
+ slot = ret;
+
+ ret = dm_aes_set_key_for_key_slot(dev, key_size, key, slot);
+ if (aes_op_unsupported(ret))
+ continue;
+ if (ret)
+ return ret;
+
+ ret = dm_aes_select_key_slot(dev, key_size, slot);
+ if (aes_op_unsupported(ret))
+ continue;
+ if (ret)
+ return ret;
+
+ ret = dm_aes_cbc_decrypt(dev, iv, src, dst, num_aes_blocks);
+ if (!ret)
+ return 0;
+ if (!aes_op_unsupported(ret))
+ return ret;
+ }
+
+ if (first_probe_err)
+ return first_probe_err;
+
+ return found ? -EOPNOTSUPP : -ENODEV;
+}
+
static void left_shift_vector(u8 *in, u8 *out, int size)
{
int carry = 0;
diff --git a/drivers/crypto/tegra/tegra_aes.c b/drivers/crypto/tegra/tegra_aes.c
index 55a4cec525b..348115ac5a7 100644
--- a/drivers/crypto/tegra/tegra_aes.c
+++ b/drivers/crypto/tegra/tegra_aes.c
@@ -321,6 +321,12 @@ static int tegra_aes_ops_available_key_slots(struct udevice *dev)
return 4; /* 4 slots in Tegra20 and Tegra30 */
}
+static int tegra_aes_ops_get_software_key_slot(struct udevice *dev)
+{
+ /* Avoid SBK slot 0, which may hold a preloaded key. */
+ return TEGRA_AES_SLOT_SBK + 1;
+}
+
static int tegra_aes_ops_select_key_slot(struct udevice *dev, u32 key_size, u8 slot)
{
struct tegra_aes_priv *priv = dev_get_priv(dev);
@@ -565,6 +571,7 @@ static int tegra_aes_probe(struct udevice *dev)
static const struct aes_ops tegra_aes_ops = {
.available_key_slots = tegra_aes_ops_available_key_slots,
+ .get_software_key_slot = tegra_aes_ops_get_software_key_slot,
.select_key_slot = tegra_aes_ops_select_key_slot,
.set_key_for_key_slot = tegra_aes_ops_set_key_for_key_slot,
.aes_ecb_encrypt = tegra_aes_ops_aes_ecb_encrypt,
diff --git a/include/uboot_aes.h b/include/uboot_aes.h
index 65a9b382843..a2c35aa30ce 100644
--- a/include/uboot_aes.h
+++ b/include/uboot_aes.h
@@ -128,11 +128,17 @@ struct udevice;
* Note that some devices like Tegra AES engine may contain preloaded keys by bootrom,
* thus in those cases the set_key_for_key_slot() may be skipped.
*
- * Sequence for a series of AES CBC encryption, one decryption and a CMAC hash example
- * with 128bits key at slot 0 would be as follow:
+ * Generic callers which load a software-provided key should first ask the
+ * driver for a suitable software key slot. This lets hardware drivers avoid
+ * reserved or preloaded slots while keeping the slot policy local to the
+ * provider.
*
- * set_key_for_key_slot(DEV, 128, KEY, 0);
- * select_key_slot(DEV, 128, 0);
+ * Sequence for a series of AES CBC encryption, one decryption and a CMAC hash
+ * example with a 128-bit software-provided key would be as follows:
+ *
+ * slot = get_software_key_slot(DEV);
+ * set_key_for_key_slot(DEV, 128, KEY, slot);
+ * select_key_slot(DEV, 128, slot);
* aes_cbc_encrypt(DEV, IV1, SRC1, DST1, LEN1);
* aes_cbc_encrypt(DEV, IV2, SRC2, DST2, LEN2);
* aes_cbc_decrypt(DEV, IV3, SRC3, DST3, LEN3);
@@ -146,6 +152,16 @@ struct aes_ops {
*/
int (*available_key_slots)(struct udevice *dev);
+ /**
+ * get_software_key_slot() - Get a slot for software-provided keys
+ *
+ * @dev The AES udevice
+ * @return Key slot to use for a software-provided key,
+ * 0 or positive on success, negative value on
+ * failure
+ */
+ int (*get_software_key_slot)(struct udevice *dev);
+
/**
* select_key_slot() - Selects the AES key slot to use for following operations
*
@@ -210,6 +226,7 @@ struct aes_ops {
* @iv Initialization vector
* @src Source data of length 'num_aes_blocks' blocks
* @dst Destination data of length 'num_aes_blocks' blocks
+ * Must support dst == src for in-place decrypt
* @num_aes_blocks Number of AES blocks to encrypt/decrypt
* @return 0 on success, negative value on failure
*/
@@ -229,6 +246,15 @@ struct aes_ops {
*/
int dm_aes_get_available_key_slots(struct udevice *dev);
+/**
+ * dm_aes_get_software_key_slot - Get a slot for software-provided keys
+ *
+ * @dev The AES udevice
+ * Return: Key slot to use for a software-provided key,
+ * 0 or positive on success, -ve on failure
+ */
+int dm_aes_get_software_key_slot(struct udevice *dev);
+
/**
* dm_aes_select_key_slot - Selects the AES key slot to use for following operations
*
@@ -291,11 +317,31 @@ int dm_aes_cbc_encrypt(struct udevice *dev, u8 *iv, u8 *src, u8 *dst, u32 num_ae
* @iv Initialization vector
* @src Source data of length 'num_aes_blocks' blocks
* @dst Destination data of length 'num_aes_blocks' blocks
+ * Must support dst == src for in-place decrypt
* @num_aes_blocks Number of AES blocks to encrypt/decrypt
* Return: 0 on success, negative value on failure
*/
int dm_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src, u8 *dst, u32 num_aes_blocks);
+/**
+ * dm_aes_cbc_decrypt_with_key() - Decrypt using a software-provided key
+ *
+ * Probe AES providers in order and use the first one which accepts the key
+ * and CBC operation. Hard failures from an accepting provider are returned
+ * without trying another provider.
+ *
+ * @key_size: AES key size in bits
+ * @key: AES key
+ * @iv: Initialization vector
+ * @src: Ciphertext input
+ * @dst: Plaintext output, which may be the same buffer as @src
+ * @num_aes_blocks: Number of AES blocks to decrypt
+ * Return: 0 on success, -ENODEV if there are no providers, -EOPNOTSUPP if no
+ * provider supports the operation, or another error from a provider
+ */
+int dm_aes_cbc_decrypt_with_key(u32 key_size, u8 *key, u8 *iv, u8 *src,
+ u8 *dst, u32 num_aes_blocks);
+
/**
* dm_aes_cmac - Hashes the input data with AES-CMAC, putting the result into dst.
* The key slot must be selected already.
@@ -316,6 +362,11 @@ static inline int dm_aes_get_available_key_slots(struct udevice *dev)
return -ENOSYS;
}
+static inline int dm_aes_get_software_key_slot(struct udevice *dev)
+{
+ return -ENOSYS;
+}
+
static inline int dm_aes_select_key_slot(struct udevice *dev, u32 key_size, u8 slot)
{
return -ENOSYS;
@@ -351,6 +402,13 @@ static inline int dm_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src,
return -ENOSYS;
}
+static inline int dm_aes_cbc_decrypt_with_key(u32 key_size, u8 *key, u8 *iv,
+ u8 *src, u8 *dst,
+ u32 num_aes_blocks)
+{
+ return -ENOSYS;
+}
+
static inline int dm_aes_cmac(struct udevice *dev, u8 *src, u8 *dst, u32 num_aes_blocks)
{
return -ENOSYS;
diff --git a/test/dm/aes.c b/test/dm/aes.c
index 9c85fb1dac9..9f4ce42ab2a 100644
--- a/test/dm/aes.c
+++ b/test/dm/aes.c
@@ -6,7 +6,10 @@
*/
#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
#include <dm/test.h>
+#include <dm/uclass-internal.h>
#include <uboot_aes.h>
#include <test/test.h>
#include <test/ut.h>
@@ -162,3 +165,149 @@ static int dm_test_aes_key_sizes(struct unit_test_state *uts)
}
DM_TEST(dm_test_aes_key_sizes, UTF_SCAN_FDT);
+
+static int unsupported_calls;
+static int success_calls;
+static int hard_error_calls;
+
+static int aes_test_get_slot(struct udevice *dev)
+{
+ return 0;
+}
+
+static int aes_test_set_key(struct udevice *dev, u32 key_size, u8 *key,
+ u8 slot)
+{
+ return 0;
+}
+
+static int aes_test_select_key(struct udevice *dev, u32 key_size, u8 slot)
+{
+ return 0;
+}
+
+static int aes_test_unsupported_slot(struct udevice *dev)
+{
+ unsupported_calls++;
+
+ return -ENOSYS;
+}
+
+static int aes_test_success_decrypt(struct udevice *dev, u8 *iv, u8 *src,
+ u8 *dst, u32 num_aes_blocks)
+{
+ success_calls++;
+ memcpy(dst, src, num_aes_blocks * AES_BLOCK_LENGTH);
+
+ return 0;
+}
+
+static int aes_test_hard_error_decrypt(struct udevice *dev, u8 *iv, u8 *src,
+ u8 *dst, u32 num_aes_blocks)
+{
+ hard_error_calls++;
+
+ return -EINVAL;
+}
+
+static const struct aes_ops aes_test_unsupported_ops = {
+ .get_software_key_slot = aes_test_unsupported_slot,
+};
+
+static const struct aes_ops aes_test_success_ops = {
+ .get_software_key_slot = aes_test_get_slot,
+ .set_key_for_key_slot = aes_test_set_key,
+ .select_key_slot = aes_test_select_key,
+ .aes_cbc_decrypt = aes_test_success_decrypt,
+};
+
+static const struct aes_ops aes_test_hard_error_ops = {
+ .get_software_key_slot = aes_test_get_slot,
+ .set_key_for_key_slot = aes_test_set_key,
+ .select_key_slot = aes_test_select_key,
+ .aes_cbc_decrypt = aes_test_hard_error_decrypt,
+};
+
+U_BOOT_DRIVER(aes_test_unsupported_drv) = {
+ .name = "aes_test_unsupported",
+ .id = UCLASS_AES,
+ .ops = &aes_test_unsupported_ops,
+};
+
+U_BOOT_DRIVER(aes_test_success_drv) = {
+ .name = "aes_test_success",
+ .id = UCLASS_AES,
+ .ops = &aes_test_success_ops,
+};
+
+U_BOOT_DRIVER(aes_test_hard_error_drv) = {
+ .name = "aes_test_hard_error",
+ .id = UCLASS_AES,
+ .ops = &aes_test_hard_error_ops,
+};
+
+static int aes_test_unbind_all(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ for (;;) {
+ ret = uclass_find_first_device(UCLASS_AES, &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 aes_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_aes_provider_selection(struct unit_test_state *uts)
+{
+ u8 key[AES128_KEY_LENGTH] = { };
+ u8 iv[AES_BLOCK_LENGTH] = { };
+ u8 src[AES_BLOCK_LENGTH] = { 0x5a };
+ u8 dst[AES_BLOCK_LENGTH] = { };
+ int ret;
+
+ ut_assertok(aes_test_unbind_all());
+ ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_unsupported_drv),
+ "aes-unsupported"));
+ ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_success_drv),
+ "aes-success"));
+
+ unsupported_calls = 0;
+ success_calls = 0;
+ ut_assertok(dm_aes_cbc_decrypt_with_key(128, key, iv, src, dst, 1));
+ ut_asserteq(1, unsupported_calls);
+ ut_asserteq(1, success_calls);
+ ut_asserteq_mem(src, dst, sizeof(src));
+
+ ut_assertok(aes_test_unbind_all());
+ ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_hard_error_drv),
+ "aes-hard-error"));
+ ut_assertok(aes_test_bind(DM_DRIVER_GET(aes_test_success_drv),
+ "aes-success"));
+
+ hard_error_calls = 0;
+ success_calls = 0;
+ ret = dm_aes_cbc_decrypt_with_key(128, key, iv, src, dst, 1);
+ ut_asserteq(-EINVAL, ret);
+ ut_asserteq(1, hard_error_calls);
+ ut_asserteq(0, success_calls);
+
+ return 0;
+}
+
+DM_TEST(dm_test_aes_provider_selection, UTF_SCAN_FDT);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 07/14] crypto: aes: add software-key provider dispatch
2026-07-20 4:13 ` [PATCH v5 07/14] crypto: aes: add software-key provider dispatch James Hilliard
@ 2026-07-28 11:23 ` Simon Glass
0 siblings, 0 replies; 29+ messages in thread
From: Simon Glass @ 2026-07-28 11:23 UTC (permalink / raw)
To: james.hilliard1
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin, u-boot
On 2026-07-20T04:13:44, James Hilliard <james.hilliard1@gmail.com> wrote:
> crypto: aes: add software-key provider dispatch
>
> Generic callers which supply an AES key need a provider-owned slot policy,
> since hardware may reserve or preload particular key slots. Selecting only
> UCLASS_AES device zero also prevents a later provider from handling an
> operation unsupported by the first device.
>
> Add a callback through which each provider exposes a slot for
> software-provided keys. Add a CBC decrypt helper which probes providers in
> order, continuing only when a provider explicitly reports an unsupported
> operation and preserving hard probe, setup and decrypt failures.
>
> Use slot zero for the software provider and avoid Tegra's preloaded SBK
> slot. Add sandbox coverage for provider fallback and hard-error
> propagation.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
>
> drivers/crypto/aes/aes-sw.c | 6 ++
> drivers/crypto/aes/aes-uclass.c | 73 +++++++++++++++++++
> drivers/crypto/tegra/tegra_aes.c | 7 ++
> include/uboot_aes.h | 66 +++++++++++++++--
> test/dm/aes.c | 149 +++++++++++++++++++++++++++++++++++++++
> 5 files changed, 297 insertions(+), 4 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 08/14] boot: image: add FIT decrypt-to-buffer helper
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (6 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 07/14] crypto: aes: add software-key provider dispatch James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-28 11:25 ` Simon Glass
2026-07-20 4:13 ` [PATCH v5 09/14] spl: fit: support encrypted payloads James Hilliard
` (5 subsequent siblings)
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
FIT cipher support currently allocates the output buffer inside the AES
helper. SPL often needs to decrypt directly into a caller-selected
buffer, for example a load buffer or a scratch buffer used before
decompression.
Add a decrypt_to callback to the FIT cipher algorithm and wire it up for
AES. The existing allocating decrypt path becomes a wrapper around the
new helper.
Validate the FIT cipher key length, IV length and unciphered-size
property while preparing decryption, and build lib/aes/ by phase when
FIT_CIPHER is enabled so the target-side decrypt helper is available to
SPL builds. Use the DM AES provider helper when enabled, retaining the
software implementation only when no provider supports the operation.
For U-Boot proper, use decrypt_to for in-place decryption when the FIT
payload is already in writable RAM. The encrypted data is no longer
needed after hash verification, and this avoids a full-size allocation
for encrypted payloads loaded into DRAM.
Add sandbox coverage for out-of-place and in-place AES-256 decrypt and
malformed key, IV and size inputs.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v3 -> v4:
- Validate cipher metadata before reading the key length
- Preserve hard provider failures and fall back only when unsupported
- Treat -EINVAL as a hard provider error
- Fix disabled-feature declarations and use the public test prototype
- Add in-place decrypt and malformed-input tests
- Simplify the legacy allocating decrypt wrapper
Changes v2 -> v3:
- Flip the image_aes_decrypt_to() host-tool guard
(suggested by Simon Glass)
- Let image_aes_decrypt_to() be the single length-validation path
(suggested by Simon Glass)
- Document that AES CBC decrypt providers must support in-place decrypt
(suggested by Simon Glass)
Changes v1 -> v2:
- Explain FIT cipher validation (suggested by Simon Glass)
- Explain phase-keyed lib/aes builds (suggested by Simon Glass)
- Return -ENOSYS without decrypt support (suggested by Simon Glass)
- Use decrypt_to for U-Boot proper in-place decrypt
---
boot/image-cipher.c | 45 ++++++++++++++++++----
boot/image-fit.c | 33 ++++++++++++++--
include/image.h | 39 +++++++++++++++++--
include/u-boot/aes.h | 27 ++++++++++----
lib/Makefile | 2 +-
lib/aes/aes-decrypt.c | 91 +++++++++++++++++++++++++++++++++++++--------
test/lib/Makefile | 3 ++
test/lib/test_aes_decrypt.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 290 insertions(+), 39 deletions(-)
diff --git a/boot/image-cipher.c b/boot/image-cipher.c
index 9d389f26cea..9470370a534 100644
--- a/boot/image-cipher.c
+++ b/boot/image-cipher.c
@@ -25,6 +25,7 @@ struct cipher_algo cipher_algos[] = {
#endif
.encrypt = image_aes_encrypt,
.decrypt = image_aes_decrypt,
+ .decrypt_to = image_aes_decrypt_to,
.add_cipher_data = image_aes_add_cipher_data
},
{
@@ -36,6 +37,7 @@ struct cipher_algo cipher_algos[] = {
#endif
.encrypt = image_aes_encrypt,
.decrypt = image_aes_decrypt,
+ .decrypt_to = image_aes_decrypt_to,
.add_cipher_data = image_aes_add_cipher_data
},
{
@@ -47,6 +49,7 @@ struct cipher_algo cipher_algos[] = {
#endif
.encrypt = image_aes_encrypt,
.decrypt = image_aes_decrypt,
+ .decrypt_to = image_aes_decrypt_to,
.add_cipher_data = image_aes_add_cipher_data
}
};
@@ -70,6 +73,7 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info,
int cipher_noffset)
{
const void *fdt = gd_fdt_blob();
+ int key_len, iv_len;
const char *node_name;
char node_path[128];
int noffset;
@@ -94,7 +98,7 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info,
return -1;
}
- info->iv = fdt_getprop(fit, cipher_noffset, "iv", NULL);
+ info->iv = fdt_getprop(fit, cipher_noffset, "iv", &iv_len);
info->ivname = fdt_getprop(fit, cipher_noffset, "iv-name-hint", NULL);
if (!info->iv && !info->ivname) {
@@ -136,20 +140,28 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info,
}
/* read key */
- info->key = fdt_getprop(fdt, noffset, "key", NULL);
+ info->key = fdt_getprop(fdt, noffset, "key", &key_len);
if (!info->key) {
printf("Can't get key in cipher node '%s'\n", node_path);
return -1;
}
+ if (key_len != info->cipher->key_len) {
+ printf("Bad key length in cipher node '%s'\n", node_path);
+ return -1;
+ }
/* read iv */
if (!info->iv) {
- info->iv = fdt_getprop(fdt, noffset, "iv", NULL);
+ info->iv = fdt_getprop(fdt, noffset, "iv", &iv_len);
if (!info->iv) {
printf("Can't get IV in cipher node '%s'\n", node_path);
return -1;
}
}
+ if (iv_len != info->cipher->iv_len) {
+ printf("Bad IV length for cipher in image '%s'\n", node_name);
+ return -1;
+ }
return 0;
}
@@ -165,11 +177,28 @@ int fit_image_decrypt_data(const void *fit,
ret = fit_image_setup_decrypt(&info, fit, image_noffset,
cipher_noffset);
if (ret < 0)
- goto out;
+ return ret;
+
+ return info.cipher->decrypt(&info, data_ciphered, size_ciphered,
+ data_unciphered, size_unciphered);
+}
+
+int fit_image_decrypt_data_to(const void *fit,
+ int image_noffset, int cipher_noffset,
+ const void *data_ciphered, size_t size_ciphered,
+ void *data_unciphered, size_t *size_unciphered)
+{
+ struct image_cipher_info info;
+ int ret;
+
+ ret = fit_image_setup_decrypt(&info, fit, image_noffset,
+ cipher_noffset);
+ if (ret < 0)
+ return ret;
- ret = info.cipher->decrypt(&info, data_ciphered, size_ciphered,
- data_unciphered, size_unciphered);
+ if (!info.cipher->decrypt_to)
+ return -ENOSYS;
- out:
- return ret;
+ return info.cipher->decrypt_to(&info, data_ciphered, size_ciphered,
+ data_unciphered, size_unciphered);
}
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 9af38ed8d82..6b55316dd37 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -1028,7 +1028,7 @@ int fit_image_get_data_size(const void *fit, int noffset, int *data_size)
*
* @fit: pointer to the FIT image header
* @noffset: component image node offset
- * @data_size: holds the data-size property
+ * @data_size: holds the data-size-unciphered property
*
* returns:
* 0, on success
@@ -1038,10 +1038,13 @@ int fit_image_get_data_size_unciphered(const void *fit, int noffset,
size_t *data_size)
{
const fdt32_t *val;
+ int len;
- val = fdt_getprop(fit, noffset, "data-size-unciphered", NULL);
+ val = fdt_getprop(fit, noffset, "data-size-unciphered", &len);
if (!val)
return -ENOENT;
+ if (len != sizeof(*val))
+ return -EINVAL;
*data_size = (size_t)fdt32_to_cpu(*val);
@@ -1562,15 +1565,37 @@ static int fit_image_uncipher(const void *fit, int image_noffset,
if (cipher_noffset < 0)
return 0;
+#ifndef USE_HOSTCC
+ if (!tools_build()) {
+ ulong start = map_to_sysmem(*data);
+ ulong end = start + *size;
+
+ /*
+ * Avoid a full-size allocation when the FIT payload is already
+ * in writable DRAM. The encrypted bytes are no longer needed
+ * after hash verification has completed.
+ */
+ if (end >= start && start >= gd->ram_base && end <= gd->ram_top) {
+ ret = fit_image_decrypt_data_to(fit, image_noffset,
+ cipher_noffset,
+ *data, *size, *data,
+ &size_dst);
+ if (ret != -ENOSYS)
+ goto out;
+ }
+ }
+#endif
+
ret = fit_image_decrypt_data(fit, image_noffset, cipher_noffset,
*data, *size, &dst, &size_dst);
if (ret)
goto out;
*data = dst;
- *size = size_dst;
+out:
+ if (!ret)
+ *size = size_dst;
- out:
return ret;
}
diff --git a/include/image.h b/include/image.h
index 4b3c9c87bf5..5a014def940 100644
--- a/include/image.h
+++ b/include/image.h
@@ -1871,11 +1871,40 @@ int fit_image_check_sig(const void *fit, int noffset, const void *data,
size_t size, const void *key_blob, int required_keynode,
char **err_msgp);
-int fit_image_decrypt_data(const void *fit,
- int image_noffset, int cipher_noffset,
- const void *data, size_t size,
+/**
+ * fit_image_decrypt_data() - Decrypt a FIT image payload
+ *
+ * @fit: FIT image
+ * @image_noffset: Offset of the image node to decrypt
+ * @cipher_noffset: Offset of the cipher node for the image
+ * @data: Encrypted image payload
+ * @size: Size of encrypted image payload
+ * @data_unciphered: Returns allocated decrypted payload
+ * @size_unciphered: Returns size of decrypted payload
+ * Return: 0 on success, <0 on error
+ */
+int fit_image_decrypt_data(const void *fit, int image_noffset,
+ int cipher_noffset, const void *data, size_t size,
void **data_unciphered, size_t *size_unciphered);
+/**
+ * fit_image_decrypt_data_to() - Decrypt a FIT image payload to a buffer
+ *
+ * @fit: FIT image
+ * @image_noffset: Offset of the image node to decrypt
+ * @cipher_noffset: Offset of the cipher node for the image
+ * @data: Encrypted image payload
+ * @size: Size of encrypted image payload
+ * @data_unciphered: Destination buffer for decrypted payload. The caller
+ * must provide at least @size bytes.
+ * @size_unciphered: Returns size of decrypted payload
+ * Return: 0 on success, <0 on error
+ */
+int fit_image_decrypt_data_to(const void *fit,
+ int image_noffset, int cipher_noffset,
+ const void *data, size_t size,
+ void *data_unciphered, size_t *size_unciphered);
+
/**
* fit_region_make_list() - Make a list of regions to hash
*
@@ -1969,6 +1998,10 @@ struct cipher_algo {
int (*decrypt)(struct image_cipher_info *info,
const void *cipher, size_t cipher_len,
void **data, size_t *data_len);
+
+ int (*decrypt_to)(struct image_cipher_info *info,
+ const void *cipher, size_t cipher_len,
+ void *data, size_t *data_len);
};
int fit_image_cipher_get_algo(const void *fit, int noffset, char **algo);
diff --git a/include/u-boot/aes.h b/include/u-boot/aes.h
index acbc50b9e6f..8fd43f02adc 100644
--- a/include/u-boot/aes.h
+++ b/include/u-boot/aes.h
@@ -16,15 +16,16 @@ int image_aes_encrypt(struct image_cipher_info *info,
int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest,
void *fit, int node_noffset);
#else
-int image_aes_encrypt(struct image_cipher_info *info,
- const unsigned char *data, int size,
- unsigned char **cipher, int *cipher_len)
+static inline int image_aes_encrypt(struct image_cipher_info *info,
+ const unsigned char *data, int size,
+ unsigned char **cipher, int *cipher_len)
{
return -ENXIO;
}
-int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest,
- void *fit, int node_noffset)
+static inline int image_aes_add_cipher_data(struct image_cipher_info *info,
+ void *keydest, void *fit,
+ int node_noffset)
{
return -ENXIO;
}
@@ -34,10 +35,20 @@ int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest,
int image_aes_decrypt(struct image_cipher_info *info,
const void *cipher, size_t cipher_len,
void **data, size_t *size);
+int image_aes_decrypt_to(struct image_cipher_info *info,
+ const void *cipher, size_t cipher_len,
+ void *data, size_t *size);
#else
-int image_aes_decrypt(struct image_cipher_info *info,
- const void *cipher, size_t cipher_len,
- void **data, size_t *size)
+static inline int image_aes_decrypt(struct image_cipher_info *info,
+ const void *cipher, size_t cipher_len,
+ void **data, size_t *size)
+{
+ return -ENXIO;
+}
+
+static inline int image_aes_decrypt_to(struct image_cipher_info *info,
+ const void *cipher, size_t cipher_len,
+ void *data, size_t *size)
{
return -ENXIO;
}
diff --git a/lib/Makefile b/lib/Makefile
index 222378a8531..e8ec4660b38 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -19,7 +19,6 @@ obj-$(CONFIG_ARCH_AT91) += at91/
obj-$(CONFIG_OPTEE_LIB) += optee/
obj-$(CONFIG_AES) += aes.o
-obj-$(CONFIG_AES) += aes/
obj-$(CONFIG_$(PHASE_)BINMAN_FDT) += binman.o
obj-$(CONFIG_FW_LOADER) += fw_loader.o
@@ -89,6 +88,7 @@ obj-$(CONFIG_$(PHASE_)ASN1_DECODER_LEGACY) += asn1_decoder.o
obj-$(CONFIG_$(PHASE_)ZLIB) += zlib/
obj-$(CONFIG_$(PHASE_)ZSTD) += zstd/
+obj-$(CONFIG_$(PHASE_)FIT_CIPHER) += aes/
obj-$(CONFIG_$(PHASE_)GZIP) += gunzip.o
obj-$(CONFIG_$(PHASE_)LZO) += lzo/
obj-$(CONFIG_$(PHASE_)LZMA) += lzma/
diff --git a/lib/aes/aes-decrypt.c b/lib/aes/aes-decrypt.c
index 741102a4723..43a5f8742bb 100644
--- a/lib/aes/aes-decrypt.c
+++ b/lib/aes/aes-decrypt.c
@@ -4,37 +4,98 @@
*/
#ifndef USE_HOSTCC
+#include <dm.h>
#include <malloc.h>
#endif
#include <image.h>
#include <uboot_aes.h>
+#ifndef USE_HOSTCC
+static int image_aes_validate(struct image_cipher_info *info,
+ const void *cipher, size_t cipher_len,
+ void *data, size_t *size)
+{
+ if (!info || !info->cipher || !info->key || !info->iv || !cipher ||
+ !data || !size)
+ return -EINVAL;
+ if (info->cipher->iv_len != AES_BLOCK_LENGTH ||
+ (info->cipher->key_len != AES128_KEY_LENGTH &&
+ info->cipher->key_len != AES192_KEY_LENGTH &&
+ info->cipher->key_len != AES256_KEY_LENGTH))
+ return -EINVAL;
+ if (!cipher_len || cipher_len % AES_BLOCK_LENGTH ||
+ info->size_unciphered > cipher_len)
+ return -EINVAL;
+
+ return 0;
+}
+#endif
+
+int image_aes_decrypt_to(struct image_cipher_info *info,
+ const void *cipher, size_t cipher_len,
+ void *data, size_t *size)
+{
+#ifdef USE_HOSTCC
+ return -ENOSYS;
+#else
+ unsigned int aes_blocks, key_len;
+ int ret;
+
+ ret = image_aes_validate(info, cipher, cipher_len, data, size);
+ if (ret)
+ return ret;
+ key_len = info->cipher->key_len;
+ aes_blocks = cipher_len / AES_BLOCK_LENGTH;
+
+ if (CONFIG_IS_ENABLED(DM_AES)) {
+ ret = dm_aes_cbc_decrypt_with_key(key_len * 8, (u8 *)info->key,
+ (u8 *)info->iv, (u8 *)cipher,
+ data, aes_blocks);
+ if (!ret) {
+ *size = info->size_unciphered;
+ return 0;
+ }
+ if (ret != -ENODEV && ret != -EOPNOTSUPP)
+ return ret;
+ }
+
+ if (!IS_ENABLED(CONFIG_XPL_BUILD)) {
+ unsigned char key_exp[AES256_EXPAND_KEY_LENGTH];
+
+ /* First we expand the key. */
+ aes_expand_key((u8 *)info->key, key_len, key_exp);
+
+ aes_cbc_decrypt_blocks(key_len, key_exp, (u8 *)info->iv,
+ (u8 *)cipher, data, aes_blocks);
+ *size = info->size_unciphered;
+ return 0;
+ }
+
+ return -ENOSYS;
+#endif
+}
+
int image_aes_decrypt(struct image_cipher_info *info,
const void *cipher, size_t cipher_len,
void **data, size_t *size)
{
-#ifndef USE_HOSTCC
- unsigned char key_exp[AES256_EXPAND_KEY_LENGTH];
- unsigned int aes_blocks, key_len = info->cipher->key_len;
+#ifdef USE_HOSTCC
+ return 0;
+#else
+ int ret;
*data = malloc(cipher_len);
if (!*data) {
printf("Can't allocate memory to decrypt\n");
return -ENOMEM;
}
- *size = info->size_unciphered;
-
- memcpy(&key_exp[0], info->key, key_len);
-
- /* First we expand the key. */
- aes_expand_key((u8 *)info->key, key_len, key_exp);
- /* Calculate the number of AES blocks to encrypt. */
- aes_blocks = DIV_ROUND_UP(cipher_len, AES_BLOCK_LENGTH);
+ ret = image_aes_decrypt_to(info, cipher, cipher_len, *data, size);
+ if (ret) {
+ free(*data);
+ *data = NULL;
+ }
- aes_cbc_decrypt_blocks(key_len, key_exp, (u8 *)info->iv,
- (u8 *)cipher, *data, aes_blocks);
+ return ret;
#endif
-
- return 0;
}
diff --git a/test/lib/Makefile b/test/lib/Makefile
index f25383a40e5..721d1470185 100644
--- a/test/lib/Makefile
+++ b/test/lib/Makefile
@@ -29,6 +29,9 @@ obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
obj-$(CONFIG_UT_LIB_RSA) += rsa.o
obj-$(CONFIG_AES) += test_aes.o
+ifeq ($(CONFIG_FIT_CIPHER)$(CONFIG_DM_AES),yy)
+obj-y += test_aes_decrypt.o
+endif
obj-$(CONFIG_SHA256) += test_sha256_hmac.o
obj-$(CONFIG_HKDF_MBEDTLS) += test_sha256_hkdf.o
obj-$(CONFIG_GETOPT) += getopt.o
diff --git a/test/lib/test_aes_decrypt.c b/test/lib/test_aes_decrypt.c
new file mode 100644
index 00000000000..3b498e23e4b
--- /dev/null
+++ b/test/lib/test_aes_decrypt.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for target-side FIT AES decryption
+ *
+ * Copyright (C) 2026 James Hilliard
+ */
+
+#include <image.h>
+#include <u-boot/aes.h>
+#include <uboot_aes.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_image_aes_decrypt(struct unit_test_state *uts)
+{
+ u8 key[AES256_KEY_LENGTH] = { };
+ u8 key_exp[AES256_EXPAND_KEY_LENGTH];
+ u8 iv[AES_BLOCK_LENGTH] = { };
+ u8 plain[2 * AES_BLOCK_LENGTH];
+ u8 cipher[sizeof(plain)];
+ u8 output[sizeof(plain)];
+ struct cipher_algo algo = {
+ .name = "aes256",
+ .key_len = sizeof(key),
+ .iv_len = sizeof(iv),
+ };
+ struct image_cipher_info info = {
+ .cipher = &algo,
+ .key = key,
+ .iv = iv,
+ .size_unciphered = sizeof(plain),
+ };
+ size_t size;
+ int i, ret;
+
+ for (i = 0; i < sizeof(key); i++)
+ key[i] = i;
+ for (i = 0; i < sizeof(iv); i++)
+ iv[i] = 0x80 + i;
+ for (i = 0; i < sizeof(plain); i++)
+ plain[i] = 0x40 + i;
+
+ aes_expand_key(key, sizeof(key), key_exp);
+ aes_cbc_encrypt_blocks(sizeof(key), key_exp, iv, plain, cipher,
+ ARRAY_SIZE(cipher) / AES_BLOCK_LENGTH);
+
+ size = 0;
+ ut_assertok(image_aes_decrypt_to(&info, cipher, sizeof(cipher), output,
+ &size));
+ ut_asserteq(sizeof(plain), size);
+ ut_asserteq_mem(plain, output, sizeof(plain));
+
+ memcpy(output, cipher, sizeof(cipher));
+ size = 0;
+ ut_assertok(image_aes_decrypt_to(&info, output, sizeof(output), output,
+ &size));
+ ut_asserteq(sizeof(plain), size);
+ ut_asserteq_mem(plain, output, sizeof(plain));
+
+ size = 0x55;
+ ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher) - 1, output,
+ &size);
+ ut_asserteq(-EINVAL, ret);
+ ut_asserteq(0x55, size);
+
+ info.size_unciphered = sizeof(cipher) + 1;
+ ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size);
+ ut_asserteq(-EINVAL, ret);
+ info.size_unciphered = sizeof(plain);
+
+ info.key = NULL;
+ ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size);
+ ut_asserteq(-EINVAL, ret);
+ info.key = key;
+ info.iv = NULL;
+ ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size);
+ ut_asserteq(-EINVAL, ret);
+ info.iv = iv;
+ info.cipher = NULL;
+ ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size);
+ ut_asserteq(-EINVAL, ret);
+ ret = image_aes_decrypt_to(NULL, cipher, sizeof(cipher), output, &size);
+ ut_asserteq(-EINVAL, ret);
+
+ return 0;
+}
+
+LIB_TEST(lib_test_image_aes_decrypt, 0);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 08/14] boot: image: add FIT decrypt-to-buffer helper
2026-07-20 4:13 ` [PATCH v5 08/14] boot: image: add FIT decrypt-to-buffer helper James Hilliard
@ 2026-07-28 11:25 ` Simon Glass
0 siblings, 0 replies; 29+ messages in thread
From: Simon Glass @ 2026-07-28 11:25 UTC (permalink / raw)
To: james.hilliard1
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin, u-boot
Hi James,
On 2026-07-20T04:13:44, James Hilliard <james.hilliard1@gmail.com> wrote:
> boot: image: add FIT decrypt-to-buffer helper
>
> FIT cipher support currently allocates the output buffer inside the AES
> helper. SPL often needs to decrypt directly into a caller-selected
> buffer, for example a load buffer or a scratch buffer used before
> decompression.
>
> Add a decrypt_to callback to the FIT cipher algorithm and wire it up for
> AES. The existing allocating decrypt path becomes a wrapper around the
> new helper.
>
> Validate the FIT cipher key length, IV length and unciphered-size
> property while preparing decryption, and build lib/aes/ by phase when
> FIT_CIPHER is enabled so the target-side decrypt helper is available to
> SPL builds. Use the DM AES provider helper when enabled, retaining the
> software implementation only when no provider supports the operation.
>
> For U-Boot proper, use decrypt_to for in-place decryption when the FIT
> payload is already in writable RAM. The encrypted data is no longer
> needed after hash verification, and this avoids a full-size allocation
Decrypting in place overwrites the encrypted payload, so a second bootm on
the same FIT re-hashes decrypted bytes (fails with verification on) or
decrypts twice and boots garbage silently (with verification off). How about
addding a note, and ideally make the second attempt fail cleanly in
the unverified case? What do you think?
> [...]
>
> boot/image-cipher.c | 45 ++++++++++++++++++----
> boot/image-fit.c | 33 ++++++++++++++--
> include/image.h | 39 +++++++++++++++++--
> include/u-boot/aes.h | 27 ++++++++++----
> lib/Makefile | 2 +-
> lib/aes/aes-decrypt.c | 91 +++++++++++++++++++++++++++++++++++++--------
> test/lib/Makefile | 3 ++
> test/lib/test_aes_decrypt.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
> 8 files changed, 290 insertions(+), 39 deletions(-)
> diff --git a/lib/aes/aes-decrypt.c b/lib/aes/aes-decrypt.c
> @@ -4,37 +4,98 @@
> +#ifdef USE_HOSTCC
> + return 0;
> +#else
This host stub still claims success while leaving *data and *size
untouched, so a host caller of fit_image_decrypt_data() uses an
uninitialised pointer. Since image_aes_decrypt_to() already returns
-ENOSYS on host, please make this one match so a host tool fails
cleanly rather than silently.
In any case:
Reviewed-by: Simon Glass <sjg@chromium.org>
Regards,
Simon
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 09/14] spl: fit: support encrypted payloads
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (7 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 08/14] boot: image: add FIT decrypt-to-buffer helper James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 4:13 ` [PATCH v5 10/14] clk: sunxi: add H6/H616 CE gates and reset James Hilliard
` (4 subsequent siblings)
13 siblings, 0 replies; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
Add SPL_FIT_CIPHER and decrypt FIT image data before post-processing,
decompression or moving it to the final load address.
SPL cannot always allocate a new output buffer while loading FIT images,
so use the caller-provided decrypt-to-buffer helper. External encrypted
images are read into scratch memory first, then decrypted in place before
the existing copy or decompression path consumes them. Embedded encrypted
images decrypt into the final load buffer, or into scratch memory when
decompression is still required.
Defer mapping the final destination until the board post-processing hook
has finalized the source and length. The direct embedded-decrypt path maps
early because the hardware needs its destination, but tracks and extends
that mapping if post-processing grows the payload. Map decompression output
for CONFIG_SYS_BOOTM_LEN rather than the compressed input length.
Use IMAGE_ENABLE_DECRYPT in the common FIT image-load path so FIT cipher
support is selected by phase. Keep that path disabled for host tools,
since the target-side decrypt helper depends on the U-Boot control FDT
and runtime crypto providers.
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v3 -> v4:
- Map the final destination after board post-processing determines size
- Size decompression mappings for the maximum output
- Require SPL_OF_CONTROL and clarify the SPL_FIT_CIPHER help text
Changes v2 -> v3:
- Use a shared helper for SPL decompression buffer decisions
(suggested by Simon Glass)
- Reject encrypted SPL FIT payloads when SPL_FIT_CIPHER is disabled
(suggested by Simon Glass)
- Flatten decrypt buffer selection (suggested by Simon Glass)
- Comment the no-copy path after direct decrypt
(suggested by Simon Glass)
Changes v1 -> v2:
- Drop redundant SPL_FIT select (suggested by Simon Glass)
- Explain the IMAGE_ENABLE_DECRYPT change (suggested by Simon Glass)
- Explain the host tools decrypt behavior (suggested by Simon Glass)
- Decrypt external encrypted payloads in place
(suggested by Simon Glass)
- Skip self-memmove after direct decrypt (suggested by Simon Glass)
---
boot/Kconfig | 9 ++++++
boot/image-fit.c | 2 +-
common/spl/spl_fit.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 91 insertions(+), 9 deletions(-)
diff --git a/boot/Kconfig b/boot/Kconfig
index 8e468c56176..2e12a72a97b 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -155,6 +155,15 @@ config FIT_CIPHER
Enable the feature of data ciphering/unciphering in the tool mkimage
and in the u-boot support of the FIT image.
+config SPL_FIT_CIPHER
+ bool "Enable decrypting data in SPL FIT images"
+ depends on SPL_LOAD_FIT
+ depends on SPL_DM_AES
+ depends on SPL_OF_CONTROL
+ help
+ Enable decrypting FIT image data in SPL. This allows SPL to
+ decrypt an encrypted U-Boot proper FIT image through an AES driver.
+
config FIT_VERITY
bool "dm-verity boot parameter generation from FIT metadata"
depends on FIT && OF_LIBFDT
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 6b55316dd37..437f6ab6381 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -2306,7 +2306,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
}
/* Decrypt data before uncompress/move */
- if (IS_ENABLED(CONFIG_FIT_CIPHER) && IMAGE_ENABLE_DECRYPT) {
+ if (!tools_build() && IMAGE_ENABLE_DECRYPT) {
puts(" Decrypting Data ... ");
if (fit_image_uncipher(fit, noffset, &buf, &size)) {
puts("Error\n");
diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
index d89384449b3..961e3338d16 100644
--- a/common/spl/spl_fit.c
+++ b/common/spl/spl_fit.c
@@ -193,6 +193,34 @@ static int get_aligned_image_size(struct spl_load_info *info, int data_size,
return ALIGN(data_size, spl_get_bl_len(info));
}
+static int spl_fit_image_decrypt(const void *fit, int node, int cipher_node,
+ void **data, size_t *size, void *dst)
+{
+ size_t dst_size;
+ int ret;
+
+ puts(" Decrypting Data ... ");
+ ret = fit_image_decrypt_data_to(fit, node, cipher_node, *data, *size,
+ dst, &dst_size);
+ if (ret) {
+ puts("Error\n");
+ return ret;
+ }
+
+ *data = dst;
+ *size = dst_size;
+
+ puts("OK\n");
+
+ return 0;
+}
+
+static bool spl_image_needs_decomp(uint8_t image_comp)
+{
+ return (IS_ENABLED(CONFIG_SPL_GZIP) && image_comp == IH_COMP_GZIP) ||
+ (IS_ENABLED(CONFIG_SPL_LZMA) && image_comp == IH_COMP_LZMA);
+}
+
/**
* load_simple_fit(): load the image described in a certain FIT node
* @info: points to information about the device to load data from
@@ -218,19 +246,23 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
int len;
ulong size;
ulong load_addr;
- void *load_ptr;
+ void *load_ptr = NULL;
+ size_t load_map_len = 0;
void *src;
ulong overhead;
uint8_t image_comp = -1, type = -1;
const void *data;
const void *fit = ctx->fit;
bool external_data = false;
+ bool encrypted;
+ bool needs_decomp = false;
+ int cipher_node = -ENOENT;
+ int ret;
log_debug("starting\n");
if (CONFIG_IS_ENABLED(BOOTMETH_VBE) &&
xpl_get_phase(info) != IH_PHASE_NONE) {
enum image_phase_t phase;
- int ret;
ret = fit_image_get_phase(fit, node, &phase);
/* if the image is for any phase, let's use it */
@@ -256,6 +288,7 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
if (spl_decompression_enabled()) {
fit_image_get_comp(fit, node, &image_comp);
debug("%s ", genimg_get_comp_name(image_comp));
+ needs_decomp = spl_image_needs_decomp(image_comp);
}
if (fit_image_get_load(fit, node, &load_addr)) {
@@ -267,6 +300,14 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
load_addr = image_info->load_addr;
}
+ cipher_node = fdt_subnode_offset(fit, node, FIT_CIPHER_NODENAME);
+ if (cipher_node >= 0 && !CONFIG_IS_ENABLED(FIT_CIPHER)) {
+ printf("Can't load %s: encrypted image without SPL_FIT_CIPHER\n",
+ fit_get_name(fit, node, NULL));
+ return -ENOSYS;
+ }
+ encrypted = cipher_node >= 0;
+
if (!fit_image_get_data_position(fit, node, &offset)) {
external_data = true;
} else if (!fit_image_get_data_offset(fit, node, &offset)) {
@@ -291,11 +332,12 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
return 0;
}
- if (spl_decompression_enabled() &&
- (image_comp == IH_COMP_GZIP || image_comp == IH_COMP_LZMA))
- src_ptr = map_sysmem(ALIGN(CONFIG_SYS_LOAD_ADDR, ARCH_DMA_MINALIGN), len);
+ if (needs_decomp || encrypted)
+ src_ptr = map_sysmem(ALIGN(CONFIG_SYS_LOAD_ADDR,
+ ARCH_DMA_MINALIGN), len);
else
- src_ptr = map_sysmem(ALIGN(load_addr, ARCH_DMA_MINALIGN), len);
+ src_ptr = map_sysmem(ALIGN(load_addr, ARCH_DMA_MINALIGN),
+ len);
length = len;
overhead = get_aligned_image_overhead(info, offset);
@@ -331,10 +373,40 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
puts("OK\n");
}
+ if (encrypted) {
+ void *decrypt_ptr;
+
+ if (external_data) {
+ decrypt_ptr = src;
+ } else if (needs_decomp) {
+ decrypt_ptr = map_sysmem(ALIGN(CONFIG_SYS_LOAD_ADDR,
+ ARCH_DMA_MINALIGN),
+ length);
+ } else {
+ load_map_len = length;
+ load_ptr = map_sysmem(load_addr, load_map_len);
+ decrypt_ptr = load_ptr;
+ }
+
+ ret = spl_fit_image_decrypt(fit, node, cipher_node, &src, &length,
+ decrypt_ptr);
+ if (ret)
+ return ret;
+ }
+
if (CONFIG_IS_ENABLED(FIT_IMAGE_POST_PROCESS))
board_fit_image_post_process(fit, node, &src, &length);
- load_ptr = map_sysmem(load_addr, length);
+ size = needs_decomp ? CONFIG_SYS_BOOTM_LEN : length;
+ if (!load_ptr || size > load_map_len) {
+ void *old_load_ptr = load_ptr;
+
+ load_ptr = map_sysmem(load_addr, size);
+ load_map_len = size;
+ if (src == old_load_ptr)
+ src = load_ptr;
+ }
+
if (IS_ENABLED(CONFIG_SPL_GZIP) && image_comp == IH_COMP_GZIP) {
size = length;
if (gunzip(load_ptr, CONFIG_SYS_BOOTM_LEN, src, &size)) {
@@ -352,7 +424,8 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
return -EIO;
}
length = loadEnd - CONFIG_SYS_LOAD_ADDR;
- } else {
+ } else if (src != load_ptr) {
+ /* Direct decrypt of an embedded image can already be in place. */
memmove(load_ptr, src, length);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v5 10/14] clk: sunxi: add H6/H616 CE gates and reset
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (8 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 09/14] spl: fit: support encrypted payloads James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 4:13 ` [PATCH v5 11/14] lib: ecdsa: support additional curve sizes James Hilliard
` (3 subsequent siblings)
13 siblings, 0 replies; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
The H6 and H616 devicetrees describe the Crypto Engine with module, bus
and MBUS clocks plus a bus reset. Add the corresponding gate and reset
entries so drivers can enable the CE through the normal clock and reset
uclasses in U-Boot proper.
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v1 -> v2:
- Add Simon's Reviewed-by tag
---
drivers/clk/sunxi/clk_h6.c | 5 +++++
drivers/clk/sunxi/clk_h616.c | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c
index 81deb5728e5..27ed5705a14 100644
--- a/drivers/clk/sunxi/clk_h6.c
+++ b/drivers/clk/sunxi/clk_h6.c
@@ -20,6 +20,10 @@ static struct ccu_clk_gate h6_gates[] = {
[CLK_DE] = GATE(0x600, BIT(31)),
[CLK_BUS_DE] = GATE(0x60c, BIT(0)),
+ [CLK_CE] = GATE(0x680, BIT(31)),
+ [CLK_BUS_CE] = GATE(0x68c, BIT(0)),
+
+ [CLK_MBUS_CE] = GATE(0x804, BIT(2)),
[CLK_MBUS_NAND] = GATE(0x804, BIT(5)),
[CLK_NAND0] = GATE(0x810, BIT(31)),
@@ -77,6 +81,7 @@ static struct ccu_clk_gate h6_gates[] = {
static struct ccu_reset h6_resets[] = {
[RST_BUS_DE] = RESET(0x60c, BIT(16)),
+ [RST_BUS_CE] = RESET(0x68c, BIT(16)),
[RST_BUS_NAND] = RESET(0x82c, BIT(16)),
[RST_BUS_MMC0] = RESET(0x84c, BIT(16)),
diff --git a/drivers/clk/sunxi/clk_h616.c b/drivers/clk/sunxi/clk_h616.c
index 3e7eea25bfe..d5b659d0bb2 100644
--- a/drivers/clk/sunxi/clk_h616.c
+++ b/drivers/clk/sunxi/clk_h616.c
@@ -19,6 +19,10 @@ static struct ccu_clk_gate h616_gates[] = {
[CLK_DE] = GATE(0x600, BIT(31)),
[CLK_BUS_DE] = GATE(0x60c, BIT(0)),
+ [CLK_CE] = GATE(0x680, BIT(31)),
+ [CLK_BUS_CE] = GATE(0x68c, BIT(0)),
+
+ [CLK_MBUS_CE] = GATE(0x804, BIT(2)),
[CLK_MBUS_NAND] = GATE(0x804, BIT(5)),
[CLK_NAND0] = GATE(0x810, BIT(31)),
@@ -86,6 +90,7 @@ static struct ccu_clk_gate h616_gates[] = {
static struct ccu_reset h616_resets[] = {
[RST_BUS_DE] = RESET(0x60c, BIT(16)),
+ [RST_BUS_CE] = RESET(0x68c, BIT(16)),
[RST_BUS_NAND] = RESET(0x82c, BIT(16)),
[RST_BUS_MMC0] = RESET(0x84c, BIT(16)),
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v5 11/14] lib: ecdsa: support additional curve sizes
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (9 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 10/14] clk: sunxi: add H6/H616 CE gates and reset James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 4:13 ` [PATCH v5 12/14] crypto: allwinner: add sun8i-ce AES driver James Hilliard
` (2 subsequent siblings)
13 siblings, 0 replies; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
U-Boot's ECDSA FIT code is tied closely to prime256v1 and secp384r1.
Hardware verifiers may support a wider set of curves, and FIT public key
properties need to preserve the fixed coordinate width of those curves.
Add common curve-size handling for secp224r1, prime256v1, secp384r1 and
secp521r1. Use fixed-width big-endian byte arrays when writing libcrypto
BIGNUM values into the control FDT so non-32-bit-aligned coordinates such
as secp521r1 are encoded correctly. RSA key sizes are already multiples
of 32 bits, so their generated byte encoding is unchanged.
Register the corresponding host and target ECDSA algorithm names and
clean up the libcrypto signing context so raw signatures are owned and
freed consistently on error paths. Exercise all four curves in the host
FIT signing test.
SPL ECDSA verification uses UCLASS_ECDSA, so make SPL_ECDSA_VERIFY
depend on SPL_DM. Keep fdt_add_bignum() in the libfdt error namespace so
RSA and ECDSA callers continue to handle FDT growth and other errors
consistently.
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v3 -> v4:
- Exercise secp224r1, prime256v1, secp384r1 and secp521r1 signing
Changes v1 -> v2:
- Explain the RSA impact of fdt_add_bignum() (suggested by Simon Glass)
- Document ECDSA public key byte-array widths (suggested by Simon Glass)
- Document ecdsa_curve_size() (suggested by Simon Glass)
- Keep fdt_add_bignum() errors in libfdt space
(suggested by Simon Glass)
- Explain the SPL_DM dependency (suggested by Simon Glass)
---
doc/mkimage.1 | 3 +
doc/usage/fit/signature.rst | 9 ++-
include/u-boot/ecdsa.h | 22 +++++++
include/u-boot/fdt-libcrypto.h | 6 +-
lib/ecdsa/Kconfig | 2 +-
lib/ecdsa/ecdsa-libcrypto.c | 141 +++++++++++++++++++++++++---------------
lib/ecdsa/ecdsa-verify.c | 39 +++++------
lib/fdt-libcrypto.c | 60 ++++-------------
test/py/tests/test_fit_ecdsa.py | 18 +++--
tools/image-sig-host.c | 7 ++
10 files changed, 174 insertions(+), 133 deletions(-)
diff --git a/doc/mkimage.1 b/doc/mkimage.1
index 9a2d07cee75..b6ba88dd314 100644
--- a/doc/mkimage.1
+++ b/doc/mkimage.1
@@ -459,7 +459,10 @@ lb.
rsa2048
rsa3072
rsa4096
+ecdsa224
ecdsa256
+ecdsa384
+secp521r1
.TE
.RE
.
diff --git a/doc/usage/fit/signature.rst b/doc/usage/fit/signature.rst
index da08cc75c3a..a6aacd691d3 100644
--- a/doc/usage/fit/signature.rst
+++ b/doc/usage/fit/signature.rst
@@ -132,13 +132,16 @@ rsa,n0-inverse
For ECDSA the following are mandatory:
ecdsa,curve
- Name of ECDSA curve (e.g. "prime256v1")
+ Name of ECDSA curve, such as "secp224r1", "prime256v1",
+ "secp384r1", or "secp521r1"
ecdsa,x-point
- Public key X coordinate as a big-endian multi-word integer
+ Public key X coordinate as a fixed-width big-endian byte array. The width
+ is the curve size rounded up to whole bytes.
ecdsa,y-point
- Public key Y coordinate as a big-endian multi-word integer
+ Public key Y coordinate as a fixed-width big-endian byte array. The width
+ is the curve size rounded up to whole bytes.
These parameters can be added to a binary device tree using parameter -K of the
mkimage command::
diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h
index f0ac0f327e9..3d70b138026 100644
--- a/include/u-boot/ecdsa.h
+++ b/include/u-boot/ecdsa.h
@@ -8,6 +8,7 @@
#include <errno.h>
#include <image.h>
+#include <string.h>
/**
* crypto_algo API impementation for ECDSA;
@@ -64,8 +65,29 @@ int ecdsa_verify(struct image_sign_info *info,
uint8_t *sig, uint sig_len);
/** @} */
+#define ECDSA224_BYTES (224 / 8)
#define ECDSA256_BYTES (256 / 8)
#define ECDSA384_BYTES (384 / 8)
#define ECDSA521_BYTES ((521 + 7) / 8)
+/**
+ * ecdsa_curve_size() - Get the size in bits for a named ECDSA curve
+ *
+ * @curve_name: OpenSSL short name for the curve
+ * Return: curve size in bits, or 0 if @curve_name is not supported
+ */
+static inline unsigned int ecdsa_curve_size(const char *curve_name)
+{
+ if (!strcmp(curve_name, "secp224r1"))
+ return 224;
+ else if (!strcmp(curve_name, "prime256v1"))
+ return 256;
+ else if (!strcmp(curve_name, "secp384r1"))
+ return 384;
+ else if (!strcmp(curve_name, "secp521r1"))
+ return 521;
+
+ return 0;
+}
+
#endif
diff --git a/include/u-boot/fdt-libcrypto.h b/include/u-boot/fdt-libcrypto.h
index b15d8a1eaf4..3164f2891aa 100644
--- a/include/u-boot/fdt-libcrypto.h
+++ b/include/u-boot/fdt-libcrypto.h
@@ -12,16 +12,16 @@
/**
* fdt_add_bignum() - Write a libcrypto BIGNUM as an FDT property
*
- * Convert a libcrypto BIGNUM * into a big endian array of integers.
+ * Convert a libcrypto BIGNUM * into a fixed-width big endian byte array.
*
* @blob: FDT blob to modify
* @noffset: Offset of the FDT node
* @prop_name: What to call the property in the FDT
* @num: pointer to a libcrypto big number
* @num_bits: How big is 'num' in bits?
- * Return: 0 if all good all working, -ve on horror
+ * Return: 0 on success, negative libfdt error on failure
*/
int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
- BIGNUM *num, int num_bits);
+ const BIGNUM *num, int num_bits);
#endif /* _FDT_LIBCRYPTO_H */
diff --git a/lib/ecdsa/Kconfig b/lib/ecdsa/Kconfig
index ca13b6bfa1f..f0c56278fb6 100644
--- a/lib/ecdsa/Kconfig
+++ b/lib/ecdsa/Kconfig
@@ -17,7 +17,7 @@ config ECDSA_VERIFY
config SPL_ECDSA_VERIFY
bool "Enable ECDSA verification support in SPL"
- depends on SPL
+ depends on SPL && SPL_DM
help
Allow ECDSA signatures to be recognized and verified in SPL.
diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c
index c4bfb2cec61..378d53ea242 100644
--- a/lib/ecdsa/ecdsa-libcrypto.c
+++ b/lib/ecdsa/ecdsa-libcrypto.c
@@ -31,7 +31,7 @@ struct signer {
EVP_PKEY *evp_key; /* Pointer to EVP_PKEY object */
EC_KEY *ecdsa_key; /* Pointer to EC_KEY object */
void *hash; /* Pointer to hash used for verification */
- void *signature; /* Pointer to output signature. Do not free()!*/
+ void *signature; /* Pointer to raw signature buffer */
};
struct ecdsa_public_key {
@@ -50,11 +50,8 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
if (!key->curve_name)
return -ENOMSG;
- if (!strcmp(key->curve_name, "prime256v1"))
- key->size_bits = 256;
- else if (!strcmp(key->curve_name, "secp384r1"))
- key->size_bits = 384;
- else
+ key->size_bits = ecdsa_curve_size(key->curve_name);
+ if (!key->size_bits)
return -EINVAL;
key->x = fdt_getprop(fdt, node, "ecdsa,x-point", &x_len);
@@ -63,7 +60,8 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
if (!key->x || !key->y)
return -EINVAL;
- if (x_len != key->size_bits / 8 || y_len != key->size_bits / 8)
+ if (x_len != (key->size_bits + 7) / 8 ||
+ y_len != (key->size_bits + 7) / 8)
return -EINVAL;
return 0;
@@ -85,18 +83,12 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
return ret;
}
- if (!strcmp(pubkey.curve_name, "prime256v1")) {
- nid = NID_X9_62_prime256v1;
- } else if (!strcmp(pubkey.curve_name, "secp384r1")) {
- nid = NID_secp384r1;
- } else {
+ nid = OBJ_sn2nid(pubkey.curve_name);
+ if (nid == NID_undef) {
fprintf(stderr, "Unsupported curve name: '%s'\n", pubkey.curve_name);
return -EINVAL;
}
- fprintf(stderr, "Loading ECDSA key: curve=%s, bits=%d\n", pubkey.curve_name,
- pubkey.size_bits);
-
ec_key = EC_KEY_new_by_curve_name(nid);
if (!ec_key) {
fprintf(stderr, "Failed to allocate EC_KEY for curve %s\n", pubkey.curve_name);
@@ -111,7 +103,7 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
return -ENOMEM;
}
- len = pubkey.size_bits / 8;
+ len = (pubkey.size_bits + 7) / 8;
uint8_t buf[1 + len * 2];
@@ -133,14 +125,13 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
return -EINVAL;
}
- fprintf(stderr, "Successfully loaded ECDSA key from FDT node %d\n", node);
EC_POINT_free(point);
ctx->ecdsa_key = ec_key;
return 0;
}
-static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
+static int init_ctx(struct signer *ctx)
{
memset(ctx, 0, sizeof(*ctx));
@@ -149,11 +140,21 @@ static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
return -1;
}
+ return 0;
+}
+
+static int alloc_sig_ctx(struct signer *ctx, const struct image_sign_info *info)
+{
ctx->hash = malloc(info->checksum->checksum_len);
ctx->signature = malloc(info->crypto->key_len * 2);
- if (!ctx->hash || !ctx->signature)
+ if (!ctx->hash || !ctx->signature) {
+ free(ctx->hash);
+ free(ctx->signature);
+ ctx->hash = NULL;
+ ctx->signature = NULL;
return -ENOMEM;
+ }
return 0;
}
@@ -166,8 +167,8 @@ static void free_ctx(struct signer *ctx)
if (ctx->evp_key)
EVP_PKEY_free(ctx->evp_key);
- if (ctx->hash)
- free(ctx->hash);
+ free(ctx->hash);
+ free(ctx->signature);
}
/*
@@ -203,7 +204,12 @@ static ECDSA_SIG *ecdsa_sig_from_raw(void *buf, size_t order)
s_buf = (uintptr_t)buf + point_bytes;
r = BN_bin2bn(buf, point_bytes, NULL);
s = BN_bin2bn((void *)s_buf, point_bytes, NULL);
- ECDSA_SIG_set0(sig, r, s);
+ if (!r || !s || !ECDSA_SIG_set0(sig, r, s)) {
+ BN_free(r);
+ BN_free(s);
+ ECDSA_SIG_free(sig);
+ return NULL;
+ }
return sig;
}
@@ -271,10 +277,6 @@ static int load_key_from_fdt(struct signer *ctx, const struct image_sign_info *i
if (!fdt)
return -EINVAL;
- ret = alloc_ctx(ctx, info);
- if (ret)
- return ret;
-
sig_node = fdt_subnode_offset(fdt, 0, FIT_SIG_NODENAME);
if (sig_node < 0) {
fprintf(stderr, "No /signature node found\n");
@@ -331,7 +333,9 @@ static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
int key_len_bytes, ret;
char kname[1024];
- memset(ctx, 0, sizeof(*ctx));
+ ret = init_ctx(ctx);
+ if (ret)
+ return ret;
if (info->fdt_blob) {
return load_key_from_fdt(ctx, info);
@@ -345,10 +349,6 @@ static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
return -EINVAL;
}
- ret = alloc_ctx(ctx, info);
- if (ret)
- return ret;
-
ret = read_key(ctx, kname);
if (ret)
return ret;
@@ -368,11 +368,18 @@ static int do_sign(struct signer *ctx, struct image_sign_info *info,
{
const struct checksum_algo *algo = info->checksum;
ECDSA_SIG *sig;
+ int ret;
+
+ ret = algo->calculate(algo->name, region, region_count, ctx->hash);
+ if (ret)
+ return ret;
- algo->calculate(algo->name, region, region_count, ctx->hash);
sig = ECDSA_do_sign(ctx->hash, algo->checksum_len, ctx->ecdsa_key);
+ if (!sig)
+ return -EIO;
ecdsa_sig_encode_raw(ctx->signature, sig, info->crypto->key_len);
+ ECDSA_SIG_free(sig);
return 0;
}
@@ -389,10 +396,16 @@ static int ecdsa_check_signature(struct signer *ctx, struct image_sign_info *inf
okay = ECDSA_do_verify(ctx->hash, info->checksum->checksum_len,
sig, ctx->ecdsa_key);
if (!okay)
- fprintf(stderr, "WARNING: Signature is fake news!\n");
+ fprintf(stderr, "WARNING: ECDSA signature verification failed\n");
+ else if (okay < 0)
+ fprintf(stderr, "ERROR: ECDSA signature verification failed\n");
ECDSA_SIG_free(sig);
- return !okay;
+
+ if (okay == 1)
+ return 0;
+
+ return okay < 0 ? -EIO : -EPERM;
}
static int do_verify(struct signer *ctx, struct image_sign_info *info,
@@ -400,6 +413,7 @@ static int do_verify(struct signer *ctx, struct image_sign_info *info,
uint8_t *raw_sig, uint sig_len)
{
const struct checksum_algo *algo = info->checksum;
+ int ret;
if (sig_len != info->crypto->key_len * 2) {
fprintf(stderr, "Signature has wrong length\n");
@@ -407,7 +421,9 @@ static int do_verify(struct signer *ctx, struct image_sign_info *info,
}
memcpy(ctx->signature, raw_sig, sig_len);
- algo->calculate(algo->name, region, region_count, ctx->hash);
+ ret = algo->calculate(algo->name, region, region_count, ctx->hash);
+ if (ret)
+ return ret;
return ecdsa_check_signature(ctx, info);
}
@@ -420,11 +436,16 @@ int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
ret = prepare_ctx(&ctx, info);
if (ret >= 0) {
- do_sign(&ctx, info, region, region_count);
- *sigp = ctx.signature;
- *sig_len = info->crypto->key_len * 2;
-
- ret = ecdsa_check_signature(&ctx, info);
+ ret = alloc_sig_ctx(&ctx, info);
+ if (!ret)
+ ret = do_sign(&ctx, info, region, region_count);
+ if (!ret)
+ ret = ecdsa_check_signature(&ctx, info);
+ if (!ret) {
+ *sigp = ctx.signature;
+ *sig_len = info->crypto->key_len * 2;
+ ctx.signature = NULL;
+ }
}
free_ctx(&ctx);
@@ -439,8 +460,12 @@ int ecdsa_verify(struct image_sign_info *info,
int ret;
ret = prepare_ctx(&ctx, info);
- if (ret >= 0)
- ret = do_verify(&ctx, info, region, region_count, sig, sig_len);
+ if (ret >= 0) {
+ ret = alloc_sig_ctx(&ctx, info);
+ if (!ret)
+ ret = do_verify(&ctx, info, region, region_count,
+ sig, sig_len);
+ }
free_ctx(&ctx);
return ret;
@@ -453,7 +478,7 @@ static int do_add(struct signer *ctx, void *fdt, const char *key_node_name,
const char *curve_name;
const EC_GROUP *group;
const EC_POINT *point;
- BIGNUM *x, *y;
+ BIGNUM *x = NULL, *y = NULL;
signature_node = fdt_subnode_offset(fdt, 0, FIT_SIG_NODENAME);
if (signature_node == -FDT_ERR_NOTFOUND) {
@@ -491,42 +516,54 @@ static int do_add(struct signer *ctx, void *fdt, const char *key_node_name,
group = EC_KEY_get0_group(ctx->ecdsa_key);
key_bits = EC_GROUP_order_bits(group);
curve_name = OBJ_nid2sn(EC_GROUP_get_curve_name(group));
- /* Let 'x' and 'y' memory leak by not BN_free()'ing them. */
x = BN_new();
y = BN_new();
+ if (!x || !y) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
point = EC_KEY_get0_public_key(ctx->ecdsa_key);
- EC_POINT_get_affine_coordinates(group, point, x, y, NULL);
+ if (!EC_POINT_get_affine_coordinates(group, point, x, y, NULL)) {
+ ret = -EINVAL;
+ goto out;
+ }
ret = fdt_setprop_string(fdt, key_node, FIT_KEY_HINT,
info->keyname);
if (ret < 0)
- return ret;
+ goto out;
ret = fdt_setprop_string(fdt, key_node, "ecdsa,curve", curve_name);
if (ret < 0)
- return ret;
+ goto out;
ret = fdt_add_bignum(fdt, key_node, "ecdsa,x-point", x, key_bits);
if (ret < 0)
- return ret;
+ goto out;
ret = fdt_add_bignum(fdt, key_node, "ecdsa,y-point", y, key_bits);
if (ret < 0)
- return ret;
+ goto out;
ret = fdt_setprop_string(fdt, key_node, FIT_ALGO_PROP,
info->name);
if (ret < 0)
- return ret;
+ goto out;
if (info->require_keys) {
ret = fdt_setprop_string(fdt, key_node, FIT_KEY_REQUIRED,
info->require_keys);
if (ret < 0)
- return ret;
+ goto out;
}
- return key_node;
+ ret = key_node;
+
+out:
+ BN_free(x);
+ BN_free(y);
+ return ret;
}
int ecdsa_add_verify_data(struct image_sign_info *info, void *fdt)
diff --git a/lib/ecdsa/ecdsa-verify.c b/lib/ecdsa/ecdsa-verify.c
index 629b662cf6c..8570d35028f 100644
--- a/lib/ecdsa/ecdsa-verify.c
+++ b/lib/ecdsa/ecdsa-verify.c
@@ -12,22 +12,6 @@
#include <dm/uclass.h>
#include <u-boot/ecdsa.h>
-/*
- * Derive size of an ECDSA key from the curve name
- *
- * While it's possible to extract the key size by using string manipulation,
- * use a list of known curves for the time being.
- */
-static int ecdsa_key_size(const char *curve_name)
-{
- if (!strcmp(curve_name, "prime256v1"))
- return 256;
- else if (!strcmp(curve_name, "secp384r1"))
- return 384;
-
- return 0;
-}
-
static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
{
int x_len, y_len;
@@ -38,9 +22,9 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
return -ENOMSG;
}
- key->size_bits = ecdsa_key_size(key->curve_name);
+ key->size_bits = ecdsa_curve_size(key->curve_name);
if (key->size_bits == 0) {
- debug("Unknown ECDSA curve '%s'", key->curve_name);
+ debug("Unknown ECDSA curve '%s'\n", key->curve_name);
return -EINVAL;
}
@@ -50,9 +34,10 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
if (!key->x || !key->y)
return -EINVAL;
- if (x_len != (key->size_bits / 8) || y_len != (key->size_bits / 8)) {
- printf("%s: node=%d, curve@%p x@%p+%i y@%p+%i\n", __func__,
- node, key->curve_name, key->x, x_len, key->y, y_len);
+ if (x_len != (key->size_bits + 7) / 8 ||
+ y_len != (key->size_bits + 7) / 8) {
+ debug("%s: node=%d, curve@%p x@%p+%i y@%p+%i\n", __func__,
+ node, key->curve_name, key->x, x_len, key->y, y_len);
return -EINVAL;
}
@@ -123,6 +108,12 @@ int ecdsa_verify(struct image_sign_info *info,
return ecdsa_verify_hash(dev, info, hash, sig, sig_len);
}
+U_BOOT_CRYPTO_ALGO(ecdsa224) = {
+ .name = "ecdsa224",
+ .key_len = ECDSA224_BYTES,
+ .verify = ecdsa_verify,
+};
+
U_BOOT_CRYPTO_ALGO(ecdsa256) = {
.name = "ecdsa256",
.key_len = ECDSA256_BYTES,
@@ -135,6 +126,12 @@ U_BOOT_CRYPTO_ALGO(ecdsa384) = {
.verify = ecdsa_verify,
};
+U_BOOT_CRYPTO_ALGO(secp521r1) = {
+ .name = "secp521r1",
+ .key_len = ECDSA521_BYTES,
+ .verify = ecdsa_verify,
+};
+
/*
* uclass definition for ECDSA API
*
diff --git a/lib/fdt-libcrypto.c b/lib/fdt-libcrypto.c
index ecb0344c8f6..8c5a7282ce9 100644
--- a/lib/fdt-libcrypto.c
+++ b/lib/fdt-libcrypto.c
@@ -5,68 +5,34 @@
*/
#include <libfdt.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <u-boot/fdt-libcrypto.h>
int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
- BIGNUM *num, int num_bits)
+ const BIGNUM *num, int num_bits)
{
- int nwords = num_bits / 32;
- int size;
- uint32_t *buf, *ptr;
- BIGNUM *tmp, *big2, *big32, *big2_32;
- BN_CTX *ctx;
+ int size = (num_bits + 7) / 8;
+ unsigned char *buf;
int ret;
- tmp = BN_new();
- big2 = BN_new();
- big32 = BN_new();
- big2_32 = BN_new();
+ if (size <= 0)
+ return -FDT_ERR_BADVALUE;
- /*
- * Note: This code assumes that all of the above succeed, or all fail.
- * In practice memory allocations generally do not fail (unless the
- * process is killed), so it does not seem worth handling each of these
- * as a separate case. Technicaly this could leak memory on failure,
- * but a) it won't happen in practice, and b) it doesn't matter as we
- * will immediately exit with a failure code.
- */
- if (!tmp || !big2 || !big32 || !big2_32) {
- fprintf(stderr, "Out of memory (bignum)\n");
- return -ENOMEM;
- }
- ctx = BN_CTX_new();
- if (!ctx) {
- fprintf(stderr, "Out of memory (bignum context)\n");
- return -ENOMEM;
- }
- BN_set_word(big2, 2L);
- BN_set_word(big32, 32L);
- BN_exp(big2_32, big2, big32, ctx); /* B = 2^32 */
-
- size = nwords * sizeof(uint32_t);
buf = malloc(size);
if (!buf) {
fprintf(stderr, "Out of memory (%d bytes)\n", size);
- return -ENOMEM;
+ return -FDT_ERR_NOSPACE;
}
- /* Write out modulus as big endian array of integers */
- for (ptr = buf + nwords - 1; ptr >= buf; ptr--) {
- BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */
- *ptr = cpu_to_fdt32(BN_get_word(tmp));
- BN_rshift(num, num, 32); /* N = N/B */
+ if (BN_bn2binpad(num, buf, size) != size) {
+ free(buf);
+ return -FDT_ERR_BADVALUE;
}
- /*
- * We try signing with successively increasing size values, so this
- * might fail several times
- */
+ /* Callers may retry with a larger FDT if the property does not fit. */
ret = fdt_setprop(blob, noffset, prop_name, buf, size);
free(buf);
- BN_free(tmp);
- BN_free(big2);
- BN_free(big32);
- BN_free(big2_32);
- return ret ? -FDT_ERR_NOSPACE : 0;
+ return ret;
}
diff --git a/test/py/tests/test_fit_ecdsa.py b/test/py/tests/test_fit_ecdsa.py
index 3e816d68eb6..ae26169b9bd 100644
--- a/test/py/tests/test_fit_ecdsa.py
+++ b/test/py/tests/test_fit_ecdsa.py
@@ -50,9 +50,9 @@ class SignableFitImage(object):
return self.signable_nodes
- def change_signature_algo_to_ecdsa(self):
+ def change_signature_algo_to_ecdsa(self, algo):
for image in self.signable_nodes:
- self.__fdt_set(f'{image}/signature', algo='sha256,ecdsa256')
+ self.__fdt_set(f'{image}/signature', algo=f'sha256,{algo}')
def sign(self, mkimage, key_file):
utils.run_and_log(self.ubman, [mkimage, '-F', self.fit, f'-G{key_file}'])
@@ -71,10 +71,16 @@ class SignableFitImage(object):
@pytest.mark.requiredtool('dtc')
@pytest.mark.requiredtool('fdtget')
@pytest.mark.requiredtool('fdtput')
-def test_fit_ecdsa(ubman):
+@pytest.mark.parametrize('curve, algo', (
+ ('secp224r1', 'ecdsa224'),
+ ('prime256v1', 'ecdsa256'),
+ ('secp384r1', 'ecdsa384'),
+ ('secp521r1', 'secp521r1'),
+))
+def test_fit_ecdsa(ubman, curve, algo):
""" Test that signatures generated by mkimage are legible. """
def generate_ecdsa_key():
- return ECC.generate(curve='prime256v1')
+ return ECC.generate(curve=curve)
def assemble_fit_image(dest_fit, its, destdir):
dtc_args = f'-I dts -O dtb -i {destdir}'
@@ -86,7 +92,7 @@ def test_fit_ecdsa(ubman):
mkimage = ubman.config.build_dir + '/tools/mkimage'
datadir = ubman.config.source_dir + '/test/py/tests/vboot/'
- tempdir = os.path.join(ubman.config.result_dir, 'ecdsa')
+ tempdir = os.path.join(ubman.config.result_dir, f'ecdsa-{curve}')
os.makedirs(tempdir, exist_ok=True)
key_file = f'{tempdir}/ecdsa-test-key.pem'
fit_file = f'{tempdir}/test.fit'
@@ -109,6 +115,6 @@ def test_fit_ecdsa(ubman):
if len(nodes) == 0:
raise ValueError('FIT image has no "/image" nodes with "signature"')
- fit.change_signature_algo_to_ecdsa()
+ fit.change_signature_algo_to_ecdsa(algo)
fit.sign(mkimage, key_file)
fit.check_signatures(key)
diff --git a/tools/image-sig-host.c b/tools/image-sig-host.c
index 5285263c616..aeca83bd440 100644
--- a/tools/image-sig-host.c
+++ b/tools/image-sig-host.c
@@ -69,6 +69,13 @@ struct crypto_algo crypto_algos[] = {
.add_verify_data = rsa_add_verify_data,
.verify = rsa_verify,
},
+ {
+ .name = "ecdsa224",
+ .key_len = ECDSA224_BYTES,
+ .sign = ecdsa_sign,
+ .add_verify_data = ecdsa_add_verify_data,
+ .verify = ecdsa_verify,
+ },
{
.name = "ecdsa256",
.key_len = ECDSA256_BYTES,
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v5 12/14] crypto: allwinner: add sun8i-ce AES driver
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (10 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 11/14] lib: ecdsa: support additional curve sizes James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-20 4:13 ` [PATCH v5 13/14] crypto: allwinner: add sun8i-ce ECDSA verifier James Hilliard
2026-07-20 4:13 ` [PATCH v5 14/14] crypto: allwinner: add sun8i-ce hash driver James Hilliard
13 siblings, 0 replies; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
Add an Allwinner sun8i Crypto Engine driver using the same directory and
driver naming style as the Linux sun8i-ce driver. The parent device owns
the shared CE registers, clocks and resets. It also provides an exclusive
task-session API which centralizes descriptor submission, per-channel
in-flight accounting, completion, error handling and reset on abort. The
AES child exposes the standard UCLASS_AES interface.
Support AES-128, AES-192 and AES-256 in ECB and CBC modes with
software-provided keys. Use one bounded scheduler for both one-engine and
two-engine operations by selecting the lane count and deriving serial or
parallel behavior from the task controls. Each lane owns two fixed banks
of ten descriptors, so memory use is independent of payload size.
For word-aligned ECB and CBC decryption requests of at least 256 KiB, run
the AES and RAES engines in parallel in SPL and U-Boot proper. Each lane
has two banks and one active-bank index. A bank's descriptor count records
whether it is prepared. Process each completion snapshot as one epoch:
retire all completed channels, submit each prepared peer bank, release
the completed mappings, then refill free banks from a shared cursor. This
keeps both engines fed without assuming a completion order. Keep a rolling
next-IV value and snapshot one IV for each queued task before DMA can
overwrite in-place ciphertext.
The H616 descriptor format uses word addresses. Map each word-aligned
bank independently, stage only its partial cacheline edges in private
aligned buffers, and map the cacheline-aligned middle directly. Build
each task's source and destination lists from those logical segments.
This isolates neighboring cachelines while preparation of the peer bank
overlaps DMA on the active bank, retaining aligned throughput for
cacheline-misaligned payloads. Support exact in-place operation, preserve
dirty bytes outside out-of-place DMA ranges, reject partial overlaps, and
use a fixed 64 KiB repack buffer only for byte addresses the descriptor
format cannot represent. Keep one CE session for the complete request,
including every bounded repack chunk.
Require DMA output mappings to cover complete cachelines so invalidation
cannot discard adjacent dirty data. Retire a channel only after its
completion and that channel's DMA current source and destination
registers are idle. Validate mapped buffers and descriptor chains before
encoding their hardware addresses, and assert the hardware descriptor
layout at build time. Use one cleanup path for normal and forced session
teardown. Track per-channel deadlines independently so activity on one
engine cannot extend another engine's timeout.
Model the H6 and H616 compatibles after the Linux sun8i-ce variant data.
Both variants use byte-sized cipher task lengths and provide the second
RAES engine; H616 additionally needs word-addressed descriptors. Program
the H616 module clock divider for 300 MHz from PLL_PERI0(2X). Select the
required SPL crypto, clock and reset support from SPL_SUNXI_CE, retain the
CE and clock-provider nodes in the pre-RAM device tree, and use the same
bulk clock/reset lifecycle in SPL and U-Boot proper.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v4 -> v5:
- Make the blocking chain helper close-on-error contract explicit and
avoid a redundant close in the one-shot wrapper
- Store the module-clock register value in variant data and share one
H6/H616 clock-programming path
- Reorder fixed bank bookkeeping before aligned DMA storage to remove
avoidable alignment holes
- Replace channel and engine claims with one exclusive parent session
tracking in-flight channels, independent deadlines and abort cleanup
- Move channel, method, tail interrupt and next-pointer setup into shared
submission, validate descriptor DMA ranges and wait for DMA idle
- Use the normal bulk clock and reset lifecycle in SPL and U-Boot proper,
select its SPL dependencies and retain the required pre-RAM DT nodes
- Replace the separate one-shot, bounce and dual-CBC paths with one
bounded one/two-lane scheduler, extending dual-engine scheduling to
word-aligned ECB requests of at least 256 KiB
- Drive each lane with two fixed banks of ten tasks, use fair refill from
a shared cursor, and submit prepared peers before CPU cache work
- Keep one session and rolling CBC IV across the complete operation,
with one compact IV snapshot for each queued task
- Direct-map word-aligned cacheline-offset buffers using private head and
tail cachelines plus SG middles, including exact in-place operation
- Use a fixed 64 KiB repack for byte-unaligned buffers, reject partial
overlap and validate length and address overflow
- Map only the configured key bytes and require output DMA mappings to
own complete cachelines
- Assert the hardware descriptor layout and reset active hardware before
releasing mappings after an error
Changes v3 -> v4:
- Drop invariant variant fields and redundant chain mapping state
- Add per-channel task sessions and engine ownership
- Use AES and RAES in parallel for CBC decrypts of at least 256 KiB
in SPL and U-Boot proper
- Double-buffer ten-descriptor chains with tail-only completion
- Support exact in-place decrypt and aligned-offset input buffers
- Map scheduler indices directly to AES/RAES channels and engine bits
- Drop redundant internal bounds checks and session initialization
- Reuse the scheduled CE register-wait helper and return status
separately
Changes v2 -> v3:
- Run the H616 CE module clock at 300 MHz
Changes v1 -> v2:
- Expose slot 0 as the software-provided AES key slot
---
MAINTAINERS | 1 +
arch/arm/dts/sunxi-u-boot.dtsi | 15 +
drivers/crypto/Kconfig | 2 +
drivers/crypto/Makefile | 1 +
drivers/crypto/allwinner/Kconfig | 3 +
drivers/crypto/allwinner/Makefile | 3 +
drivers/crypto/allwinner/sun8i-ce/Kconfig | 41 ++
drivers/crypto/allwinner/sun8i-ce/Makefile | 4 +
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-aes.c | 809 ++++++++++++++++++++++
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 779 +++++++++++++++++++++
drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 120 ++++
11 files changed, 1778 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e5b2a2e373c..9d823387be5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -752,6 +752,7 @@ F: arch/arm/include/asm/arch-sunxi/
F: arch/arm/mach-sunxi/
F: board/sunxi/
F: drivers/clk/sunxi/
+F: drivers/crypto/allwinner/
F: drivers/phy/allwinner/
F: drivers/pinctrl/sunxi/
F: drivers/video/sunxi/
diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
index e1a9a7f5d4c..c1dc7404bf8 100644
--- a/arch/arm/dts/sunxi-u-boot.dtsi
+++ b/arch/arm/dts/sunxi-u-boot.dtsi
@@ -19,6 +19,21 @@
};
};
+#ifdef CONFIG_SPL_CLK
+&ccu {
+ bootph-pre-ram;
+};
+
+&rtc {
+ bootph-pre-ram;
+};
+#endif
+
+#ifdef CONFIG_SPL_SUNXI_CE
+&crypto {
+ bootph-pre-ram;
+};
+#endif
/* Let U-Boot be the firmware layer that controls the watchdog. */
#ifdef CONFIG_MACH_SUN8I_R528
&wdt {
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 0d58e3910fe..d15a59f87ee 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -6,6 +6,8 @@ source "drivers/crypto/aes/Kconfig"
source "drivers/crypto/fsl/Kconfig"
+source "drivers/crypto/allwinner/Kconfig"
+
source "drivers/crypto/aspeed/Kconfig"
source "drivers/crypto/nuvoton/Kconfig"
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index e4a4482b7f3..cbbd5dc1dbe 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -8,6 +8,7 @@ obj-y += aes/
obj-y += rsa_mod_exp/
obj-y += fsl/
obj-y += hash/
+obj-y += allwinner/
obj-y += aspeed/
obj-y += nuvoton/
obj-y += tegra/
diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig
new file mode 100644
index 00000000000..9765b089e25
--- /dev/null
+++ b/drivers/crypto/allwinner/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+source "drivers/crypto/allwinner/sun8i-ce/Kconfig"
diff --git a/drivers/crypto/allwinner/Makefile b/drivers/crypto/allwinner/Makefile
new file mode 100644
index 00000000000..2dcae98bac9
--- /dev/null
+++ b/drivers/crypto/allwinner/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-y += sun8i-ce/
diff --git a/drivers/crypto/allwinner/sun8i-ce/Kconfig b/drivers/crypto/allwinner/sun8i-ce/Kconfig
new file mode 100644
index 00000000000..973c4f3af21
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/Kconfig
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+config SUNXI_CE
+ bool
+ depends on ARCH_SUNXI
+ depends on CLK && DM_RESET
+
+config SPL_SUNXI_CE
+ bool
+ depends on ARCH_SUNXI
+ depends on MACH_SUN50I_H6 || MACH_SUN50I_H616
+ depends on SPL_DM
+ depends on SPL_OF_CONTROL
+ select SPL_CLK
+ select SPL_CRYPTO
+ select SPL_DM_RESET
+
+config SUNXI_CE_AES
+ bool "Allwinner sunxi CE AES"
+ depends on ARCH_SUNXI
+ depends on DM_AES
+ depends on CLK && DM_RESET
+ select AES
+ select SUNXI_CE
+ help
+ Select this option to enable AES encryption and decryption using
+ the Crypto Engine found in Allwinner sunxi SoCs. The driver
+ supports software-provided AES-128, AES-192 and AES-256 keys.
+
+config SPL_SUNXI_CE_AES
+ bool "Allwinner sunxi CE AES in SPL"
+ depends on ARCH_SUNXI
+ depends on MACH_SUN50I_H6 || MACH_SUN50I_H616
+ depends on SPL_DM_AES
+ depends on SPL_OF_CONTROL
+ select SPL_CRYPTO
+ select SPL_SUNXI_CE
+ help
+ Select this option to enable AES decryption in SPL using the Crypto
+ Engine found in Allwinner H6 and H616 compatible SoCs. This can be
+ used to decrypt FIT images before loading U-Boot proper.
diff --git a/drivers/crypto/allwinner/sun8i-ce/Makefile b/drivers/crypto/allwinner/sun8i-ce/Makefile
new file mode 100644
index 00000000000..2a8778065b1
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_$(PHASE_)SUNXI_CE) += sun8i-ce-core.o
+obj-$(CONFIG_$(PHASE_)SUNXI_CE_AES) += sun8i-ce-aes.o
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-aes.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-aes.c
new file mode 100644
index 00000000000..8dde4863642
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-aes.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 James Hilliard
+ */
+
+#define LOG_CATEGORY UCLASS_AES
+
+#include <dm.h>
+#include <limits.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <u-boot/schedule.h>
+#include <uboot_aes.h>
+#include <asm/cache.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "sun8i-ce.h"
+
+#define SUNXI_CE_ENCRYPTION 0
+#define SUNXI_CE_DECRYPTION BIT(8)
+
+#define SUNXI_CE_OP_ECB 0
+#define SUNXI_CE_OP_CBC BIT(8)
+
+#define SUNXI_CE_AES_KEY_128BIT 0
+#define SUNXI_CE_AES_KEY_192BIT 1
+#define SUNXI_CE_AES_KEY_256BIT 2
+
+#define SUNXI_CE_AES_TASK_SIZE (128 * 1024)
+#define SUNXI_CE_AES_TASK_BLOCKS \
+ (SUNXI_CE_AES_TASK_SIZE / AES_BLOCK_LENGTH)
+#define SUNXI_CE_AES_CHAIN_DEPTH 10
+#define SUNXI_CE_AES_MAX_LANES 2
+#define SUNXI_CE_AES_BANK_COUNT 2
+#define SUNXI_CE_AES_DUAL_MIN_BLOCKS \
+ (SUNXI_CE_AES_MAX_LANES * SUNXI_CE_AES_TASK_BLOCKS)
+#define SUNXI_CE_AES_REPACK_SIZE (64 * 1024)
+#define SUNXI_CE_AES_REPACK_BLOCKS \
+ (SUNXI_CE_AES_REPACK_SIZE / AES_BLOCK_LENGTH)
+#define SUNXI_CE_AES_NO_BANK SUNXI_CE_AES_BANK_COUNT
+
+static_assert(SUNXI_CE_AES_BANK_COUNT == 2);
+
+enum sunxi_aes_segment_type {
+ SUNXI_AES_SEGMENT_HEAD,
+ SUNXI_AES_SEGMENT_MIDDLE,
+ SUNXI_AES_SEGMENT_TAIL,
+ SUNXI_AES_SEGMENT_COUNT,
+};
+
+struct sunxi_aes_priv {
+ u8 key[AES256_KEY_LENGTH] __aligned(sizeof(u32));
+ u8 key_len;
+ u8 ce_key_size;
+};
+
+struct sunxi_aes_segment {
+ dma_addr_t dma;
+ u32 len;
+};
+
+struct sunxi_aes_bank_map {
+ u8 src_edges[2][ARCH_DMA_MINALIGN] __aligned(ARCH_DMA_MINALIGN);
+ u8 dst_edges[2][ARCH_DMA_MINALIGN] __aligned(ARCH_DMA_MINALIGN);
+ struct sunxi_ce_dma_buf src_edges_dma;
+ struct sunxi_ce_dma_buf src_middle_dma;
+ struct sunxi_ce_dma_buf dst_edges_dma;
+ struct sunxi_ce_dma_buf dst_middle_dma;
+ struct sunxi_aes_segment src_segments[SUNXI_AES_SEGMENT_COUNT];
+ struct sunxi_aes_segment dst_segments[SUNXI_AES_SEGMENT_COUNT];
+ u32 dst_head;
+ u32 dst_tail;
+};
+
+struct sunxi_aes_bank {
+ struct sunxi_ce_task tasks[SUNXI_CE_AES_CHAIN_DEPTH]
+ __aligned(ARCH_DMA_MINALIGN);
+ size_t offset;
+ u32 len;
+ /* Zero means free; otherwise every DMA mapping remains owned here. */
+ u32 task_count;
+ u8 ivs[SUNXI_CE_AES_CHAIN_DEPTH][AES_BLOCK_LENGTH]
+ __aligned(ARCH_DMA_MINALIGN);
+ struct sunxi_ce_dma_buf iv_dma;
+ struct sunxi_aes_bank_map data;
+};
+
+struct sunxi_aes_lane {
+ struct sunxi_aes_bank banks[SUNXI_CE_AES_BANK_COUNT];
+ /* Submitted bank, or SUNXI_CE_AES_NO_BANK while the lane is idle. */
+ u8 active_bank;
+};
+
+struct sunxi_aes_xfer {
+ struct sunxi_ce_session session;
+ dma_addr_t key_dma;
+ u8 *src;
+ u8 *dst;
+ size_t cursor;
+ u32 remaining;
+ u32 comm_ctl;
+ u32 sym_ctl;
+ u8 lane_count;
+ u8 next_iv[AES_BLOCK_LENGTH];
+ struct sunxi_aes_lane lanes[SUNXI_CE_AES_MAX_LANES];
+};
+
+static const u8 sunxi_aes_methods[SUNXI_CE_AES_MAX_LANES] = {
+ SUNXI_CE_METHOD_AES,
+ SUNXI_CE_METHOD_RAES,
+};
+
+static bool sunxi_aes_is_cbc(const struct sunxi_aes_xfer *xfer)
+{
+ return xfer->sym_ctl & SUNXI_CE_OP_CBC;
+}
+
+static bool sunxi_aes_is_decrypt(const struct sunxi_aes_xfer *xfer)
+{
+ return xfer->comm_ctl & SUNXI_CE_DECRYPTION;
+}
+
+static bool sunxi_aes_is_serial(const struct sunxi_aes_xfer *xfer)
+{
+ return sunxi_aes_is_cbc(xfer) && !sunxi_aes_is_decrypt(xfer);
+}
+
+static u32 sunxi_aes_other_bank(u32 bank_index)
+{
+ return bank_index ^ 1;
+}
+
+static int sunxi_ce_key_size(u32 key_bits)
+{
+ switch (key_bits) {
+ case AES128_KEY_LENGTH * 8:
+ return SUNXI_CE_AES_KEY_128BIT;
+ case AES192_KEY_LENGTH * 8:
+ return SUNXI_CE_AES_KEY_192BIT;
+ case AES256_KEY_LENGTH * 8:
+ return SUNXI_CE_AES_KEY_256BIT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static bool sunxi_aes_ranges_overlap(const u8 *src, const u8 *dst, size_t len)
+{
+ uintptr_t src_start = (uintptr_t)src;
+ uintptr_t dst_start = (uintptr_t)dst;
+
+ if (src_start < dst_start)
+ return dst_start - src_start < len;
+
+ return src_start - dst_start < len;
+}
+
+static u8 sunxi_aes_lane_count(struct sunxi_ce_priv *ce, bool cbc,
+ bool decrypt, u32 num_blocks)
+{
+ if (ce->variant->aes_engine_count > 1 && (!cbc || decrypt) &&
+ num_blocks >= SUNXI_CE_AES_DUAL_MIN_BLOCKS)
+ return SUNXI_CE_AES_MAX_LANES;
+
+ return 1;
+}
+
+static void sunxi_aes_edge_lengths(const u8 *buf, u32 len, u32 *head,
+ u32 *middle, u32 *tail)
+{
+ *head = (ARCH_DMA_MINALIGN -
+ ((uintptr_t)buf & (ARCH_DMA_MINALIGN - 1))) &
+ (ARCH_DMA_MINALIGN - 1);
+ *head = min(*head, len);
+ *middle = ALIGN_DOWN(len - *head, ARCH_DMA_MINALIGN);
+ *tail = len - *head - *middle;
+}
+
+static void sunxi_aes_release_bank_data(struct sunxi_aes_bank_map *map,
+ u8 *dst, u32 len,
+ bool commit_output)
+{
+ sunxi_ce_dma_unmap(&map->dst_middle_dma);
+ sunxi_ce_dma_unmap(&map->dst_edges_dma);
+ sunxi_ce_dma_unmap(&map->src_middle_dma);
+ sunxi_ce_dma_unmap(&map->src_edges_dma);
+
+ if (commit_output) {
+ if (map->dst_head)
+ memcpy(dst, map->dst_edges[0], map->dst_head);
+ if (map->dst_tail)
+ memcpy(dst + len - map->dst_tail,
+ map->dst_edges[1], map->dst_tail);
+ }
+
+ memset(map, 0, sizeof(*map));
+}
+
+static int sunxi_aes_map_bank_data(struct sunxi_ce_priv *ce,
+ struct sunxi_aes_bank_map *map,
+ u8 *src, u8 *dst, u32 len)
+{
+ u32 src_head, src_middle, src_tail;
+ u32 dst_head, dst_middle, dst_tail;
+ enum dma_data_direction src_dir;
+ bool in_place = src == dst;
+ int ret;
+
+ if (!IS_ALIGNED((uintptr_t)src, sizeof(u32)) ||
+ !IS_ALIGNED((uintptr_t)dst, sizeof(u32)) ||
+ !IS_ALIGNED(len, sizeof(u32)))
+ return -EINVAL;
+
+ memset(map, 0, sizeof(*map));
+ src_dir = in_place ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
+
+ /* Independently refilled banks must not share DMA cache envelopes. */
+ sunxi_aes_edge_lengths(src, len, &src_head, &src_middle, &src_tail);
+ sunxi_aes_edge_lengths(dst, len, &dst_head, &dst_middle, &dst_tail);
+ map->dst_head = dst_head;
+ map->dst_tail = dst_tail;
+
+ if (src_head)
+ memcpy(map->src_edges[0], src, src_head);
+ if (src_tail)
+ memcpy(map->src_edges[1], src + len - src_tail, src_tail);
+ if (src_head || src_tail) {
+ ret = sunxi_ce_dma_map(ce, &map->src_edges_dma, map->src_edges,
+ sizeof(map->src_edges), DMA_TO_DEVICE);
+ if (ret)
+ return ret;
+ }
+
+ if (src_middle) {
+ ret = sunxi_ce_dma_map(ce, &map->src_middle_dma, src + src_head,
+ src_middle, src_dir);
+ if (ret)
+ return ret;
+ }
+
+ if (dst_head || dst_tail) {
+ ret = sunxi_ce_dma_map(ce, &map->dst_edges_dma, map->dst_edges,
+ sizeof(map->dst_edges), DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
+ }
+
+ if (!in_place && dst_middle) {
+ ret = sunxi_ce_dma_map(ce, &map->dst_middle_dma, dst + dst_head,
+ dst_middle, DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
+ }
+
+ map->src_segments[SUNXI_AES_SEGMENT_HEAD].dma = map->src_edges_dma.dma;
+ map->src_segments[SUNXI_AES_SEGMENT_HEAD].len = src_head;
+ map->src_segments[SUNXI_AES_SEGMENT_MIDDLE].dma = map->src_middle_dma.dma;
+ map->src_segments[SUNXI_AES_SEGMENT_MIDDLE].len = src_middle;
+ map->src_segments[SUNXI_AES_SEGMENT_TAIL].dma =
+ map->src_edges_dma.dma + ARCH_DMA_MINALIGN;
+ map->src_segments[SUNXI_AES_SEGMENT_TAIL].len = src_tail;
+
+ map->dst_segments[SUNXI_AES_SEGMENT_HEAD].dma = map->dst_edges_dma.dma;
+ map->dst_segments[SUNXI_AES_SEGMENT_HEAD].len = dst_head;
+ map->dst_segments[SUNXI_AES_SEGMENT_MIDDLE].dma =
+ in_place ? map->src_middle_dma.dma : map->dst_middle_dma.dma;
+ map->dst_segments[SUNXI_AES_SEGMENT_MIDDLE].len = dst_middle;
+ map->dst_segments[SUNXI_AES_SEGMENT_TAIL].dma =
+ map->dst_edges_dma.dma + ARCH_DMA_MINALIGN;
+ map->dst_segments[SUNXI_AES_SEGMENT_TAIL].len = dst_tail;
+
+ return 0;
+}
+
+static int sunxi_aes_fill_sg(struct sunxi_ce_priv *ce,
+ struct sunxi_ce_sginfo *sg,
+ const struct sunxi_aes_segment *segments,
+ u32 offset, u32 len)
+{
+ u32 end = offset + len;
+ u32 covered = 0;
+ u32 segment_offset = 0;
+ u8 i, sg_count = 0;
+
+ for (i = 0; i < SUNXI_AES_SEGMENT_COUNT; i++) {
+ u32 segment_end = segment_offset + segments[i].len;
+ u32 start = max(offset, segment_offset);
+ u32 stop = min(end, segment_end);
+ u32 part_len;
+ dma_addr_t dma;
+
+ if (start >= stop) {
+ segment_offset = segment_end;
+ continue;
+ }
+ if (sg_count >= SUNXI_CE_MAX_SG)
+ return -EINVAL;
+
+ part_len = stop - start;
+ if (!IS_ALIGNED(part_len, sizeof(u32)))
+ return -EINVAL;
+ dma = segments[i].dma + start - segment_offset;
+ sg[sg_count].addr = sunxi_ce_desc_dma_addr(ce, dma);
+ sg[sg_count].len = part_len / sizeof(u32);
+ covered += part_len;
+ sg_count++;
+ segment_offset = segment_end;
+ }
+
+ return covered == len ? 0 : -EINVAL;
+}
+
+static void sunxi_aes_init_task(struct sunxi_ce_priv *ce,
+ struct sunxi_ce_task *task,
+ dma_addr_t key, dma_addr_t iv,
+ u32 len, u32 comm_ctl, u32 sym_ctl)
+{
+ memset(task, 0, sizeof(*task));
+
+ task->t_common_ctl = comm_ctl;
+ task->t_sym_ctl = sym_ctl;
+ task->t_key = sunxi_ce_desc_dma_addr(ce, key);
+ if (iv)
+ task->t_iv = sunxi_ce_desc_dma_addr(ce, iv);
+ task->t_dlen = len;
+}
+
+static u32 sunxi_aes_fair_blocks(struct sunxi_aes_xfer *xfer,
+ u32 lanes_left)
+{
+ if (!xfer->remaining)
+ return 0;
+
+ return 1 + (xfer->remaining - 1) / lanes_left;
+}
+
+static void sunxi_aes_release_bank(struct sunxi_aes_xfer *xfer,
+ struct sunxi_aes_bank *bank,
+ bool commit_output)
+{
+ u8 *dst = xfer->dst + bank->offset;
+ u32 len = bank->len;
+
+ sunxi_aes_release_bank_data(&bank->data, dst, len, commit_output);
+ sunxi_ce_dma_unmap(&bank->iv_dma);
+
+ if (commit_output && sunxi_aes_is_serial(xfer))
+ memcpy(xfer->next_iv, dst + len - AES_BLOCK_LENGTH,
+ AES_BLOCK_LENGTH);
+
+ bank->offset = 0;
+ bank->len = 0;
+ bank->task_count = 0;
+}
+
+static void sunxi_aes_release_all_banks(struct sunxi_aes_xfer *xfer)
+{
+ u32 bank_index, lane_index;
+
+ for (lane_index = 0; lane_index < xfer->lane_count; lane_index++) {
+ struct sunxi_aes_lane *lane = &xfer->lanes[lane_index];
+
+ for (bank_index = 0; bank_index < SUNXI_CE_AES_BANK_COUNT;
+ bank_index++)
+ sunxi_aes_release_bank(xfer, &lane->banks[bank_index],
+ false);
+ lane->active_bank = SUNXI_CE_AES_NO_BANK;
+ }
+}
+
+static int sunxi_aes_prepare_bank(struct sunxi_aes_xfer *xfer, u32 lane_index,
+ u32 bank_index, u32 max_blocks)
+{
+ struct sunxi_aes_lane *lane = &xfer->lanes[lane_index];
+ struct sunxi_aes_bank *bank = &lane->banks[bank_index];
+ struct sunxi_ce_priv *ce = xfer->session.ce;
+ u32 chain_depth = sunxi_aes_is_serial(xfer) ? 1 :
+ SUNXI_CE_AES_CHAIN_DEPTH;
+ u32 blocks, offset = 0;
+ u32 task_index;
+ int ret;
+
+ if (!xfer->remaining)
+ return 0;
+ if (lane->active_bank == bank_index || bank->task_count)
+ return -EBUSY;
+
+ blocks = min(xfer->remaining, max_blocks);
+ blocks = min_t(u32, blocks,
+ chain_depth * SUNXI_CE_AES_TASK_BLOCKS);
+ bank->offset = xfer->cursor;
+ bank->len = blocks * AES_BLOCK_LENGTH;
+ bank->task_count = DIV_ROUND_UP(blocks, SUNXI_CE_AES_TASK_BLOCKS);
+
+ if (sunxi_aes_is_cbc(xfer)) {
+ for (task_index = 0; task_index < bank->task_count; task_index++) {
+ u32 task_len = min_t(u32, bank->len - offset,
+ SUNXI_CE_AES_TASK_SIZE);
+
+ /* Snapshot before an in-place task can overwrite ciphertext. */
+ memcpy(bank->ivs[task_index], xfer->next_iv,
+ AES_BLOCK_LENGTH);
+ if (sunxi_aes_is_decrypt(xfer))
+ memcpy(xfer->next_iv,
+ xfer->src + bank->offset + offset + task_len -
+ AES_BLOCK_LENGTH, AES_BLOCK_LENGTH);
+ offset += task_len;
+ }
+
+ ret = sunxi_ce_dma_map(ce, &bank->iv_dma, bank->ivs,
+ bank->task_count * AES_BLOCK_LENGTH,
+ DMA_TO_DEVICE);
+ if (ret)
+ goto out_release;
+ }
+
+ ret = sunxi_aes_map_bank_data(ce, &bank->data,
+ xfer->src + bank->offset,
+ xfer->dst + bank->offset, bank->len);
+ if (ret)
+ goto out_release;
+
+ for (task_index = 0, offset = 0; task_index < bank->task_count;
+ task_index++) {
+ struct sunxi_ce_task *task = &bank->tasks[task_index];
+ u32 task_len = min_t(u32, bank->len - offset,
+ SUNXI_CE_AES_TASK_SIZE);
+ dma_addr_t iv = sunxi_aes_is_cbc(xfer) ? bank->iv_dma.dma +
+ task_index * AES_BLOCK_LENGTH : 0;
+
+ sunxi_aes_init_task(ce, task, xfer->key_dma, iv,
+ task_len, xfer->comm_ctl, xfer->sym_ctl);
+ ret = sunxi_aes_fill_sg(ce, task->t_src,
+ bank->data.src_segments,
+ offset, task_len);
+ if (ret)
+ goto out_release;
+ ret = sunxi_aes_fill_sg(ce, task->t_dst,
+ bank->data.dst_segments,
+ offset, task_len);
+ if (ret)
+ goto out_release;
+
+ offset += task_len;
+ }
+ xfer->cursor += bank->len;
+ xfer->remaining -= blocks;
+
+ return 0;
+
+out_release:
+ sunxi_aes_release_bank(xfer, bank, false);
+
+ return ret;
+}
+
+static int sunxi_aes_submit_bank(struct sunxi_aes_xfer *xfer, u32 lane_index,
+ u32 bank_index)
+{
+ struct sunxi_aes_lane *lane = &xfer->lanes[lane_index];
+ struct sunxi_aes_bank *bank = &lane->banks[bank_index];
+ int ret;
+
+ if (!bank->task_count)
+ return 0;
+ if (lane->active_bank != SUNXI_CE_AES_NO_BANK)
+ return -EINVAL;
+
+ ret = sunxi_ce_session_submit_chain(&xfer->session, lane_index,
+ sunxi_aes_methods[lane_index],
+ bank->tasks, bank->task_count);
+ if (!ret)
+ lane->active_bank = bank_index;
+
+ return ret;
+}
+
+static int sunxi_aes_service(struct sunxi_aes_xfer *xfer, u32 completed_mask)
+{
+ u8 completed_banks[SUNXI_CE_AES_MAX_LANES] = {
+ SUNXI_CE_AES_NO_BANK,
+ SUNXI_CE_AES_NO_BANK,
+ };
+ u32 lane_index, refills_left = 0;
+ int ret;
+
+ /* Snapshot completions before changing or releasing any bank state. */
+ for (lane_index = 0; lane_index < xfer->lane_count; lane_index++) {
+ struct sunxi_aes_lane *lane = &xfer->lanes[lane_index];
+
+ if (!(completed_mask & SUNXI_CE_CHAN_MASK(lane_index)))
+ continue;
+ if (lane->active_bank == SUNXI_CE_AES_NO_BANK)
+ return -EINVAL;
+
+ completed_banks[lane_index] = lane->active_bank;
+ lane->active_bank = SUNXI_CE_AES_NO_BANK;
+ refills_left++;
+ }
+
+ /* Submit every prepared peer before doing CPU-side continuation work. */
+ for (lane_index = 0; lane_index < xfer->lane_count; lane_index++) {
+ u32 peer_bank;
+
+ if (completed_banks[lane_index] == SUNXI_CE_AES_NO_BANK)
+ continue;
+ peer_bank = sunxi_aes_other_bank(completed_banks[lane_index]);
+ ret = sunxi_aes_submit_bank(xfer, lane_index, peer_bank);
+ if (ret)
+ return ret;
+ }
+
+ /* Retire completed mappings and commit their edge cache lines. */
+ for (lane_index = 0; lane_index < xfer->lane_count; lane_index++) {
+ struct sunxi_aes_bank *bank;
+
+ if (completed_banks[lane_index] == SUNXI_CE_AES_NO_BANK)
+ continue;
+ bank = &xfer->lanes[lane_index].banks[completed_banks[lane_index]];
+ sunxi_aes_release_bank(xfer, bank, true);
+ }
+
+ /* Refill each newly free bank, then submit it if its lane is idle. */
+ for (lane_index = 0; lane_index < xfer->lane_count; lane_index++) {
+ struct sunxi_aes_lane *lane = &xfer->lanes[lane_index];
+ u32 completed_bank = completed_banks[lane_index];
+
+ if (completed_bank == SUNXI_CE_AES_NO_BANK)
+ continue;
+ if (xfer->remaining) {
+ u32 max_blocks = sunxi_aes_fair_blocks(xfer,
+ refills_left);
+
+ ret = sunxi_aes_prepare_bank(xfer, lane_index,
+ completed_bank,
+ max_blocks);
+ if (ret)
+ return ret;
+ }
+ refills_left--;
+ if (lane->active_bank == SUNXI_CE_AES_NO_BANK) {
+ ret = sunxi_aes_submit_bank(xfer, lane_index,
+ completed_bank);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sunxi_aes_run_scheduler(struct sunxi_aes_xfer *xfer, u8 *src,
+ u8 *dst, u32 num_blocks)
+{
+ u32 bank_index, lane_index;
+ int completed, ret;
+
+ xfer->src = src;
+ xfer->dst = dst;
+ xfer->cursor = 0;
+ xfer->remaining = num_blocks;
+ for (lane_index = 0; lane_index < xfer->lane_count; lane_index++)
+ xfer->lanes[lane_index].active_bank = SUNXI_CE_AES_NO_BANK;
+
+ /*
+ * Prime bank 0 and submit it, then prepare bank 1 as its peer:
+ *
+ * active_bank --> submitted chain
+ * other bank --> prepared chain, or free when task_count is zero
+ */
+ for (bank_index = 0;
+ bank_index < (sunxi_aes_is_serial(xfer) ? 1 :
+ SUNXI_CE_AES_BANK_COUNT);
+ bank_index++) {
+ for (lane_index = 0; lane_index < xfer->lane_count; lane_index++) {
+ u32 lanes_left = xfer->lane_count - lane_index;
+ u32 max_blocks;
+
+ if (!xfer->remaining)
+ break;
+ max_blocks = sunxi_aes_fair_blocks(xfer, lanes_left);
+ ret = sunxi_aes_prepare_bank(xfer, lane_index, bank_index,
+ max_blocks);
+ if (ret)
+ return ret;
+ if (!bank_index) {
+ ret = sunxi_aes_submit_bank(xfer, lane_index,
+ bank_index);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ while (sunxi_ce_session_busy(&xfer->session)) {
+ completed = sunxi_ce_session_poll(&xfer->session);
+ if (completed < 0)
+ return completed;
+ if (!completed) {
+ schedule();
+ continue;
+ }
+
+ ret = sunxi_aes_service(xfer, completed);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sunxi_aes_run(struct udevice *dev, u8 *iv, u8 *src, u8 *dst,
+ u32 num_blocks, u32 aes_mode, bool decrypt)
+{
+ struct sunxi_aes_priv *priv = dev_get_priv(dev);
+ struct sunxi_ce_priv *ce = dev_get_priv(dev_get_parent(dev));
+ struct sunxi_ce_dma_buf key_dma = { };
+ struct sunxi_aes_xfer *xfer;
+ u8 *repack = NULL;
+ u8 lane_count;
+ u32 comm_ctl, sym_ctl;
+ bool cbc = aes_mode == SUNXI_CE_OP_CBC;
+ bool word_addressable;
+ size_t total_len;
+ int ret;
+
+ if (!priv->key_len)
+ return -EINVAL;
+ if (!num_blocks)
+ return 0;
+ if (!src || !dst)
+ return -EINVAL;
+ if (cbc && !iv)
+ return -EINVAL;
+
+ if (num_blocks > SIZE_MAX / AES_BLOCK_LENGTH)
+ return -EOVERFLOW;
+ total_len = (size_t)num_blocks * AES_BLOCK_LENGTH;
+ if (total_len > UINTPTR_MAX - (uintptr_t)src ||
+ total_len > UINTPTR_MAX - (uintptr_t)dst)
+ return -EOVERFLOW;
+ if (src != dst && sunxi_aes_ranges_overlap(src, dst, total_len))
+ return -EINVAL;
+
+ xfer = malloc_cache_aligned(sizeof(*xfer));
+ if (!xfer)
+ return -ENOMEM;
+ memset(xfer, 0, sizeof(*xfer));
+ if (cbc)
+ memcpy(xfer->next_iv, iv, AES_BLOCK_LENGTH);
+
+ ret = sunxi_ce_dma_map(ce, &key_dma, priv->key, priv->key_len,
+ DMA_TO_DEVICE);
+ if (ret)
+ goto out_free;
+
+ comm_ctl = decrypt ? SUNXI_CE_DECRYPTION : SUNXI_CE_ENCRYPTION;
+ sym_ctl = priv->ce_key_size | aes_mode;
+ word_addressable = IS_ALIGNED((uintptr_t)src, sizeof(u32)) &&
+ IS_ALIGNED((uintptr_t)dst, sizeof(u32));
+ lane_count = word_addressable ?
+ sunxi_aes_lane_count(ce, cbc, decrypt, num_blocks) : 1;
+ xfer->key_dma = key_dma.dma;
+ xfer->comm_ctl = comm_ctl;
+ xfer->sym_ctl = sym_ctl;
+ xfer->lane_count = lane_count;
+
+ if (!word_addressable) {
+ repack = memalign(ARCH_DMA_MINALIGN, SUNXI_CE_AES_REPACK_SIZE);
+ if (!repack) {
+ ret = -ENOMEM;
+ goto out_unmap_key;
+ }
+ }
+
+ ret = sunxi_ce_session_begin(ce, GENMASK(lane_count - 1, 0),
+ &xfer->session);
+ if (ret)
+ goto out_unmap_key;
+
+ if (word_addressable) {
+ ret = sunxi_aes_run_scheduler(xfer, src, dst, num_blocks);
+ goto out_close;
+ }
+
+ while (num_blocks) {
+ u32 blocks = min_t(u32, num_blocks,
+ SUNXI_CE_AES_REPACK_BLOCKS);
+ u32 len = blocks * AES_BLOCK_LENGTH;
+
+ memcpy(repack, src, len);
+ ret = sunxi_aes_run_scheduler(xfer, repack, repack, blocks);
+ if (ret)
+ goto out_close;
+ memcpy(dst, repack, len);
+
+ num_blocks -= blocks;
+ src += len;
+ dst += len;
+ }
+
+ ret = 0;
+
+out_close:
+ /* Stop every lane before releasing any DMA-owned memory. */
+ ret = sunxi_ce_session_close(&xfer->session, ret);
+ sunxi_aes_release_all_banks(xfer);
+out_unmap_key:
+ sunxi_ce_dma_unmap(&key_dma);
+out_free:
+ free(repack);
+ free(xfer);
+
+ return ret;
+}
+
+static int sunxi_aes_available_key_slots(struct udevice *dev)
+{
+ return 1;
+}
+
+static int sunxi_aes_get_software_key_slot(struct udevice *dev)
+{
+ return 0;
+}
+
+static int sunxi_aes_select_key_slot(struct udevice *dev, u32 key_size,
+ u8 slot)
+{
+ struct sunxi_aes_priv *priv = dev_get_priv(dev);
+ int ce_key_size;
+
+ if (slot)
+ return -EINVAL;
+
+ ce_key_size = sunxi_ce_key_size(key_size);
+ if (ce_key_size < 0)
+ return ce_key_size;
+
+ priv->key_len = key_size / 8;
+ priv->ce_key_size = ce_key_size;
+
+ return 0;
+}
+
+static int sunxi_aes_set_key_for_key_slot(struct udevice *dev, u32 key_size,
+ u8 *key, u8 slot)
+{
+ struct sunxi_aes_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ if (!key)
+ return -EINVAL;
+
+ ret = sunxi_aes_select_key_slot(dev, key_size, slot);
+ if (ret)
+ return ret;
+
+ memcpy(priv->key, key, key_size / 8);
+
+ return 0;
+}
+
+static int sunxi_aes_ecb_encrypt(struct udevice *dev, u8 *src, u8 *dst,
+ u32 num_blocks)
+{
+ return sunxi_aes_run(dev, NULL, src, dst, num_blocks,
+ SUNXI_CE_OP_ECB, false);
+}
+
+static int sunxi_aes_ecb_decrypt(struct udevice *dev, u8 *src, u8 *dst,
+ u32 num_blocks)
+{
+ return sunxi_aes_run(dev, NULL, src, dst, num_blocks,
+ SUNXI_CE_OP_ECB, true);
+}
+
+static int sunxi_aes_cbc_encrypt(struct udevice *dev, u8 *iv, u8 *src,
+ u8 *dst, u32 num_blocks)
+{
+ return sunxi_aes_run(dev, iv, src, dst, num_blocks,
+ SUNXI_CE_OP_CBC, false);
+}
+
+static int sunxi_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src,
+ u8 *dst, u32 num_blocks)
+{
+ return sunxi_aes_run(dev, iv, src, dst, num_blocks,
+ SUNXI_CE_OP_CBC, true);
+}
+
+static const struct aes_ops sunxi_aes_ops = {
+ .available_key_slots = sunxi_aes_available_key_slots,
+ .get_software_key_slot = sunxi_aes_get_software_key_slot,
+ .select_key_slot = sunxi_aes_select_key_slot,
+ .set_key_for_key_slot = sunxi_aes_set_key_for_key_slot,
+ .aes_ecb_encrypt = sunxi_aes_ecb_encrypt,
+ .aes_ecb_decrypt = sunxi_aes_ecb_decrypt,
+ .aes_cbc_encrypt = sunxi_aes_cbc_encrypt,
+ .aes_cbc_decrypt = sunxi_aes_cbc_decrypt,
+};
+
+U_BOOT_DRIVER(sun8i_ce_aes) = {
+ .name = "sun8i-ce-aes",
+ .id = UCLASS_AES,
+ .ops = &sunxi_aes_ops,
+ .priv_auto = sizeof(struct sunxi_aes_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
new file mode 100644
index 00000000000..f22d534caea
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
@@ -0,0 +1,779 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 James Hilliard
+ */
+
+#include <cpu_func.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <errno.h>
+#include <time.h>
+#include <u-boot/schedule.h>
+#include <vsprintf.h>
+#include <asm/arch/cpu.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include "sun8i-ce.h"
+
+#define SUNXI_CE_TDQ 0x00
+#define SUNXI_CE_ICR 0x08
+#define SUNXI_CE_ISR 0x0c
+#define SUNXI_CE_TLR 0x10
+#define SUNXI_CE_ESR 0x18
+#define SUNXI_CE_SCSA 0x24
+#define SUNXI_CE_SCDA 0x28
+#define SUNXI_CE_HCSA 0x34
+#define SUNXI_CE_HCDA 0x38
+#define SUNXI_CE_ACSA 0x44
+#define SUNXI_CE_ACDA 0x48
+#define SUNXI_CE_XCSA 0x54
+#define SUNXI_CE_XCDA 0x58
+
+#define SUNXI_CE_ERR_ALGO_NOTSUP BIT(0)
+#define SUNXI_CE_ERR_DATALEN BIT(1)
+#define SUNXI_CE_ERR_KEYSRAM BIT(2)
+#define SUNXI_CE_ERR_ADDR_INVALID BIT(5)
+#define SUNXI_CE_ERR_KEYLADDER BIT(6)
+#define SUNXI_CE_TASK_START BIT(0)
+#define SUNXI_CE_METHOD_MASK GENMASK(6, 0)
+#define SUNXI_CE_TLR_METHOD_SHIFT 8
+#define SUNXI_CE_WORD_SHIFT 2
+#define SUNXI_CE_TIMEOUT_US 3000000
+
+#define SUN50I_H6_CCU_CE_CLK 0x680
+#define SUN50I_H6_CCU_CE_CLK_SRC_MASK BIT(24)
+#define SUN50I_H6_CCU_CE_CLK_N_MASK GENMASK(9, 8)
+#define SUN50I_H6_CCU_CE_CLK_M_MASK GENMASK(3, 0)
+#define SUN50I_H6_CCU_CE_CLK_GATE BIT(31)
+#define SUN50I_H6_CCU_CE_CLK_MASK (SUN50I_H6_CCU_CE_CLK_GATE | \
+ SUN50I_H6_CCU_CE_CLK_SRC_MASK | \
+ SUN50I_H6_CCU_CE_CLK_N_MASK | \
+ SUN50I_H6_CCU_CE_CLK_M_MASK)
+/* PLL_PERI0(2X) / 4 = 300 MHz */
+#define SUN50I_H616_CCU_CE_CLK_M 3
+
+static int sunxi_ce_reset(struct sunxi_ce_priv *priv);
+
+struct sunxi_ce_channel_route {
+ u16 src_reg;
+ u16 dst_reg;
+};
+
+static const struct sunxi_ce_channel_route
+sunxi_ce_channel_routes[SUNXI_CE_MAX_CHANS] = {
+ [SUNXI_CE_CHANNEL_AES] = {
+ .src_reg = SUNXI_CE_SCSA,
+ .dst_reg = SUNXI_CE_SCDA,
+ },
+ [SUNXI_CE_CHANNEL_RAES] = {
+ .src_reg = SUNXI_CE_XCSA,
+ .dst_reg = SUNXI_CE_XCDA,
+ },
+ [SUNXI_CE_CHANNEL_HASH] = {
+ .src_reg = SUNXI_CE_HCSA,
+ .dst_reg = SUNXI_CE_HCDA,
+ },
+ [SUNXI_CE_CHANNEL_ASYM] = {
+ .src_reg = SUNXI_CE_ACSA,
+ .dst_reg = SUNXI_CE_ACDA,
+ },
+};
+
+u32 sunxi_ce_desc_dma_addr(struct sunxi_ce_priv *priv, dma_addr_t addr)
+{
+ if (priv->variant->needs_word_addresses)
+ addr >>= SUNXI_CE_WORD_SHIFT;
+
+ return (u32)addr;
+}
+
+static void sunxi_ce_flush(void *buf, size_t len)
+{
+ ulong start = ALIGN_DOWN((ulong)buf, ARCH_DMA_MINALIGN);
+ ulong end = ALIGN((ulong)buf + len, ARCH_DMA_MINALIGN);
+
+ flush_dcache_range(start, end);
+}
+
+static int sunxi_ce_validate_dma_range(struct sunxi_ce_priv *priv,
+ dma_addr_t addr, size_t len)
+{
+ dma_addr_t last;
+
+ if (!IS_ALIGNED(addr, sizeof(u32)))
+ return -EINVAL;
+ if (!len)
+ return 0;
+ if (len - 1 > (dma_addr_t)-1 - addr)
+ return -EOVERFLOW;
+
+ last = addr + len - 1;
+ if (priv->variant->needs_word_addresses)
+ last >>= SUNXI_CE_WORD_SHIFT;
+
+ return last > U32_MAX ? -ERANGE : 0;
+}
+
+int sunxi_ce_dma_map(struct sunxi_ce_priv *priv,
+ struct sunxi_ce_dma_buf *map, void *buf, size_t len,
+ enum dma_data_direction dir)
+{
+ ulong addr, map_start, map_end;
+ dma_addr_t base;
+ int ret;
+
+ if (!priv || !map || (!buf && len) || map->map_len)
+ return -EINVAL;
+ if (dir != DMA_TO_DEVICE && dir != DMA_FROM_DEVICE &&
+ dir != DMA_BIDIRECTIONAL)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ if (dir != DMA_TO_DEVICE &&
+ (!IS_ALIGNED((ulong)buf, ARCH_DMA_MINALIGN) ||
+ !IS_ALIGNED(len, ARCH_DMA_MINALIGN)))
+ return -EINVAL;
+
+ addr = (ulong)buf;
+ if (len > ULONG_MAX - addr)
+ return -EOVERFLOW;
+ if (addr + len > ULONG_MAX - (ARCH_DMA_MINALIGN - 1))
+ return -EOVERFLOW;
+ map_start = ALIGN_DOWN(addr, ARCH_DMA_MINALIGN);
+ map_end = ALIGN(addr + len, ARCH_DMA_MINALIGN);
+
+ base = dma_map_single((void *)map_start, map_end - map_start, dir);
+ if (dma_mapping_error(NULL, base))
+ return -EIO;
+
+ map->base = base;
+ map->dma = base + addr - map_start;
+ map->map_len = map_end - map_start;
+ map->dir = dir;
+ ret = sunxi_ce_validate_dma_range(priv, map->dma, len);
+ if (ret) {
+ sunxi_ce_dma_unmap(map);
+ return ret;
+ }
+
+ return 0;
+}
+
+void sunxi_ce_dma_unmap(struct sunxi_ce_dma_buf *map)
+{
+ if (!map || !map->map_len)
+ return;
+
+ dma_unmap_single(map->base, map->map_len, map->dir);
+ memset(map, 0, sizeof(*map));
+}
+
+static void sunxi_ce_print_error(u32 err)
+{
+ printf("CE ERROR: %#x\n", err);
+ if (err & SUNXI_CE_ERR_ALGO_NOTSUP)
+ printf("CE ERROR: algorithm not supported\n");
+ if (err & SUNXI_CE_ERR_DATALEN)
+ printf("CE ERROR: data length error\n");
+ if (err & SUNXI_CE_ERR_KEYSRAM)
+ printf("CE ERROR: keysram access error for AES\n");
+ if (err & SUNXI_CE_ERR_ADDR_INVALID)
+ printf("CE ERROR: address invalid\n");
+ if (err & SUNXI_CE_ERR_KEYLADDER)
+ printf("CE ERROR: key ladder configuration error\n");
+}
+
+static int sunxi_ce_wait(void __iomem *addr, u32 mask, u32 expect)
+{
+ unsigned long timeout = timer_get_us() + SUNXI_CE_TIMEOUT_US;
+ u32 val;
+
+ do {
+ val = readl(addr);
+ if ((val & mask) == expect)
+ return 0;
+ schedule();
+ } while (!time_after(timer_get_us(), timeout));
+
+ val = readl(addr);
+ if ((val & mask) == expect)
+ return 0;
+
+ return -ETIMEDOUT;
+}
+
+static u32 sunxi_ce_error_mask(u32 channel_mask)
+{
+ u32 error_mask = 0;
+ u32 chan;
+
+ for (chan = 0; chan < SUNXI_CE_MAX_CHANS; chan++) {
+ if (channel_mask & SUNXI_CE_CHAN_MASK(chan))
+ error_mask |= SUNXI_CE_CHAN_ERR_MASK(chan);
+ }
+
+ return error_mask;
+}
+
+static int sunxi_ce_prepare_channels(struct sunxi_ce_priv *priv,
+ u32 channel_mask)
+{
+ u32 error_mask, val;
+ int ret;
+
+ error_mask = sunxi_ce_error_mask(channel_mask);
+
+ val = readl(priv->base + SUNXI_CE_ICR);
+ writel(val | channel_mask, priv->base + SUNXI_CE_ICR);
+ writel(channel_mask, priv->base + SUNXI_CE_ISR);
+ writel(error_mask, priv->base + SUNXI_CE_ESR);
+ ret = sunxi_ce_wait(priv->base + SUNXI_CE_ISR, channel_mask, 0);
+ if (ret) {
+ printf("%s: timeout waiting for stale interrupt\n", __func__);
+ clrbits_le32(priv->base + SUNXI_CE_ICR, channel_mask);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sunxi_ce_submit_task(struct sunxi_ce_priv *priv,
+ dma_addr_t task_dma, u32 method)
+{
+ u32 load = (method << SUNXI_CE_TLR_METHOD_SHIFT) |
+ SUNXI_CE_TASK_START;
+ int ret;
+
+ ret = sunxi_ce_wait(priv->base + SUNXI_CE_TLR,
+ SUNXI_CE_TASK_START, 0);
+ if (ret) {
+ printf("%s: timeout waiting for task launcher\n", __func__);
+ return ret;
+ }
+
+ writel(sunxi_ce_desc_dma_addr(priv, task_dma),
+ priv->base + SUNXI_CE_TDQ);
+ /* Be sure all data is written before enabling the task. */
+ wmb();
+ writel(load, priv->base + SUNXI_CE_TLR);
+ ret = sunxi_ce_wait(priv->base + SUNXI_CE_TLR,
+ SUNXI_CE_TASK_START, 0);
+ if (ret)
+ printf("%s: timeout registering task\n", __func__);
+
+ return ret;
+}
+
+static int sunxi_ce_ack_channel(struct sunxi_ce_priv *priv, u32 chan)
+{
+ u32 channel_mask, error_mask, err;
+
+ channel_mask = SUNXI_CE_CHAN_MASK(chan);
+ writel(channel_mask, priv->base + SUNXI_CE_ISR);
+ error_mask = SUNXI_CE_CHAN_ERR_MASK(chan);
+ err = readl(priv->base + SUNXI_CE_ESR) & error_mask;
+ writel(error_mask, priv->base + SUNXI_CE_ESR);
+ if (err) {
+ sunxi_ce_print_error(err >> (chan * 8));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sunxi_ce_wait_channel_idle(struct sunxi_ce_priv *priv, u32 chan)
+{
+ const struct sunxi_ce_channel_route *route;
+ int ret;
+
+ if (chan >= SUNXI_CE_MAX_CHANS)
+ return -EINVAL;
+ route = &sunxi_ce_channel_routes[chan];
+
+ ret = sunxi_ce_wait(priv->base + route->src_reg, ~0U, 0);
+ if (!ret)
+ ret = sunxi_ce_wait(priv->base + route->dst_reg, ~0U, 0);
+ if (ret)
+ printf("%s: timeout waiting for channel %u DMA idle\n",
+ __func__, chan);
+
+ return ret;
+}
+
+static int sunxi_ce_wait_channels_idle(struct sunxi_ce_priv *priv,
+ u32 channel_mask)
+{
+ u32 chan;
+ int cleanup_ret, ret = 0;
+
+ for (chan = 0; chan < SUNXI_CE_MAX_CHANS; chan++) {
+ if (!(channel_mask & SUNXI_CE_CHAN_MASK(chan)))
+ continue;
+
+ cleanup_ret = sunxi_ce_wait_channel_idle(priv, chan);
+ if (cleanup_ret && !ret)
+ ret = cleanup_ret;
+ }
+
+ return ret;
+}
+
+static int sunxi_ce_clear_errors(struct sunxi_ce_priv *priv, u32 channel_mask)
+{
+ u32 error_mask, err;
+
+ error_mask = sunxi_ce_error_mask(channel_mask);
+ err = readl(priv->base + SUNXI_CE_ESR) & error_mask;
+ writel(error_mask, priv->base + SUNXI_CE_ESR);
+
+ if (err) {
+ u32 chan;
+
+ for (chan = 0; chan < SUNXI_CE_MAX_CHANS; chan++) {
+ u32 chan_err = (err >> (chan * 8)) & 0xff;
+
+ if (chan_err)
+ sunxi_ce_print_error(chan_err);
+ }
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sunxi_ce_session_check(struct sunxi_ce_session *session)
+{
+ if (!session || !session->ce)
+ return -EINVAL;
+ if (session->ce->active_session != session)
+ return -ECANCELED;
+
+ return 0;
+}
+
+int sunxi_ce_session_begin(struct sunxi_ce_priv *priv, u32 channel_mask,
+ struct sunxi_ce_session *session)
+{
+ u32 valid_channels = GENMASK(SUNXI_CE_MAX_CHANS - 1, 0);
+ int ret;
+
+ if (!priv || !session || !channel_mask ||
+ channel_mask & ~valid_channels)
+ return -EINVAL;
+ if (priv->active_session)
+ return -EBUSY;
+
+ memset(session, 0, sizeof(*session));
+ session->ce = priv;
+ session->channel_mask = channel_mask;
+ priv->active_session = session;
+
+ ret = sunxi_ce_prepare_channels(priv, channel_mask);
+ if (ret) {
+ priv->active_session = NULL;
+ session->ce = NULL;
+ }
+
+ return ret;
+}
+
+int sunxi_ce_session_submit_chain(struct sunxi_ce_session *session,
+ u32 channel, u32 method,
+ struct sunxi_ce_task *tasks, u32 task_count)
+{
+ dma_addr_t tasks_dma;
+ size_t tasks_len;
+ u32 channel_mask;
+ u32 i;
+ int ret;
+
+ ret = sunxi_ce_session_check(session);
+ if (ret)
+ return ret;
+ if (channel >= SUNXI_CE_MAX_CHANS || method & ~SUNXI_CE_METHOD_MASK)
+ return -EINVAL;
+ channel_mask = SUNXI_CE_CHAN_MASK(channel);
+ if (!(session->channel_mask & channel_mask))
+ return -EINVAL;
+ if (session->inflight_mask & channel_mask)
+ return -EBUSY;
+ if (!tasks || !task_count)
+ return -EINVAL;
+ if (task_count > SIZE_MAX / sizeof(*tasks))
+ return -EOVERFLOW;
+ tasks_len = task_count * sizeof(*tasks);
+ tasks_dma = virt_to_phys(tasks);
+ ret = sunxi_ce_validate_dma_range(session->ce, tasks_dma, tasks_len);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < task_count; i++) {
+ tasks[i].t_id = channel;
+ tasks[i].t_common_ctl &= ~(SUNXI_CE_METHOD_MASK |
+ SUNXI_CE_COMM_INT);
+ tasks[i].t_common_ctl |= method;
+ if (i + 1 == task_count)
+ tasks[i].t_common_ctl |= SUNXI_CE_COMM_INT;
+ tasks[i].next = i + 1 < task_count ?
+ sunxi_ce_desc_dma_addr(session->ce,
+ tasks_dma +
+ (i + 1) * sizeof(*tasks)) : 0;
+ }
+
+ sunxi_ce_flush(tasks, tasks_len);
+
+ ret = sunxi_ce_submit_task(session->ce, tasks_dma, method);
+ if (!ret) {
+ session->inflight_mask |= channel_mask;
+ session->deadline[channel] = timer_get_us() + SUNXI_CE_TIMEOUT_US;
+ }
+
+ return ret;
+}
+
+int sunxi_ce_session_poll(struct sunxi_ce_session *session)
+{
+ unsigned long now;
+ u32 channel_mask, completed, pending;
+ unsigned int chan;
+ int ret;
+
+ ret = sunxi_ce_session_check(session);
+ if (ret)
+ return ret;
+
+ completed = readl(session->ce->base + SUNXI_CE_ISR) &
+ session->inflight_mask;
+ pending = completed;
+ while (pending) {
+ chan = __ffs(pending);
+ channel_mask = SUNXI_CE_CHAN_MASK(chan);
+ pending &= ~channel_mask;
+
+ ret = sunxi_ce_ack_channel(session->ce, chan);
+ if (ret)
+ return ret;
+ ret = sunxi_ce_wait_channel_idle(session->ce, chan);
+ if (ret)
+ return ret;
+ session->inflight_mask &= ~channel_mask;
+ session->deadline[chan] = 0;
+ }
+
+ if (completed) {
+ /* A chain-tail completion orders every preceding output DMA. */
+ rmb();
+ }
+
+ now = timer_get_us();
+ pending = session->inflight_mask;
+ while (pending) {
+ chan = __ffs(pending);
+ channel_mask = SUNXI_CE_CHAN_MASK(chan);
+ pending &= ~channel_mask;
+ if (!time_after(now, session->deadline[chan]))
+ continue;
+
+ /* Do not report a timeout for a completion racing this snapshot. */
+ if (readl(session->ce->base + SUNXI_CE_ISR) & channel_mask)
+ continue;
+
+ printf("%s: DMA timeout on channel %u\n", __func__, chan);
+ return -ETIMEDOUT;
+ }
+
+ return completed;
+}
+
+static int sunxi_ce_session_wait(struct sunxi_ce_session *session)
+{
+ int completed, ret;
+
+ ret = sunxi_ce_session_check(session);
+ if (ret)
+ return ret;
+
+ while (session->inflight_mask) {
+ completed = sunxi_ce_session_poll(session);
+ if (completed < 0)
+ return completed;
+ if (!completed)
+ schedule();
+ }
+
+ return 0;
+}
+
+int sunxi_ce_session_run_chain_or_close(struct sunxi_ce_session *session,
+ u32 channel, u32 method,
+ struct sunxi_ce_task *tasks,
+ u32 task_count)
+{
+ int ret;
+
+ ret = sunxi_ce_session_submit_chain(session, channel, method, tasks,
+ task_count);
+ if (!ret)
+ ret = sunxi_ce_session_wait(session);
+ if (ret)
+ return sunxi_ce_session_close(session, ret);
+
+ return 0;
+}
+
+static void sunxi_ce_session_release(struct sunxi_ce_session *session)
+{
+ struct sunxi_ce_priv *priv = session->ce;
+
+ priv->active_session = NULL;
+ memset(session, 0, sizeof(*session));
+}
+
+static int sunxi_ce_session_finish(struct sunxi_ce_session *session, bool abort)
+{
+ struct sunxi_ce_priv *priv;
+ u32 channel_mask, valid_channels;
+ int cleanup_ret, idle_ret, ret;
+
+ ret = sunxi_ce_session_check(session);
+ if (ret)
+ return ret;
+
+ priv = session->ce;
+ channel_mask = session->channel_mask;
+ valid_channels = GENMASK(SUNXI_CE_MAX_CHANS - 1, 0);
+ ret = 0;
+
+ if (!abort && session->inflight_mask) {
+ ret = -EBUSY;
+ abort = true;
+ }
+
+ if (!abort) {
+ clrbits_le32(priv->base + SUNXI_CE_ICR, channel_mask);
+ writel(channel_mask, priv->base + SUNXI_CE_ISR);
+ ret = sunxi_ce_clear_errors(priv, channel_mask);
+
+ writel(0, priv->base + SUNXI_CE_TLR);
+ writel(0, priv->base + SUNXI_CE_TDQ);
+ cleanup_ret = sunxi_ce_wait(priv->base + SUNXI_CE_TLR,
+ SUNXI_CE_TASK_START, 0);
+ readl(priv->base + SUNXI_CE_TDQ);
+ if (cleanup_ret) {
+ printf("%s: timeout clearing task launcher\n", __func__);
+ if (!ret)
+ ret = cleanup_ret;
+ abort = true;
+ }
+ }
+
+ if (abort) {
+ cleanup_ret = sunxi_ce_reset(priv);
+ if (cleanup_ret) {
+ idle_ret = sunxi_ce_wait_channels_idle(priv, channel_mask);
+ if (idle_ret)
+ panic_str("CE: failed to stop DMA");
+ if (!ret)
+ ret = cleanup_ret;
+ }
+
+ /* Reset or observed idle DMA makes releasing mapped memory safe. */
+ rmb();
+ clrbits_le32(priv->base + SUNXI_CE_ICR, valid_channels);
+ writel(valid_channels, priv->base + SUNXI_CE_ISR);
+ writel(sunxi_ce_error_mask(valid_channels),
+ priv->base + SUNXI_CE_ESR);
+ writel(0, priv->base + SUNXI_CE_TLR);
+ writel(0, priv->base + SUNXI_CE_TDQ);
+ }
+
+ sunxi_ce_session_release(session);
+
+ return ret;
+}
+
+int sunxi_ce_session_close(struct sunxi_ce_session *session, int status)
+{
+ int cleanup_ret;
+
+ if (!session || !session->ce)
+ return status ? status : -EINVAL;
+
+ cleanup_ret = sunxi_ce_session_finish(session, status != 0);
+
+ return status ? status : cleanup_ret;
+}
+
+int sunxi_ce_run_task(struct sunxi_ce_priv *priv, u32 channel, u32 method,
+ struct sunxi_ce_task *task)
+{
+ struct sunxi_ce_session session;
+ u32 channel_mask;
+ int ret;
+
+ if (channel >= SUNXI_CE_MAX_CHANS)
+ return -EINVAL;
+ channel_mask = SUNXI_CE_CHAN_MASK(channel);
+
+ ret = sunxi_ce_session_begin(priv, channel_mask, &session);
+ if (ret)
+ return ret;
+
+ ret = sunxi_ce_session_run_chain_or_close(&session, channel, method,
+ task, 1);
+ if (ret)
+ return ret;
+
+ return sunxi_ce_session_close(&session, 0);
+}
+
+static int sunxi_ce_reset(struct sunxi_ce_priv *priv)
+{
+ int ret;
+
+ ret = reset_assert_bulk(&priv->resets);
+ if (ret)
+ return ret;
+
+ udelay(1);
+
+ ret = reset_deassert_bulk(&priv->resets);
+ if (ret)
+ return ret;
+
+ udelay(10);
+
+ return 0;
+}
+
+static void sunxi_ce_setup_mod_clock(const struct sunxi_ce_variant *variant)
+{
+ void __iomem *ccu = (void __iomem *)SUNXI_CCM_BASE;
+
+ clrsetbits_le32(ccu + SUN50I_H6_CCU_CE_CLK,
+ SUN50I_H6_CCU_CE_CLK_MASK, variant->mod_clk_cfg);
+}
+
+static int sunxi_ce_bind_child(struct udevice *dev, const char *name)
+{
+ return device_bind_driver(dev, name, name, NULL);
+}
+
+static int sunxi_ce_bind(struct udevice *dev)
+{
+ int ret;
+
+ if (CONFIG_IS_ENABLED(SUNXI_CE_AES)) {
+ ret = sunxi_ce_bind_child(dev, "sun8i-ce-aes");
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sunxi_ce_probe(struct udevice *dev)
+{
+ struct sunxi_ce_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->variant = (const struct sunxi_ce_variant *)
+ dev_get_driver_data(dev);
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ ret = reset_get_bulk(dev, &priv->resets);
+ if (ret) {
+ dev_err(dev, "failed to get resets: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_get_bulk(dev, &priv->clks);
+ if (ret) {
+ dev_err(dev, "failed to get clocks: %d\n", ret);
+ goto err_release_resets;
+ }
+
+ sunxi_ce_setup_mod_clock(priv->variant);
+
+ ret = reset_deassert_bulk(&priv->resets);
+ if (ret) {
+ dev_err(dev, "failed to deassert resets: %d\n", ret);
+ goto err_release_clks;
+ }
+
+ ret = clk_enable_bulk(&priv->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks: %d\n", ret);
+ goto err_assert_resets;
+ }
+
+ ret = sunxi_ce_reset(priv);
+ if (ret) {
+ dev_err(dev, "failed to reset CE: %d\n", ret);
+ goto err_disable_clks;
+ }
+
+ return 0;
+
+err_disable_clks:
+ clk_disable_bulk(&priv->clks);
+err_assert_resets:
+ reset_assert_bulk(&priv->resets);
+err_release_clks:
+ clk_release_bulk(&priv->clks);
+err_release_resets:
+ reset_release_bulk(&priv->resets);
+
+ return ret;
+}
+
+static int sunxi_ce_remove(struct udevice *dev)
+{
+ struct sunxi_ce_priv *priv = dev_get_priv(dev);
+
+ clk_disable_bulk(&priv->clks);
+ clk_release_bulk(&priv->clks);
+ reset_assert_bulk(&priv->resets);
+ reset_release_bulk(&priv->resets);
+
+ return 0;
+}
+
+static const struct sunxi_ce_variant sun50i_h6_variant = {
+ .aes_engine_count = 2,
+};
+
+static const struct sunxi_ce_variant sun50i_h616_variant = {
+ .needs_word_addresses = true,
+ .aes_engine_count = 2,
+ .mod_clk_cfg = SUN50I_H6_CCU_CE_CLK_SRC_MASK |
+ SUN50I_H616_CCU_CE_CLK_M,
+};
+
+static const struct udevice_id sunxi_ce_ids[] = {
+ {
+ .compatible = "allwinner,sun50i-h6-crypto",
+ .data = (ulong)&sun50i_h6_variant,
+ }, {
+ .compatible = "allwinner,sun50i-h616-crypto",
+ .data = (ulong)&sun50i_h616_variant,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(sun8i_ce) = {
+ .name = "sun8i-ce",
+ .id = UCLASS_NOP,
+ .of_match = sunxi_ce_ids,
+ .bind = sunxi_ce_bind,
+ .probe = sunxi_ce_probe,
+ .remove = sunxi_ce_remove,
+ .priv_auto = sizeof(struct sunxi_ce_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
new file mode 100644
index 00000000000..d0035af559a
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 James Hilliard
+ */
+
+#ifndef __SUN8I_CE_H
+#define __SUN8I_CE_H
+
+#include <stddef.h>
+#include <clk.h>
+#include <reset.h>
+#include <linux/bitops.h>
+#include <linux/dma-direction.h>
+#include <linux/types.h>
+
+#define SUNXI_CE_CHANNEL_AES 0
+#define SUNXI_CE_CHANNEL_RAES 1
+#define SUNXI_CE_CHANNEL_HASH 2
+#define SUNXI_CE_CHANNEL_ASYM 3
+#define SUNXI_CE_CHAN_MASK(x) BIT(x)
+#define SUNXI_CE_COMM_INT BIT(31)
+#define SUNXI_CE_METHOD_AES 0
+#define SUNXI_CE_METHOD_RAES 0x30
+#define SUNXI_CE_MAX_SG 8
+#define SUNXI_CE_MAX_CHANS 4
+#define SUNXI_CE_CHAN_ERR_MASK(x) (0xffU << ((x) * 8))
+
+struct sunxi_ce_sginfo {
+ u32 addr;
+ u32 len;
+};
+
+struct sunxi_ce_task {
+ u32 t_id;
+ u32 t_common_ctl;
+ u32 t_sym_ctl;
+ u32 t_asym_ctl;
+ u32 t_key;
+ u32 t_iv;
+ u32 t_ctr;
+ u32 t_dlen;
+ struct sunxi_ce_sginfo t_src[SUNXI_CE_MAX_SG];
+ struct sunxi_ce_sginfo t_dst[SUNXI_CE_MAX_SG];
+ u32 next;
+ u32 reserved[3];
+};
+
+static_assert(sizeof(struct sunxi_ce_sginfo) == 8);
+static_assert(offsetof(struct sunxi_ce_task, next) == 160);
+static_assert(sizeof(struct sunxi_ce_task) == 176);
+
+struct sunxi_ce_variant {
+ bool needs_word_addresses;
+ u8 aes_engine_count;
+ u32 mod_clk_cfg;
+};
+
+struct sunxi_ce_session;
+
+struct sunxi_ce_priv {
+ void __iomem *base;
+ const struct sunxi_ce_variant *variant;
+ struct clk_bulk clks;
+ struct reset_ctl_bulk resets;
+ struct sunxi_ce_session *active_session;
+};
+
+struct sunxi_ce_session {
+ struct sunxi_ce_priv *ce;
+ u32 channel_mask;
+ u32 inflight_mask;
+ unsigned long deadline[SUNXI_CE_MAX_CHANS];
+};
+
+static inline bool sunxi_ce_session_busy(const struct sunxi_ce_session *session)
+{
+ return session && session->inflight_mask;
+}
+
+/*
+ * DMA_FROM_DEVICE and DMA_BIDIRECTIONAL buffers must cover complete cache
+ * lines. DMA_TO_DEVICE buffers may use a rounded cache envelope. Keep every
+ * mapping alive until its chain completes or the owning session is closed.
+ */
+struct sunxi_ce_dma_buf {
+ dma_addr_t base;
+ dma_addr_t dma;
+ size_t map_len;
+ enum dma_data_direction dir;
+};
+
+u32 sunxi_ce_desc_dma_addr(struct sunxi_ce_priv *priv, dma_addr_t addr);
+int sunxi_ce_dma_map(struct sunxi_ce_priv *priv,
+ struct sunxi_ce_dma_buf *map, void *buf, size_t len,
+ enum dma_data_direction dir);
+void sunxi_ce_dma_unmap(struct sunxi_ce_dma_buf *map);
+/*
+ * A session owns at most one descriptor chain per selected channel. Submission
+ * supplies the descriptor transport fields, including the tail interrupt.
+ * Polling returns a completed-channel mask (or a negative error) after output
+ * DMA is idle and ordered. A nonzero close status aborts all active channels
+ * before callers release their DMA mappings. A blocking run also closes the
+ * session on error so its caller can immediately unwind local DMA mappings.
+ */
+int sunxi_ce_session_begin(struct sunxi_ce_priv *priv, u32 channel_mask,
+ struct sunxi_ce_session *session);
+int sunxi_ce_session_submit_chain(struct sunxi_ce_session *session,
+ u32 channel, u32 method,
+ struct sunxi_ce_task *tasks,
+ u32 task_count);
+int sunxi_ce_session_poll(struct sunxi_ce_session *session);
+int sunxi_ce_session_run_chain_or_close(struct sunxi_ce_session *session,
+ u32 channel, u32 method,
+ struct sunxi_ce_task *tasks,
+ u32 task_count);
+int sunxi_ce_session_close(struct sunxi_ce_session *session, int status);
+int sunxi_ce_run_task(struct sunxi_ce_priv *priv, u32 channel, u32 method,
+ struct sunxi_ce_task *task);
+
+#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v5 13/14] crypto: allwinner: add sun8i-ce ECDSA verifier
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (11 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 12/14] crypto: allwinner: add sun8i-ce AES driver James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-28 11:25 ` Simon Glass
2026-07-20 4:13 ` [PATCH v5 14/14] crypto: allwinner: add sun8i-ce hash driver James Hilliard
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
The H6 and H616 Crypto Engines include an ECC engine which can verify
ECDSA signatures. Add a UCLASS_ECDSA child for the sun8i-ce parent so FIT
signature verification can use the hardware block from U-Boot proper and
SPL.
The CE takes explicit curve parameters for each operation. Provide tables
for the curves accepted by U-Boot's FIT ECDSA parser: secp224r1,
prime256v1, secp384r1 and secp521r1. Derive each curve's a = p - 3
parameter from p instead of storing duplicate constants. Make each curve
independently selectable for U-Boot proper and SPL. SRAM-constrained
builds can keep only the curves they need. The task input layout follows
the ECC verify buffer order used by Allwinner's CE implementation.
The CE input buffer uses fixed-width curve fields. Reuse the parameter
packing logic for the message digest as well, so wider digests are
truncated to the leftmost curve-width bytes according to ECDSA rules.
Map the private input and result through the parent's cacheline-safe DMA
objects. Round result storage to complete cachelines while keeping the
descriptor output curve-sized. Submit ECC work on the dedicated asymmetric
completion channel. After retirement, unmap the result and input exactly
once before inspecting the result.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v4 -> v5:
- Let parent submission populate the descriptor transport fields
- Name fields in the fixed ECC input sequence and inline the trivial DM
verifier wrapper
- Reject missing key coordinates, digest or signature before curve lookup
- Use shared cacheline-safe DMA mappings and round private result storage
to complete cachelines
- Unmap the result and input before inspecting the verification result
Changes v3 -> v4:
- Enable CE ECDSA for both H6 and H616
- Use the dedicated asymmetric channel and shared task-session path
- Derive the NIST a = p - 3 parameter instead of storing duplicate arrays
Changes v1 -> v2:
- Document the CE input-buffer layout (suggested by Simon Glass)
- Document ECC byte-sized task length (suggested by Simon Glass)
- Make CE ECDSA curves independently selectable
(suggested by Simon Glass)
- Use neutral wording in comments and commit log
---
drivers/crypto/allwinner/sun8i-ce/Kconfig | 94 +++++
drivers/crypto/allwinner/sun8i-ce/Makefile | 1 +
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 6 +
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-ecdsa.c | 382 +++++++++++++++++++++
drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 3 +
5 files changed, 486 insertions(+)
diff --git a/drivers/crypto/allwinner/sun8i-ce/Kconfig b/drivers/crypto/allwinner/sun8i-ce/Kconfig
index 973c4f3af21..49686d07aa3 100644
--- a/drivers/crypto/allwinner/sun8i-ce/Kconfig
+++ b/drivers/crypto/allwinner/sun8i-ce/Kconfig
@@ -39,3 +39,97 @@ config SPL_SUNXI_CE_AES
Select this option to enable AES decryption in SPL using the Crypto
Engine found in Allwinner H6 and H616 compatible SoCs. This can be
used to decrypt FIT images before loading U-Boot proper.
+
+config SUNXI_CE_ECDSA
+ bool "Allwinner sunxi CE ECDSA verifier"
+ depends on ARCH_SUNXI
+ depends on ECDSA_VERIFY
+ depends on CLK && DM_RESET
+ select SUNXI_CE
+ help
+ Select this option to enable ECDSA signature verification using
+ the Crypto Engine found in Allwinner sunxi SoCs. FIT image
+ signatures can then be checked by the hardware accelerator in
+ U-Boot proper. Digests wider than the selected curve are
+ truncated according to ECDSA rules.
+
+config SPL_SUNXI_CE_ECDSA
+ bool "Allwinner sunxi CE ECDSA verifier in SPL"
+ depends on ARCH_SUNXI
+ depends on MACH_SUN50I_H6 || MACH_SUN50I_H616
+ depends on SPL_DM
+ depends on SPL_OF_CONTROL
+ depends on SPL_ECDSA_VERIFY
+ select SPL_CRYPTO
+ select SPL_SUNXI_CE
+ help
+ Select this option to enable ECDSA signature verification in SPL
+ using the Crypto Engine found in Allwinner H6 and H616 compatible
+ SoCs.
+ This allows SPL FIT image signatures to be checked by the hardware
+ accelerator before U-Boot proper is loaded. Digests wider than the
+ selected curve are truncated according to ECDSA rules.
+
+if SUNXI_CE_ECDSA
+
+config SUNXI_CE_ECDSA_SECP224R1
+ bool "Support secp224r1"
+ default y
+ help
+ Enable the secp224r1 curve parameters for the sunxi CE ECDSA
+ verifier.
+
+config SUNXI_CE_ECDSA_PRIME256V1
+ bool "Support prime256v1"
+ default y
+ help
+ Enable the prime256v1 curve parameters for the sunxi CE ECDSA
+ verifier.
+
+config SUNXI_CE_ECDSA_SECP384R1
+ bool "Support secp384r1"
+ default y
+ help
+ Enable the secp384r1 curve parameters for the sunxi CE ECDSA
+ verifier.
+
+config SUNXI_CE_ECDSA_SECP521R1
+ bool "Support secp521r1"
+ default y
+ help
+ Enable the secp521r1 curve parameters for the sunxi CE ECDSA
+ verifier.
+
+endif
+
+if SPL_SUNXI_CE_ECDSA
+
+config SPL_SUNXI_CE_ECDSA_SECP224R1
+ bool "Support secp224r1 in SPL"
+ default y
+ help
+ Enable the secp224r1 curve parameters for the sunxi CE ECDSA
+ verifier in SPL.
+
+config SPL_SUNXI_CE_ECDSA_PRIME256V1
+ bool "Support prime256v1 in SPL"
+ default y
+ help
+ Enable the prime256v1 curve parameters for the sunxi CE ECDSA
+ verifier in SPL.
+
+config SPL_SUNXI_CE_ECDSA_SECP384R1
+ bool "Support secp384r1 in SPL"
+ default y
+ help
+ Enable the secp384r1 curve parameters for the sunxi CE ECDSA
+ verifier in SPL.
+
+config SPL_SUNXI_CE_ECDSA_SECP521R1
+ bool "Support secp521r1 in SPL"
+ default y
+ help
+ Enable the secp521r1 curve parameters for the sunxi CE ECDSA
+ verifier in SPL.
+
+endif
diff --git a/drivers/crypto/allwinner/sun8i-ce/Makefile b/drivers/crypto/allwinner/sun8i-ce/Makefile
index 2a8778065b1..753ea827a0d 100644
--- a/drivers/crypto/allwinner/sun8i-ce/Makefile
+++ b/drivers/crypto/allwinner/sun8i-ce/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_$(PHASE_)SUNXI_CE) += sun8i-ce-core.o
obj-$(CONFIG_$(PHASE_)SUNXI_CE_AES) += sun8i-ce-aes.o
+obj-$(CONFIG_$(PHASE_)SUNXI_CE_ECDSA) += sun8i-ce-ecdsa.o
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
index f22d534caea..4ce1ee70c14 100644
--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
@@ -673,6 +673,12 @@ static int sunxi_ce_bind(struct udevice *dev)
return ret;
}
+ if (CONFIG_IS_ENABLED(SUNXI_CE_ECDSA)) {
+ ret = sunxi_ce_bind_child(dev, "sun8i-ce-ecdsa");
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-ecdsa.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-ecdsa.c
new file mode 100644
index 00000000000..f9b36347e4f
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-ecdsa.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 James Hilliard
+ */
+
+#include <crypto/ecdsa-uclass.h>
+#include <dm.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <string.h>
+#include <linux/kernel.h>
+#include "sun8i-ce.h"
+
+enum sunxi_ecdsa_field {
+ SUNXI_ECDSA_FIELD_N,
+ SUNXI_ECDSA_FIELD_S,
+ SUNXI_ECDSA_FIELD_E,
+ SUNXI_ECDSA_FIELD_R,
+ SUNXI_ECDSA_FIELD_P,
+ SUNXI_ECDSA_FIELD_A,
+ SUNXI_ECDSA_FIELD_GX,
+ SUNXI_ECDSA_FIELD_GY,
+ SUNXI_ECDSA_FIELD_QX,
+ SUNXI_ECDSA_FIELD_QY,
+ SUNXI_ECDSA_FIELD_N2,
+ SUNXI_ECDSA_FIELD_R2,
+ SUNXI_ECDSA_FIELD_COUNT,
+};
+
+#define SUNXI_ECDSA_MAX_WORDS \
+ (CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_SECP521R1) ? 17 : \
+ CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_SECP384R1) ? 12 : \
+ CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_PRIME256V1) ? 8 : \
+ CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_SECP224R1) ? 7 : 1)
+#define SUNXI_ECDSA_MAX_BYTES (SUNXI_ECDSA_MAX_WORDS * sizeof(u32))
+#define SUNXI_ECDSA_MAX_SRC_BYTES \
+ (SUNXI_ECDSA_FIELD_COUNT * SUNXI_ECDSA_MAX_BYTES)
+#define SUNXI_ECDSA_MAX_DST_BYTES ALIGN(SUNXI_ECDSA_MAX_BYTES, \
+ ARCH_DMA_MINALIGN)
+
+struct sunxi_ecdsa_job {
+ struct sunxi_ce_task task __aligned(ARCH_DMA_MINALIGN);
+ u8 src[SUNXI_ECDSA_MAX_SRC_BYTES] __aligned(ARCH_DMA_MINALIGN);
+ u8 dst[SUNXI_ECDSA_MAX_DST_BYTES] __aligned(ARCH_DMA_MINALIGN);
+};
+
+struct sunxi_ecdsa_curve {
+ /* p, Gx, Gy and n as consecutive big-endian curve-width values. */
+ const u8 *params;
+ u16 bits;
+ u8 bytes;
+ u8 words;
+};
+
+static const u8 ecdsa_p224_params[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ /* Gx */
+ 0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f,
+ 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3,
+ 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6,
+ 0x11, 0x5c, 0x1d, 0x21,
+ /* Gy */
+ 0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb,
+ 0x4c, 0x22, 0xdf, 0xe6, 0xcd, 0x43, 0x75, 0xa0,
+ 0x5a, 0x07, 0x47, 0x64, 0x44, 0xd5, 0x81, 0x99,
+ 0x85, 0x00, 0x7e, 0x34,
+ /* n */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x3d,
+};
+
+static const u8 ecdsa_p256_params[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ /* Gx */
+ 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
+ 0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
+ 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
+ 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96,
+ /* Gy */
+ 0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b,
+ 0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
+ 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+ 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5,
+ /* n */
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
+ 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
+};
+
+static const u8 ecdsa_p384_params[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ /* Gx */
+ 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37,
+ 0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74,
+ 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98,
+ 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38,
+ 0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c,
+ 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7,
+ /* Gy */
+ 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f,
+ 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29,
+ 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
+ 0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0,
+ 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d,
+ 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f,
+ /* n */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
+ 0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
+ 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73,
+};
+
+static const u8 ecdsa_p521_params[] = {
+ 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff,
+ /* Gx */
+ 0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04,
+ 0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, 0x95,
+ 0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
+ 0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d,
+ 0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, 0xe7,
+ 0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
+ 0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a,
+ 0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, 0xe5,
+ 0xbd, 0x66,
+ /* Gy */
+ 0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b,
+ 0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d,
+ 0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+ 0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e,
+ 0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4,
+ 0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+ 0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72,
+ 0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1,
+ 0x66, 0x50,
+ /* n */
+ 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfa, 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f,
+ 0x96, 0x6b, 0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09,
+ 0xa5, 0xd0, 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c,
+ 0x47, 0xae, 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38,
+ 0x64, 0x09,
+};
+
+static const struct sunxi_ecdsa_curve sunxi_ecdsa_secp224r1 = {
+ .bits = 224,
+ .bytes = 28,
+ .words = 7,
+ .params = ecdsa_p224_params,
+};
+
+static const struct sunxi_ecdsa_curve sunxi_ecdsa_prime256v1 = {
+ .bits = 256,
+ .bytes = 32,
+ .words = 8,
+ .params = ecdsa_p256_params,
+};
+
+static const struct sunxi_ecdsa_curve sunxi_ecdsa_secp384r1 = {
+ .bits = 384,
+ .bytes = 48,
+ .words = 12,
+ .params = ecdsa_p384_params,
+};
+
+static const struct sunxi_ecdsa_curve sunxi_ecdsa_secp521r1 = {
+ .bits = 521,
+ .bytes = 66,
+ .words = 17,
+ .params = ecdsa_p521_params,
+};
+
+static const struct sunxi_ecdsa_curve *
+sunxi_ecdsa_find_curve(const struct ecdsa_public_key *pubkey)
+{
+ if (CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_SECP224R1) &&
+ !strcmp(pubkey->curve_name, "secp224r1"))
+ return &sunxi_ecdsa_secp224r1;
+ if (CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_PRIME256V1) &&
+ !strcmp(pubkey->curve_name, "prime256v1"))
+ return &sunxi_ecdsa_prime256v1;
+ if (CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_SECP384R1) &&
+ !strcmp(pubkey->curve_name, "secp384r1"))
+ return &sunxi_ecdsa_secp384r1;
+ if (CONFIG_IS_ENABLED(SUNXI_CE_ECDSA_SECP521R1) &&
+ !strcmp(pubkey->curve_name, "secp521r1"))
+ return &sunxi_ecdsa_secp521r1;
+
+ return NULL;
+}
+
+static u8 *sunxi_ecdsa_copy_le_param(u8 *dst,
+ const struct sunxi_ecdsa_curve *curve,
+ const void *src, size_t len)
+{
+ const u8 *p = src;
+ size_t copy_len;
+ int i;
+
+ copy_len = min_t(size_t, len, curve->bytes);
+ for (i = 0; i < copy_len; i++)
+ dst[i] = p[copy_len - 1 - i];
+
+ return dst + curve->words * sizeof(u32);
+}
+
+static void sunxi_ecdsa_fill_src(struct sunxi_ecdsa_job *job,
+ const struct sunxi_ecdsa_curve *curve,
+ const struct ecdsa_public_key *pubkey,
+ const void *hash, size_t hash_len,
+ const void *signature)
+{
+ const u8 *r = signature;
+ const u8 *s = r + curve->bytes;
+ const u8 *p = curve->params;
+ const u8 *gx = p + curve->bytes;
+ const u8 *gy = gx + curve->bytes;
+ const u8 *n = gy + curve->bytes;
+ const void *params[SUNXI_ECDSA_FIELD_COUNT] = {
+ [SUNXI_ECDSA_FIELD_N] = n,
+ [SUNXI_ECDSA_FIELD_S] = s,
+ [SUNXI_ECDSA_FIELD_E] = hash,
+ [SUNXI_ECDSA_FIELD_R] = r,
+ [SUNXI_ECDSA_FIELD_P] = p,
+ [SUNXI_ECDSA_FIELD_A] = p,
+ [SUNXI_ECDSA_FIELD_GX] = gx,
+ [SUNXI_ECDSA_FIELD_GY] = gy,
+ [SUNXI_ECDSA_FIELD_QX] = pubkey->x,
+ [SUNXI_ECDSA_FIELD_QY] = pubkey->y,
+ [SUNXI_ECDSA_FIELD_N2] = n,
+ [SUNXI_ECDSA_FIELD_R2] = r,
+ };
+ u8 *dst = job->src;
+ u32 i;
+
+ /*
+ * The CE manual specifies this fixed sequence of little-endian
+ * curve-width fields for ECC signature verification:
+ *
+ * n, s, e, r, p, a, Gx, Gy, Qx, Qy, n, r
+ */
+ for (i = 0; i < ARRAY_SIZE(params); i++) {
+ u8 *field = dst;
+ size_t len = i == SUNXI_ECDSA_FIELD_E ? hash_len : curve->bytes;
+
+ dst = sunxi_ecdsa_copy_le_param(dst, curve, params[i], len);
+ if (i == SUNXI_ECDSA_FIELD_A) {
+ u32 borrow;
+
+ /* All supported NIST curves use a = p - 3. */
+ for (borrow = 3; borrow; field++) {
+ u8 val = *field;
+
+ *field = val - borrow;
+ borrow = val < borrow;
+ }
+ }
+ }
+}
+
+static void sunxi_ecdsa_fill_task(struct sunxi_ce_priv *priv,
+ struct sunxi_ecdsa_job *job,
+ const struct sunxi_ecdsa_curve *curve,
+ dma_addr_t src, dma_addr_t dst)
+{
+ struct sunxi_ce_task *task = &job->task;
+ u32 bytes = curve->words * sizeof(u32);
+
+ task->t_asym_ctl = curve->words |
+ (SUNXI_CE_ECC_OP_VERIFY << SUNXI_CE_ECC_OP_SHIFT);
+ /* The ECC engine uses a byte-sized task length. */
+ task->t_dlen = SUNXI_ECDSA_FIELD_COUNT * bytes;
+ task->t_src[0].addr = sunxi_ce_desc_dma_addr(priv, src);
+ task->t_src[0].len = task->t_dlen / sizeof(u32);
+ task->t_dst[0].addr = sunxi_ce_desc_dma_addr(priv, dst);
+ task->t_dst[0].len = curve->words;
+}
+
+static int sunxi_ecdsa_run(struct sunxi_ce_priv *priv,
+ struct sunxi_ecdsa_job *job,
+ const struct sunxi_ecdsa_curve *curve)
+{
+ size_t dst_len = curve->words * sizeof(u32);
+ size_t src_len = SUNXI_ECDSA_FIELD_COUNT * dst_len;
+ struct sunxi_ce_dma_buf src_dma = { };
+ struct sunxi_ce_dma_buf dst_dma = { };
+ u32 result;
+ int ret;
+
+ ret = sunxi_ce_dma_map(priv, &src_dma, job->src, src_len,
+ DMA_TO_DEVICE);
+ if (ret)
+ goto out;
+ ret = sunxi_ce_dma_map(priv, &dst_dma, job->dst, sizeof(job->dst),
+ DMA_FROM_DEVICE);
+ if (ret)
+ goto out;
+
+ sunxi_ecdsa_fill_task(priv, job, curve, src_dma.dma, dst_dma.dma);
+
+ ret = sunxi_ce_run_task(priv, SUNXI_CE_CHANNEL_ASYM,
+ SUNXI_CE_METHOD_ECC, &job->task);
+
+out:
+ sunxi_ce_dma_unmap(&dst_dma);
+ sunxi_ce_dma_unmap(&src_dma);
+ if (ret)
+ return ret;
+
+ result = *(u32 *)job->dst;
+
+ return result == 1 ? 0 : -EPERM;
+}
+
+static int sunxi_ecdsa_verify_dm(struct udevice *dev,
+ const struct ecdsa_public_key *pubkey,
+ const void *hash, size_t hash_len,
+ const void *signature, size_t sig_len)
+{
+ struct sunxi_ce_priv *priv = dev_get_priv(dev_get_parent(dev));
+ const struct sunxi_ecdsa_curve *curve;
+ struct sunxi_ecdsa_job *job;
+ int ret;
+
+ if (!priv || !pubkey || !pubkey->curve_name || !pubkey->x ||
+ !pubkey->y || !hash || !hash_len || !signature)
+ return -EINVAL;
+
+ curve = sunxi_ecdsa_find_curve(pubkey);
+ if (!curve || pubkey->size_bits != curve->bits ||
+ sig_len != curve->bytes * 2)
+ return -EINVAL;
+
+ job = malloc_cache_aligned(sizeof(*job));
+ if (!job)
+ return -ENOMEM;
+
+ memset(job, 0, sizeof(*job));
+ sunxi_ecdsa_fill_src(job, curve, pubkey, hash, hash_len, signature);
+
+ ret = sunxi_ecdsa_run(priv, job, curve);
+ free(job);
+
+ return ret;
+}
+
+static const struct ecdsa_ops sunxi_ecdsa_ops = {
+ .verify = sunxi_ecdsa_verify_dm,
+};
+
+U_BOOT_DRIVER(sun8i_ce_ecdsa) = {
+ .name = "sun8i-ce-ecdsa",
+ .id = UCLASS_ECDSA,
+ .ops = &sunxi_ecdsa_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
index d0035af559a..d5b7edd357d 100644
--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
@@ -21,6 +21,9 @@
#define SUNXI_CE_COMM_INT BIT(31)
#define SUNXI_CE_METHOD_AES 0
#define SUNXI_CE_METHOD_RAES 0x30
+#define SUNXI_CE_METHOD_ECC 33
+#define SUNXI_CE_ECC_OP_VERIFY 7
+#define SUNXI_CE_ECC_OP_SHIFT 16
#define SUNXI_CE_MAX_SG 8
#define SUNXI_CE_MAX_CHANS 4
#define SUNXI_CE_CHAN_ERR_MASK(x) (0xffU << ((x) * 8))
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 13/14] crypto: allwinner: add sun8i-ce ECDSA verifier
2026-07-20 4:13 ` [PATCH v5 13/14] crypto: allwinner: add sun8i-ce ECDSA verifier James Hilliard
@ 2026-07-28 11:25 ` Simon Glass
0 siblings, 0 replies; 29+ messages in thread
From: Simon Glass @ 2026-07-28 11:25 UTC (permalink / raw)
To: james.hilliard1
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin, u-boot
On 2026-07-20T04:13:44, James Hilliard <james.hilliard1@gmail.com> wrote:
> crypto: allwinner: add sun8i-ce ECDSA verifier
>
> The H6 and H616 Crypto Engines include an ECC engine which can verify
> ECDSA signatures. Add a UCLASS_ECDSA child for the sun8i-ce parent so FIT
> signature verification can use the hardware block from U-Boot proper and
> SPL.
>
> The CE takes explicit curve parameters for each operation. Provide tables
> for the curves accepted by U-Boot's FIT ECDSA parser: secp224r1,
> prime256v1, secp384r1 and secp521r1. Derive each curve's a = p - 3
> parameter from p instead of storing duplicate constants. Make each curve
> independently selectable for U-Boot proper and SPL. SRAM-constrained
> builds can keep only the curves they need. The task input layout follows
> the ECC verify buffer order used by Allwinner's CE implementation.
>
> The CE input buffer uses fixed-width curve fields. Reuse the parameter
> packing logic for the message digest as well, so wider digests are
> truncated to the leftmost curve-width bytes according to ECDSA rules.
> Map the private input and result through the parent's cacheline-safe DMA
> objects. Round result storage to complete cachelines while keeping the
> descriptor output curve-sized. Submit ECC work on the dedicated asymmetric
> completion channel. After retirement, unmap the result and input exactly
> once before inspecting the result.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
>
> drivers/crypto/allwinner/sun8i-ce/Kconfig | 94 +++++
> drivers/crypto/allwinner/sun8i-ce/Makefile | 1 +
> drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 6 +
> drivers/crypto/allwinner/sun8i-ce/sun8i-ce-ecdsa.c | 382 +++++++++++++++++++++
> drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 3 +
> 5 files changed, 486 insertions(+)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v5 14/14] crypto: allwinner: add sun8i-ce hash driver
2026-07-20 4:13 [PATCH v5 00/14] crypto: allwinner: enable sun8i-ce FIT crypto James Hilliard
` (12 preceding siblings ...)
2026-07-20 4:13 ` [PATCH v5 13/14] crypto: allwinner: add sun8i-ce ECDSA verifier James Hilliard
@ 2026-07-20 4:13 ` James Hilliard
2026-07-28 11:25 ` Simon Glass
13 siblings, 1 reply; 29+ messages in thread
From: James Hilliard @ 2026-07-20 4:13 UTC (permalink / raw)
To: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley
Cc: Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
James Hilliard, Thierry Reding, Quentin Schulz, Quentin Schulz,
Marek Vasut, Marek Vasut, Rasmus Villemoes, Rasmus Villemoes,
Aristo Chen, Anton Ivanov, Daniel Golle, Francois Berder,
Peng Fan, Neil Armstrong, Randolph Sapp, Jonas Karlman,
Wolfgang Wallner, Alexey Charkov, Ilias Apalodimas,
Heiko Schocher, Kory Maincent (TI.com), Anshul Dalal,
Johan Jonker, Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin
The Allwinner sun8i Crypto Engine includes hardware hash methods. Add a
UCLASS_HASH child for the sun8i-ce parent so FIT hash verification can
use the accelerator from U-Boot proper and SPL.
Support MD5, SHA1, SHA256, SHA384 and SHA512. Build the final hash padding
in the driver. Stream directly addressable input through bounded 64 MiB
zero-copy tasks and word-unaligned input through a fixed 128 KiB repack
buffer. Feed each intermediate state back through hash input-IV mode,
keeping heap use independent of input size while word-aligned cacheline
offsets remain zero-copy.
Map input, padding, optional state and a cacheline-rounded private output
through the parent's DMA objects. Keep the descriptor output length limited
to the algorithm state, then submit hash work on the dedicated completion
channel through the shared task-session API. Keep one session across every
continuation chunk while retiring each task before releasing its mappings.
Release mappings in reverse preparation order after the session retires.
H6 and H616 use bit-sized hash task lengths, so keep each submitted chunk
within the 32-bit task descriptor field. Feed the watchdog from the shared
CE polling path while waiting for hardware completion.
In SPL, only advertise hash algorithms selected for that phase so
SRAM-constrained builds do not accept wider algorithms unless requested.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v4 -> v5:
- Use the parent's explicit close-on-error blocking helper to populate
descriptor transport fields and abort failed chains before callers
release their DMA mappings
- Replace the algorithm switch and separate size lookups with one table
- Derive bounded padding without adding near the input-size limit, and
validate input ranges and descriptor-length arithmetic
- Replace the payload-sized word-unaligned bounce with a fixed 128 KiB
streaming repack while keeping word-aligned cacheline offsets zero-copy
- Split large direct input into bounded 64 MiB tasks and use one
continuation loop and one CE session for direct and repacked input
- Carry intermediate state through hash input-IV mode
- Use shared cacheline-safe DMA mappings and round private result storage
to complete cachelines
Changes v3 -> v4:
- Consolidate algorithm selection and invariant task sizing
- Use the dedicated hash channel and shared task-session path
- Return -EOPNOTSUPP for unavailable algorithms
Changes v1 -> v2:
- Reject oversized bit-length hash tasks (suggested by Simon Glass)
- Feed the watchdog during CE polling (suggested by Simon Glass)
- Drop the post-operation schedule() call (suggested by Simon Glass)
- Bounce only word-unaligned input
---
drivers/crypto/allwinner/sun8i-ce/Kconfig | 26 ++
drivers/crypto/allwinner/sun8i-ce/Makefile | 1 +
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 6 +
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c | 343 ++++++++++++++++++++++
drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 5 +
5 files changed, 381 insertions(+)
diff --git a/drivers/crypto/allwinner/sun8i-ce/Kconfig b/drivers/crypto/allwinner/sun8i-ce/Kconfig
index 49686d07aa3..c53c38cf5d6 100644
--- a/drivers/crypto/allwinner/sun8i-ce/Kconfig
+++ b/drivers/crypto/allwinner/sun8i-ce/Kconfig
@@ -40,6 +40,32 @@ config SPL_SUNXI_CE_AES
Engine found in Allwinner H6 and H616 compatible SoCs. This can be
used to decrypt FIT images before loading U-Boot proper.
+config SUNXI_CE_HASH
+ bool "Allwinner sunxi CE hash"
+ depends on ARCH_SUNXI
+ depends on DM_HASH
+ depends on CLK && DM_RESET
+ select SUNXI_CE
+ help
+ Select this option to enable hash calculation using the Crypto Engine
+ found in Allwinner sunxi SoCs. The driver supports MD5, SHA1,
+ SHA256, SHA384 and SHA512.
+
+config SPL_SUNXI_CE_HASH
+ bool "Allwinner sunxi CE hash in SPL"
+ depends on ARCH_SUNXI
+ depends on MACH_SUN50I_H6 || MACH_SUN50I_H616
+ depends on SPL_DM
+ depends on SPL_OF_CONTROL
+ select SPL_DM_HASH
+ select SPL_CRYPTO
+ select SPL_SUNXI_CE
+ help
+ Select this option to enable hash calculation in SPL using the Crypto
+ Engine found in Allwinner H6 and H616 compatible SoCs. FIT image
+ hashes can then be calculated by the hardware accelerator before
+ U-Boot proper is loaded.
+
config SUNXI_CE_ECDSA
bool "Allwinner sunxi CE ECDSA verifier"
depends on ARCH_SUNXI
diff --git a/drivers/crypto/allwinner/sun8i-ce/Makefile b/drivers/crypto/allwinner/sun8i-ce/Makefile
index 753ea827a0d..5baf65e40ec 100644
--- a/drivers/crypto/allwinner/sun8i-ce/Makefile
+++ b/drivers/crypto/allwinner/sun8i-ce/Makefile
@@ -2,4 +2,5 @@
obj-$(CONFIG_$(PHASE_)SUNXI_CE) += sun8i-ce-core.o
obj-$(CONFIG_$(PHASE_)SUNXI_CE_AES) += sun8i-ce-aes.o
+obj-$(CONFIG_$(PHASE_)SUNXI_CE_HASH) += sun8i-ce-hash.o
obj-$(CONFIG_$(PHASE_)SUNXI_CE_ECDSA) += sun8i-ce-ecdsa.o
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
index 4ce1ee70c14..201e9203bf1 100644
--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
@@ -679,6 +679,12 @@ static int sunxi_ce_bind(struct udevice *dev)
return ret;
}
+ if (CONFIG_IS_ENABLED(SUNXI_CE_HASH)) {
+ ret = sunxi_ce_bind_child(dev, "sun8i-ce-hash");
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c
new file mode 100644
index 00000000000..583d2e5de20
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 James Hilliard
+ */
+
+#define LOG_CATEGORY UCLASS_HASH
+
+#include <dm.h>
+#include <limits.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <u-boot/hash.h>
+#include <u-boot/md5.h>
+#include <u-boot/sha1.h>
+#include <u-boot/sha256.h>
+#include <u-boot/sha512.h>
+#include <linux/kernel.h>
+#include "sun8i-ce.h"
+
+#define SUNXI_CE_HASH_MAX_BLOCK_SIZE SHA512_BLOCK_SIZE
+#define SUNXI_CE_HASH_MAX_DIGEST_SIZE SHA512_SUM_LEN
+#define SUNXI_CE_HASH_RESULT_SIZE ALIGN(SUNXI_CE_HASH_MAX_DIGEST_SIZE, \
+ ARCH_DMA_MINALIGN)
+#define SUNXI_CE_HASH_MAX_PAD_SIZE (2 * SUNXI_CE_HASH_MAX_BLOCK_SIZE)
+#define SUNXI_CE_HASH_REPACK_SIZE (128 * 1024)
+#define SUNXI_CE_HASH_DIRECT_SIZE (64 * 1024 * 1024)
+#define SUNXI_CE_HASH_IV_INPUT BIT(16)
+#define SUNXI_CE_HASH_BLOCK_SIZE 64
+
+static_assert(IS_ALIGNED(SUNXI_CE_HASH_REPACK_SIZE,
+ SUNXI_CE_HASH_MAX_BLOCK_SIZE));
+static_assert(IS_ALIGNED(SUNXI_CE_HASH_DIRECT_SIZE,
+ SUNXI_CE_HASH_MAX_BLOCK_SIZE));
+
+struct sunxi_hash_job {
+ struct sunxi_ce_task task __aligned(ARCH_DMA_MINALIGN);
+ u8 pad[SUNXI_CE_HASH_MAX_PAD_SIZE] __aligned(ARCH_DMA_MINALIGN);
+ u8 result[SUNXI_CE_HASH_RESULT_SIZE] __aligned(ARCH_DMA_MINALIGN);
+ u8 state[SUNXI_CE_HASH_MAX_DIGEST_SIZE] __aligned(ARCH_DMA_MINALIGN);
+};
+
+struct sunxi_hash_alg {
+ u8 method;
+ u8 block_size;
+ u8 digest_size;
+ u8 state_size;
+ bool little_endian_len;
+ bool available;
+};
+
+static const struct sunxi_hash_alg sunxi_hash_algs[HASH_ALGO_NUM] = {
+ [HASH_ALGO_MD5] = {
+ .method = SUNXI_CE_METHOD_MD5,
+ .block_size = SUNXI_CE_HASH_BLOCK_SIZE,
+ .digest_size = MD5_SUM_LEN,
+ .state_size = MD5_SUM_LEN,
+ .little_endian_len = true,
+ .available = !IS_ENABLED(CONFIG_XPL_BUILD) ||
+ CONFIG_IS_ENABLED(MD5),
+ },
+ [HASH_ALGO_SHA1] = {
+ .method = SUNXI_CE_METHOD_SHA1,
+ .block_size = SUNXI_CE_HASH_BLOCK_SIZE,
+ .digest_size = SHA1_SUM_LEN,
+ .state_size = SHA1_SUM_LEN,
+ .available = !IS_ENABLED(CONFIG_XPL_BUILD) ||
+ CONFIG_IS_ENABLED(SHA1),
+ },
+ [HASH_ALGO_SHA256] = {
+ .method = SUNXI_CE_METHOD_SHA256,
+ .block_size = SUNXI_CE_HASH_BLOCK_SIZE,
+ .digest_size = SHA256_SUM_LEN,
+ .state_size = SHA256_SUM_LEN,
+ .available = !IS_ENABLED(CONFIG_XPL_BUILD) ||
+ CONFIG_IS_ENABLED(SHA256),
+ },
+ [HASH_ALGO_SHA384] = {
+ .method = SUNXI_CE_METHOD_SHA384,
+ .block_size = SHA512_BLOCK_SIZE,
+ .digest_size = SHA384_SUM_LEN,
+ .state_size = SHA512_SUM_LEN,
+ .available = !IS_ENABLED(CONFIG_XPL_BUILD) ||
+ CONFIG_IS_ENABLED(SHA384),
+ },
+ [HASH_ALGO_SHA512] = {
+ .method = SUNXI_CE_METHOD_SHA512,
+ .block_size = SHA512_BLOCK_SIZE,
+ .digest_size = SHA512_SUM_LEN,
+ .state_size = SHA512_SUM_LEN,
+ .available = !IS_ENABLED(CONFIG_XPL_BUILD) ||
+ CONFIG_IS_ENABLED(SHA512),
+ },
+};
+
+static const struct sunxi_hash_alg *sunxi_hash_get_alg(enum HASH_ALGO algo)
+{
+ if ((u32)algo >= ARRAY_SIZE(sunxi_hash_algs) ||
+ !sunxi_hash_algs[algo].available)
+ return NULL;
+
+ return &sunxi_hash_algs[algo];
+}
+
+static size_t sunxi_hash_pad(const struct sunxi_hash_alg *alg, u8 *pad,
+ const u8 *tail, size_t tail_len, size_t len)
+{
+ size_t block_size = alg->block_size;
+ size_t rem = len % block_size;
+ size_t len_size = block_size == SHA512_BLOCK_SIZE ? 16 : 8;
+ size_t pad_len, len_off;
+ u64 bits;
+
+ pad_len = tail_len + (rem < block_size - len_size ?
+ block_size - rem : 2 * block_size - rem);
+
+ memset(pad, 0, pad_len);
+ if (tail_len)
+ memcpy(pad, tail, tail_len);
+ pad[tail_len] = 0x80;
+
+ bits = (u64)len << 3;
+ len_off = pad_len - 8;
+ if (alg->little_endian_len) {
+ bits = cpu_to_le64(bits);
+ memcpy(pad + len_off, &bits, sizeof(bits));
+ } else {
+ bits = cpu_to_be64(bits);
+ memcpy(pad + len_off, &bits, sizeof(bits));
+ }
+
+ return pad_len;
+}
+
+static void sunxi_hash_fill_task(struct sunxi_ce_priv *ce,
+ struct sunxi_hash_job *job,
+ dma_addr_t iv,
+ dma_addr_t src, size_t src_len,
+ dma_addr_t pad, size_t pad_len,
+ dma_addr_t result, size_t state_len)
+{
+ struct sunxi_ce_task *task = &job->task;
+ u32 total_len = src_len + pad_len;
+ u32 sg = 0;
+
+ memset(task, 0, sizeof(*task));
+
+ task->t_common_ctl = iv ? SUNXI_CE_HASH_IV_INPUT : 0;
+ if (iv)
+ task->t_iv = sunxi_ce_desc_dma_addr(ce, iv);
+ task->t_dlen = total_len * 8;
+
+ if (src_len) {
+ task->t_src[sg].addr = sunxi_ce_desc_dma_addr(ce, src);
+ task->t_src[sg].len = src_len / sizeof(u32);
+ sg++;
+ }
+ if (pad_len) {
+ task->t_src[sg].addr = sunxi_ce_desc_dma_addr(ce, pad);
+ task->t_src[sg].len = pad_len / sizeof(u32);
+ }
+
+ task->t_dst[0].addr = sunxi_ce_desc_dma_addr(ce, result);
+ task->t_dst[0].len = state_len / sizeof(u32);
+}
+
+static int sunxi_hash_run_chunk(struct sunxi_ce_priv *ce,
+ struct sunxi_ce_session *session,
+ struct sunxi_hash_job *job,
+ u32 method,
+ const void *src, size_t src_len, size_t pad_len,
+ const void *iv, size_t state_len)
+{
+ struct sunxi_ce_dma_buf result_dma = { };
+ struct sunxi_ce_dma_buf src_dma = { };
+ struct sunxi_ce_dma_buf pad_dma = { };
+ struct sunxi_ce_dma_buf iv_dma = { };
+ int ret;
+
+ if (!IS_ALIGNED(src_len, sizeof(u32)) ||
+ !IS_ALIGNED(pad_len, sizeof(u32)) ||
+ src_len > U32_MAX / 8 || pad_len > U32_MAX / 8 - src_len)
+ return -EINVAL;
+
+ ret = sunxi_ce_dma_map(ce, &src_dma, (void *)src, src_len,
+ DMA_TO_DEVICE);
+ if (ret)
+ goto out_unmap;
+ ret = sunxi_ce_dma_map(ce, &pad_dma, job->pad, pad_len,
+ DMA_TO_DEVICE);
+ if (ret)
+ goto out_unmap;
+ ret = sunxi_ce_dma_map(ce, &iv_dma, (void *)iv, iv ? state_len : 0,
+ DMA_TO_DEVICE);
+ if (ret)
+ goto out_unmap;
+ ret = sunxi_ce_dma_map(ce, &result_dma, job->result,
+ sizeof(job->result),
+ DMA_FROM_DEVICE);
+ if (ret)
+ goto out_unmap;
+
+ sunxi_hash_fill_task(ce, job, iv_dma.dma, src_dma.dma,
+ src_len, pad_dma.dma, pad_len, result_dma.dma,
+ state_len);
+
+ ret = sunxi_ce_session_run_chain_or_close(session,
+ SUNXI_CE_CHANNEL_HASH, method,
+ &job->task, 1);
+
+out_unmap:
+ sunxi_ce_dma_unmap(&result_dma);
+ sunxi_ce_dma_unmap(&iv_dma);
+ sunxi_ce_dma_unmap(&pad_dma);
+ sunxi_ce_dma_unmap(&src_dma);
+
+ return ret;
+}
+
+static int sunxi_hash_run_stream(struct sunxi_ce_priv *ce,
+ struct sunxi_hash_job *job,
+ u32 method,
+ const u8 *src, size_t src_len,
+ size_t pad_len, size_t state_len)
+{
+ struct sunxi_ce_session session;
+ const void *iv = NULL;
+ const void *chunk;
+ size_t chunk_size;
+ u8 *repack = NULL;
+ int ret;
+
+ if (src_len && !IS_ALIGNED((uintptr_t)src, sizeof(u32))) {
+ /* CE scatter-gather addresses require word-aligned chunks. */
+ chunk_size = SUNXI_CE_HASH_REPACK_SIZE;
+ repack = memalign(ARCH_DMA_MINALIGN, chunk_size);
+ if (!repack)
+ return -ENOMEM;
+ } else {
+ chunk_size = SUNXI_CE_HASH_DIRECT_SIZE;
+ }
+
+ ret = sunxi_ce_session_begin(ce,
+ SUNXI_CE_CHAN_MASK(SUNXI_CE_CHANNEL_HASH),
+ &session);
+ if (ret)
+ goto out;
+
+ while (src_len > chunk_size) {
+ chunk = src;
+ if (repack) {
+ memcpy(repack, src, chunk_size);
+ chunk = repack;
+ }
+ ret = sunxi_hash_run_chunk(ce, &session, job, method, chunk,
+ chunk_size, 0, iv, state_len);
+ if (ret)
+ goto out_close;
+
+ memcpy(job->state, job->result, state_len);
+ iv = job->state;
+ src += chunk_size;
+ src_len -= chunk_size;
+ }
+
+ chunk = src;
+ if (repack) {
+ memcpy(repack, src, src_len);
+ chunk = repack;
+ }
+ ret = sunxi_hash_run_chunk(ce, &session, job, method, chunk, src_len,
+ pad_len, iv, state_len);
+out_close:
+ ret = sunxi_ce_session_close(&session, ret);
+out:
+ free(repack);
+
+ return ret;
+}
+
+static int sunxi_hash_digest(struct udevice *dev, enum HASH_ALGO hash_algo,
+ const void *ibuf, const uint32_t ilen, void *obuf)
+{
+ struct sunxi_ce_priv *ce = dev_get_priv(dev_get_parent(dev));
+ const struct sunxi_hash_alg *alg;
+ size_t src_len = ALIGN_DOWN(ilen, sizeof(u32));
+ size_t tail_len = ilen - src_len;
+ struct sunxi_hash_job *job;
+ const u8 *tail = ibuf;
+ size_t pad_len;
+ u8 digest_size, state_size;
+ u32 method;
+ int ret;
+
+ alg = sunxi_hash_get_alg(hash_algo);
+ if (!alg)
+ return -EOPNOTSUPP;
+ if ((!ibuf && ilen) || !obuf)
+ return -EINVAL;
+ if (ilen > UINTPTR_MAX - (uintptr_t)ibuf)
+ return -EOVERFLOW;
+ method = alg->method;
+ digest_size = alg->digest_size;
+ state_size = alg->state_size;
+
+ job = malloc_cache_aligned(sizeof(*job));
+ if (!job)
+ return -ENOMEM;
+
+ if (tail_len)
+ tail += src_len;
+
+ pad_len = sunxi_hash_pad(alg, job->pad, tail, tail_len, ilen);
+ ret = sunxi_hash_run_stream(ce, job, method, ibuf, src_len, pad_len,
+ state_size);
+ if (ret)
+ goto out;
+
+ memcpy(obuf, job->result, digest_size);
+
+out:
+ free(job);
+
+ return ret;
+}
+
+static int sunxi_hash_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
+ const void *ibuf, const uint32_t ilen,
+ void *obuf, uint32_t chunk_sz)
+{
+ return sunxi_hash_digest(dev, algo, ibuf, ilen, obuf);
+}
+
+static const struct hash_ops sunxi_hash_ops = {
+ .hash_digest = sunxi_hash_digest,
+ .hash_digest_wd = sunxi_hash_digest_wd,
+};
+
+U_BOOT_DRIVER(sun8i_ce_hash) = {
+ .name = "sun8i-ce-hash",
+ .id = UCLASS_HASH,
+ .ops = &sunxi_hash_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
index d5b7edd357d..b7bb899c5f0 100644
--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
@@ -21,6 +21,11 @@
#define SUNXI_CE_COMM_INT BIT(31)
#define SUNXI_CE_METHOD_AES 0
#define SUNXI_CE_METHOD_RAES 0x30
+#define SUNXI_CE_METHOD_MD5 16
+#define SUNXI_CE_METHOD_SHA1 17
+#define SUNXI_CE_METHOD_SHA256 19
+#define SUNXI_CE_METHOD_SHA384 20
+#define SUNXI_CE_METHOD_SHA512 21
#define SUNXI_CE_METHOD_ECC 33
#define SUNXI_CE_ECC_OP_VERIFY 7
#define SUNXI_CE_ECC_OP_SHIFT 16
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v5 14/14] crypto: allwinner: add sun8i-ce hash driver
2026-07-20 4:13 ` [PATCH v5 14/14] crypto: allwinner: add sun8i-ce hash driver James Hilliard
@ 2026-07-28 11:25 ` Simon Glass
0 siblings, 0 replies; 29+ messages in thread
From: Simon Glass @ 2026-07-28 11:25 UTC (permalink / raw)
To: james.hilliard1
Cc: Svyatoslav Ryhel, Ion Agorria, u-boot, Aspeed BMC SW team,
Joel Stanley, Chen-Yu Tsai, Samuel Holland, Tom Rini, Simon Glass,
Thierry Reding, Quentin Schulz, Quentin Schulz, Marek Vasut,
Marek Vasut, Rasmus Villemoes, Rasmus Villemoes, Aristo Chen,
Anton Ivanov, Daniel Golle, Francois Berder, Peng Fan,
Neil Armstrong, Randolph Sapp, Jonas Karlman, Wolfgang Wallner,
Alexey Charkov, Ilias Apalodimas, Heiko Schocher,
Kory Maincent (TI.com), Anshul Dalal, Johan Jonker,
Francesco Valla, Heinrich Schuchardt, Michael Walle,
Andre Przywara, Lukasz Majewski, Richard Genoud,
Michael Trimarchi, E Shattow, Enric Balletbo i Serra,
Mattijs Korpershoek, Lucas Dietrich, David Lechner,
Julien Stephan, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Ryan Chen, Chia-Wei Wang, Lucien.Jheng, Mateusz Furdyna,
Dinesh Maniyam, Heiko Stuebner, Vincent Jardin, u-boot
On 2026-07-20T04:13:44, James Hilliard <james.hilliard1@gmail.com> wrote:
> crypto: allwinner: add sun8i-ce hash driver
>
> The Allwinner sun8i Crypto Engine includes hardware hash methods. Add a
> UCLASS_HASH child for the sun8i-ce parent so FIT hash verification can
> use the accelerator from U-Boot proper and SPL.
>
> Support MD5, SHA1, SHA256, SHA384 and SHA512. Build the final hash padding
> in the driver. Stream directly addressable input through bounded 64 MiB
> zero-copy tasks and word-unaligned input through a fixed 128 KiB repack
> buffer. Feed each intermediate state back through hash input-IV mode,
> keeping heap use independent of input size while word-aligned cacheline
> offsets remain zero-copy.
>
> Map input, padding, optional state and a cacheline-rounded private output
> through the parent's DMA objects. Keep the descriptor output length limited
> to the algorithm state, then submit hash work on the dedicated completion
> channel through the shared task-session API. Keep one session across every
> continuation chunk while retiring each task before releasing its mappings.
> Release mappings in reverse preparation order after the session retires.
>
> H6 and H616 use bit-sized hash task lengths, so keep each submitted chunk
> within the 32-bit task descriptor field. Feed the watchdog from the shared
> CE polling path while waiting for hardware completion.
>
> In SPL, only advertise hash algorithms selected for that phase so
> SRAM-constrained builds do not accept wider algorithms unless requested.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
>
> drivers/crypto/allwinner/sun8i-ce/Kconfig | 26 ++
> drivers/crypto/allwinner/sun8i-ce/Makefile | 1 +
> drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 6 +
> drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c | 343 ++++++++++++++++++++++
> drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 5 +
> 5 files changed, 381 insertions(+)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 29+ messages in thread