]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
drivers/crypto: aspeed: Add Caliptra SHA ACC support
authorChia-Wei Wang <chiawei_wang@aspeedtech.com>
Fri, 30 Aug 2024 07:23:34 +0000 (15:23 +0800)
committerTom Rini <trini@konsulko.com>
Mon, 16 Sep 2024 22:37:17 +0000 (16:37 -0600)
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 <chiawei_wang@aspeedtech.com>
drivers/crypto/aspeed/Kconfig
drivers/crypto/aspeed/Makefile
drivers/crypto/aspeed/cptra_sha.c [new file with mode: 0644]

index 9bf317177aad54889294f8489102d2a25b4aa553..473e3e5a86339e71b2f0199387fdb15f1fd9aff8 100644 (file)
@@ -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.
index 58b55fc46e4a6d57893b402ce5702b3ebc58ebb1..570587e744fbd404e3a8b284d10f2e9cd1942ae8 100644 (file)
@@ -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 (file)
index 0000000..26b97bd
--- /dev/null
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2024 ASPEED Technology Inc.
+ */
+#include <asm/io.h>
+#include <config.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <malloc.h>
+#include <u-boot/hash.h>
+#include <watchdog.h>
+
+/* 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,
+};