From: Chia-Wei Wang Date: Fri, 30 Aug 2024 07:23:34 +0000 (+0800) Subject: drivers/crypto: aspeed: Add Caliptra SHA ACC support X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=fca70d61817b9ad2fb4b4821b029e55f1945997b;p=u-boot.git drivers/crypto: aspeed: Add Caliptra SHA ACC support Aspeed AST27xx SoCs integrate the CPTRA 1.0 secure IP, which export a SHA accelerator interface for SoC to use. Note that CPTRA 1.0 supports only SHA384 and SHA512 and this patch is verified by the 'hash test sha384/sha512' commands. Signed-off-by: Chia-Wei Wang --- diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig index 9bf317177a..473e3e5a86 100644 --- a/drivers/crypto/aspeed/Kconfig +++ b/drivers/crypto/aspeed/Kconfig @@ -18,3 +18,13 @@ config ASPEED_ACRY Enabling this allows the use of RSA/ECC operations in hardware without requiring the software implementations. It also improves performance and saves code size. + +config ASPEED_CPTRA_SHA + bool "Caliptra SHA ACC for Aspeed AST27xx SoCs" + depends on DM_HASH + help + Select this option to enable a driver for using the SHA accelerator provided + by Caliptra 1.0, which is integrated in AST27xx BMC SoCs. + + Enabling this allows the use of SHA operations in hardware. Note that only + SHA384 and SHA512 are supported by Caliptra 1.0. diff --git a/drivers/crypto/aspeed/Makefile b/drivers/crypto/aspeed/Makefile index 58b55fc46e..570587e744 100644 --- a/drivers/crypto/aspeed/Makefile +++ b/drivers/crypto/aspeed/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_ASPEED_HACE) += aspeed_hace.o obj-$(CONFIG_ASPEED_ACRY) += aspeed_acry.o +obj-$(CONFIG_ASPEED_CPTRA_SHA) += cptra_sha.o diff --git a/drivers/crypto/aspeed/cptra_sha.c b/drivers/crypto/aspeed/cptra_sha.c new file mode 100644 index 0000000000..26b97bdd92 --- /dev/null +++ b/drivers/crypto/aspeed/cptra_sha.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2024 ASPEED Technology Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SHA register offsets */ +#define CPTRA_SHA_LOCK 0x00 +#define CPTRA_SHA_USER 0x04 +#define CPTRA_SHA_MODE 0x08 +#define CPTRA_SHA_MODE_ENDIAN BIT(2) +#define CPTRA_SHA_MODE_SEL GENMASK(1, 0) +#define CPTRA_SHA_DLEN 0x10 +#define CPTRA_SHA_DATAIN 0x14 +#define CPTRA_SHA_EXEC 0x18 +#define CPTRA_SHA_STS 0x1c +#define CPTRA_SHA_STS_SOC_LOCK BIT(1) +#define CPTRA_SHA_STS_VLD BIT(0) +#define CPTRA_SHA_DIGEST(n) (0x20 + ((n) << 2)) +#define CPTRA_SHA_CTRL 0x60 +#define CPTRA_SHA_CTRL_ZEROIZE BIT(0) + +enum cptra_sha_modes { + CPTRA_SHA384_STREAM, + CPTRA_SHA512_STREAM, +}; + +struct cptra_sha_ctx { + enum HASH_ALGO algo; + uint32_t dgst_len; +}; + +struct cptra_sha { + void *regs; +}; + +static int cptra_sha_init(struct udevice *dev, enum HASH_ALGO algo, void **ctxp) +{ + struct cptra_sha_ctx *cs_ctx; + struct cptra_sha *cs; + uint32_t mode; + uint32_t reg; + int rc; + + cs_ctx = malloc(sizeof(struct cptra_sha_ctx)); + if (!cs_ctx) + return -ENOMEM; + + memset(cs_ctx, 0, sizeof(struct cptra_sha_ctx)); + + cs_ctx->algo = algo; + + switch (algo) { + case HASH_ALGO_SHA384: + mode = CPTRA_SHA384_STREAM; + cs_ctx->dgst_len = 48; + break; + case HASH_ALGO_SHA512: + mode = CPTRA_SHA512_STREAM; + cs_ctx->dgst_len = 64; + break; + default: + rc = -EINVAL; + goto free_n_out; + }; + + cs = dev_get_priv(dev); + + /* get CPTRA SHA lock */ + if (readl_poll_timeout(cs->regs + CPTRA_SHA_LOCK, reg, reg == 0, 1000000)) + return -EBUSY; + + /* zero clear SHA */ + writel(CPTRA_SHA_CTRL_ZEROIZE, cs->regs + CPTRA_SHA_CTRL); + + /* zero clear length */ + writel(0x0, cs->regs + CPTRA_SHA_DLEN); + + /* set SHA mode */ + reg = readl(cs->regs + CPTRA_SHA_MODE); + reg &= ~(CPTRA_SHA_MODE_SEL); + reg |= FIELD_PREP(CPTRA_SHA_MODE_SEL, mode); + writel(reg, cs->regs + CPTRA_SHA_MODE); + + *ctxp = cs_ctx; + + return 0; + +free_n_out: + free(cs_ctx); + + return rc; +} + +static int cptra_sha_update(struct udevice *dev, void *ctx, const void *ibuf, uint32_t ilen) +{ + struct cptra_sha *cs; + uint32_t din_be; + uint32_t dlen_sum; + uint8_t *p8; + uint32_t i; + + cs = dev_get_priv(dev); + + /* update length */ + dlen_sum = readl(cs->regs + CPTRA_SHA_DLEN) + ilen; + writel(dlen_sum, cs->regs + CPTRA_SHA_DLEN); + + din_be = 0; + for (i = 0, p8 = (uint8_t *)ibuf; i < ilen; ++i) { + if (i && (i % sizeof(din_be) == 0)) { + writel(din_be, cs->regs + CPTRA_SHA_DATAIN); + din_be = 0; + } + + din_be <<= 8; + din_be |= p8[i]; + } + + if (i % sizeof(din_be)) + din_be <<= (8 * (sizeof(din_be) - (i % sizeof(din_be)))); + + writel(din_be, cs->regs + CPTRA_SHA_DATAIN); + + return 0; +} + +static int cptra_sha_finish(struct udevice *dev, void *ctx, void *obuf) +{ + struct cptra_sha_ctx *cs_ctx; + struct cptra_sha *cs; + uint32_t i, *p32; + uint32_t sts; + + cs = dev_get_priv(dev); + cs_ctx = (struct cptra_sha_ctx *)ctx; + + /* trigger SHA calculation */ + writel(0x1, cs->regs + CPTRA_SHA_EXEC); + + /* wait for completion */ + while (1) { + sts = readl(cs->regs + CPTRA_SHA_STS); + if (sts & CPTRA_SHA_STS_VLD) + break; + } + + /* get the SHA digest in big-endian */ + p32 = (uint32_t *)obuf; + for (i = 0; i < (cs_ctx->dgst_len / sizeof(*p32)); ++i, p32++) + *p32 = be32_to_cpu(readl(cs->regs + CPTRA_SHA_DIGEST(i))); + + /* release CPTRA SHA lock */ + writel(0x1, cs->regs + CPTRA_SHA_LOCK); + + free(cs_ctx); + + return 0; +} + +static int cptra_sha_digest_wd(struct udevice *dev, enum HASH_ALGO algo, + const void *ibuf, const uint32_t ilen, + void *obuf, uint32_t chunk_sz) +{ + const void *cur, *end; + uint32_t chunk; + void *ctx; + int rc; + + rc = cptra_sha_init(dev, algo, &ctx); + if (rc) + return rc; + + if (IS_ENABLED(CONFIG_HW_WATCHDOG) || CONFIG_IS_ENABLED(WATCHDOG)) { + cur = ibuf; + end = ibuf + ilen; + + while (cur < end) { + chunk = end - cur; + if (chunk > chunk_sz) + chunk = chunk_sz; + + rc = cptra_sha_update(dev, ctx, cur, chunk); + if (rc) + return rc; + + cur += chunk; + schedule(); + } + } else { + rc = cptra_sha_update(dev, ctx, ibuf, ilen); + if (rc) + return rc; + } + + rc = cptra_sha_finish(dev, ctx, obuf); + if (rc) + return rc; + + return 0; +} + +static int cptra_sha_digest(struct udevice *dev, enum HASH_ALGO algo, + const void *ibuf, const uint32_t ilen, void *obuf) +{ + /* re-use the watchdog version with input length as the chunk_sz */ + return cptra_sha_digest_wd(dev, algo, ibuf, ilen, obuf, ilen); +} + +static int cptra_sha_probe(struct udevice *dev) +{ + struct cptra_sha *cs = dev_get_priv(dev); + + cs->regs = (void *)devfdt_get_addr(dev); + if (cs->regs == (void *)FDT_ADDR_T_NONE) { + debug("cannot map Caliptra SHA ACC registers\n"); + return -ENODEV; + } + + return 0; +} + +static int cptra_sha_remove(struct udevice *dev) +{ + return 0; +} + +static const struct hash_ops cptra_sha_ops = { + .hash_init = cptra_sha_init, + .hash_update = cptra_sha_update, + .hash_finish = cptra_sha_finish, + .hash_digest_wd = cptra_sha_digest_wd, + .hash_digest = cptra_sha_digest, +}; + +static const struct udevice_id cptra_sha_ids[] = { + { .compatible = "aspeed,ast2700-cptra-sha" }, + { } +}; + +U_BOOT_DRIVER(aspeed_cptra_sha) = { + .name = "aspeed_cptra_sha", + .id = UCLASS_HASH, + .of_match = cptra_sha_ids, + .ops = &cptra_sha_ops, + .probe = cptra_sha_probe, + .remove = cptra_sha_remove, + .priv_auto = sizeof(struct cptra_sha), + .flags = DM_FLAG_PRE_RELOC, +};