Message ID | 20210518070926.604053-1-joel@jms.id.au |
---|---|
State | New |
Headers | show |
Series | crypto: Add driver for Aspeed HACE | expand |
On 5/18/21 9:09 AM, Joel Stanley wrote: > The HACE supports MD5, SHA1 and SHA2 family hash functions. This driver > uses it in a polling mode to perform hash calculations over buffers > placed in DRAM. > > It registers a driver to allow the hardware's clocking to be enabled > using the clock framework. > > Co-developed-by: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> > Signed-off-by: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> > Signed-off-by: Joel Stanley <joel@jms.id.au> > --- > configs/evb-ast2600_defconfig | 1 + > drivers/crypto/Kconfig | 19 +++ > drivers/crypto/Makefile | 1 + > drivers/crypto/aspeed_hace.c | 256 ++++++++++++++++++++++++++++++++++ > 4 files changed, 277 insertions(+) > create mode 100644 drivers/crypto/aspeed_hace.c > > diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig > index 91518dbe3589..52ae8e423bfd 100644 > --- a/configs/evb-ast2600_defconfig > +++ b/configs/evb-ast2600_defconfig > @@ -45,6 +45,7 @@ CONFIG_REGMAP=y > CONFIG_SPL_OF_TRANSLATE=y > CONFIG_CLK=y > CONFIG_SPL_CLK=y > +CONFIG_ASPEED_HACE=y > CONFIG_DM_I2C=y > CONFIG_MISC=y > CONFIG_SPL_MISC=y > diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig > index 1ea116be7503..e92037d88906 100644 > --- a/drivers/crypto/Kconfig > +++ b/drivers/crypto/Kconfig > @@ -2,4 +2,23 @@ menu "Hardware crypto devices" > > source drivers/crypto/fsl/Kconfig > > +config ASPEED_HACE > + bool "ASPEED Hash and Crypto Engine" > + depends on ASPEED_AST2600 > + imply SHA_HW_ACCEL > + imply SHA_PROG_HW_ACCEL > + imply SHA512 > + imply SHA512_ALGO > + imply CMD_HASH > + help > + Select this option to enable a driver for using the SHA engine in > + the ASPEED BMC SoCs. > + > + Enabling this allows the use of SHA operations in hardware without requiring the > + SHA software implementations, saving code size. > + > + Due to hardware limitations it cannot be used with a FIT placed in SPI > + FLASH. Data can only be hashed if it is in SDRAM, making this relevant > + for MMC and network boot only. > + > endmenu > diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile > index efbd1d3fca05..ac93b1295954 100644 > --- a/drivers/crypto/Makefile > +++ b/drivers/crypto/Makefile > @@ -4,5 +4,6 @@ > # http://www.samsung.com > > obj-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.o > +obj-$(CONFIG_ASPEED_HACE) += aspeed_hace.o > obj-y += rsa_mod_exp/ > obj-y += fsl/ > diff --git a/drivers/crypto/aspeed_hace.c b/drivers/crypto/aspeed_hace.c > new file mode 100644 > index 000000000000..941c1555fe5c > --- /dev/null > +++ b/drivers/crypto/aspeed_hace.c > @@ -0,0 +1,256 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +// > +// (C) Copyright ASPEED Technology Inc. > +// Copyright 2021 IBM Corp. > + > +#include <common.h> > +#include <clk.h> > + > +#include <log.h> > +#include <asm/io.h> > +#include <malloc.h> > +#include <hash.h> > + > +#include <dm/device.h> > +#include <dm/fdtaddr.h> > + > +#include <linux/bitops.h> > +#include <linux/delay.h> > +#include <linux/kernel.h> > +#include <linux/iopoll.h> > + > +#define ASPEED_HACE_STS 0x1C > +#define HACE_RSA_ISR BIT(13) > +#define HACE_CRYPTO_ISR BIT(12) > +#define HACE_HASH_ISR BIT(9) > +#define HACE_RSA_BUSY BIT(2) > +#define HACE_CRYPTO_BUSY BIT(1) > +#define HACE_HASH_BUSY BIT(0) > +#define ASPEED_HACE_HASH_SRC 0x20 > +#define ASPEED_HACE_HASH_DIGEST_BUFF 0x24 > +#define ASPEED_HACE_HASH_KEY_BUFF 0x28 > +#define ASPEED_HACE_HASH_DATA_LEN 0x2C > +#define HACE_SG_LAST BIT(31) > +#define ASPEED_HACE_HASH_CMD 0x30 > +#define HACE_SHA_BE_EN BIT(3) > +#define HACE_MD5_LE_EN BIT(2) > +#define HACE_ALGO_MD5 0 > +#define HACE_ALGO_SHA1 BIT(5) > +#define HACE_ALGO_SHA224 BIT(6) > +#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) > +#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) > +#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) > +#define HACE_SG_EN BIT(18) > + > +#define ASPEED_MAX_SG 32 It would nice to have a struct aspeed_hace describing the register layout. > +struct aspeed_sg { > + u32 len; > + u32 addr; > +}; > + > +struct aspeed_hash_ctx { > + u32 method; > + u32 digest_size; > + u32 len; > + u32 count; > + struct aspeed_sg list[ASPEED_MAX_SG]; /* Must be 8 byte aligned */ > +}; Shouldn't we use __aligned(8); ? > +struct aspeed_hace { > + struct clk clk; > +}; > + > +static phys_addr_t base; > + > +static int aspeed_hace_wait_completion(u32 reg, u32 flag, int timeout_us) > +{ > + u32 val; > + > + return readl_poll_timeout(reg, val, (val & flag) == flag, timeout_us); > +} > + > +static int digest_object(const void *src, unsigned int length, void *digest, u32 method) > +{ > + if (!((u32)src & BIT(31))) { > + debug("HACE src out of bounds: can only copy from SDRAM\n"); > + return -EINVAL; > + } > + > + if ((u32)digest & 0x7) { > + debug("HACE dest alignment incorrect: %p\n", digest); > + return -EINVAL; > + } > + > + if (readl(base + ASPEED_HACE_STS) & HACE_HASH_BUSY) { > + debug("HACE error: engine busy\n"); > + return -EBUSY; > + } > + > + /* Clear pending completion status */ > + writel(HACE_HASH_ISR, base + ASPEED_HACE_STS); > + > + writel((u32)src, base + ASPEED_HACE_HASH_SRC); > + writel((u32)digest, base + ASPEED_HACE_HASH_DIGEST_BUFF); > + writel(length, base + ASPEED_HACE_HASH_DATA_LEN); > + writel(HACE_SHA_BE_EN | method, base + ASPEED_HACE_HASH_CMD); > + > + /* SHA512 hashing appears to have a througput of about 12MB/s */ > + return aspeed_hace_wait_completion(base + ASPEED_HACE_STS, > + HACE_HASH_ISR, > + 1000 + (length >> 3)); > +} > + > +void hw_sha1(const unsigned char *pbuf, unsigned int buf_len, > + unsigned char *pout, unsigned int chunk_size) > +{ > + int rc; > + > + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA1); > + if (rc) > + debug("HACE failure: %d\n", rc); > +} > + > +void hw_sha256(const unsigned char *pbuf, unsigned int buf_len, > + unsigned char *pout, unsigned int chunk_size) > +{ > + int rc; > + > + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA256); > + if (rc) > + debug("HACE failure: %d\n", rc); > +} > + > +void hw_sha512(const unsigned char *pbuf, unsigned int buf_len, > + unsigned char *pout, unsigned int chunk_size) > +{ > + int rc; > + > + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA512); > + if (rc) > + debug("HACE failure: %d\n", rc); > +} > + > +int hw_sha_init(struct hash_algo *algo, void **ctxp) > +{ > + struct aspeed_hash_ctx *ctx; > + u32 method; > + > + if (!strcmp(algo->name, "sha1")) > + method = HACE_ALGO_SHA1; > + else if (!strcmp(algo->name, "sha256")) > + method = HACE_ALGO_SHA256; > + else if (!strcmp(algo->name, "sha512")) > + method = HACE_ALGO_SHA512; > + else > + return -ENOTSUPP; > + } > + > + ctx = memalign(8, sizeof(*ctx)); ARCH_DMA_MINALIGN ? > + if (!ctx) { > + debug("HACE error: Cannot allocate memory for context\n"); > + return -ENOMEM; > + } > + > + memset(ctx, '\0', sizeof(*ctx)); > + > + if (((uintptr_t)ctx->list & 0x3) != 0) { > + printf("HACE error: Invalid alignment for input data\n"); > + return -EINVAL; > + } > + > + ctx->method = method | HACE_SG_EN; > + ctx->digest_size = algo->digest_size; > + *ctxp = ctx; > + > + return 0; > +} > + > +int hw_sha_update(struct hash_algo *algo, void *hash_ctx, const void *buf, > + unsigned int size, int is_last) > +{ > + struct aspeed_hash_ctx *ctx = hash_ctx; > + struct aspeed_sg *sg = &ctx->list[ctx->count]; > + > + if (ctx->count >= ARRAY_SIZE(ctx->list)) { > + debug("HACE error: Reached maximum number of hash segments\n"); > + free(ctx); > + return -EINVAL; > + } > + > + sg->addr = (u32)buf; > + sg->len = size; > + if (is_last) > + sg->len |= HACE_SG_LAST; > + > + ctx->count++; > + ctx->len += size; > + > + return 0; > +} > + > +int hw_sha_finish(struct hash_algo *algo, void *hash_ctx, void *dest_buf, int size) > +{ > + struct aspeed_hash_ctx *ctx = hash_ctx; > + int rc; > + > + if (size < ctx->digest_size) { > + debug("HACE error: insufficient size on destination buffer\n"); > + free(ctx); > + return -EINVAL; > + } > + > + rc = digest_object(ctx->list, ctx->len, dest_buf, ctx->method); > + if (rc) > + debug("HACE Scatter-Gather failure\n"); > + > + free(ctx); > + > + return rc; > +} > + > +static int aspeed_hace_probe(struct udevice *dev) > +{ > + struct aspeed_hace *hace = dev_get_priv(dev); > + int ret; > + > + ret = clk_get_by_index(dev, 0, &hace->clk); > + if (ret < 0) { > + debug("Can't get clock for %s: %d\n", dev->name, ret); > + return ret; > + } > + > + ret = clk_enable(&hace->clk); > + if (ret) { > + debug("Failed to enable fsi clock (%d)\n", ret); > + return ret; > + } > + > + /* As the crypto code does not pass us any driver state */ > + base = devfdt_get_addr(dev); > + > + return ret; > +} > + > +static int aspeed_hace_remove(struct udevice *dev) > +{ > + struct aspeed_hace *hace = dev_get_priv(dev); > + > + clk_disable(&hace->clk); > + > + return 0; > +} > + > +static const struct udevice_id aspeed_hace_ids[] = { > + { .compatible = "aspeed,ast2600-hace" }, > + { } > +}; > + > +U_BOOT_DRIVER(aspeed_hace) = { > + .name = "aspeed_hace", > + .id = UCLASS_MISC, > + .of_match = aspeed_hace_ids, > + .probe = aspeed_hace_probe, > + .remove = aspeed_hace_remove, > + .priv_auto = sizeof(struct aspeed_hace), > +}; >
Hi Cédric, On Tue, 18 May 2021 at 03:02, Cédric Le Goater <clg@kaod.org> wrote: > > On 5/18/21 9:09 AM, Joel Stanley wrote: > > The HACE supports MD5, SHA1 and SHA2 family hash functions. This driver > > uses it in a polling mode to perform hash calculations over buffers > > placed in DRAM. > > > > It registers a driver to allow the hardware's clocking to be enabled > > using the clock framework. > > > > Co-developed-by: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> > > Signed-off-by: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> > > Signed-off-by: Joel Stanley <joel@jms.id.au> > > --- > > configs/evb-ast2600_defconfig | 1 + > > drivers/crypto/Kconfig | 19 +++ > > drivers/crypto/Makefile | 1 + > > drivers/crypto/aspeed_hace.c | 256 ++++++++++++++++++++++++++++++++++ > > 4 files changed, 277 insertions(+) > > create mode 100644 drivers/crypto/aspeed_hace.c At some point we need to fix up this interface to use its own uclass, not misc. +Alexandru Gagniuc who is looking at something similar > > > > diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig > > index 91518dbe3589..52ae8e423bfd 100644 > > --- a/configs/evb-ast2600_defconfig > > +++ b/configs/evb-ast2600_defconfig > > @@ -45,6 +45,7 @@ CONFIG_REGMAP=y > > CONFIG_SPL_OF_TRANSLATE=y > > CONFIG_CLK=y > > CONFIG_SPL_CLK=y > > +CONFIG_ASPEED_HACE=y > > CONFIG_DM_I2C=y > > CONFIG_MISC=y > > CONFIG_SPL_MISC=y > > diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig > > index 1ea116be7503..e92037d88906 100644 > > --- a/drivers/crypto/Kconfig > > +++ b/drivers/crypto/Kconfig > > @@ -2,4 +2,23 @@ menu "Hardware crypto devices" > > > > source drivers/crypto/fsl/Kconfig > > > > +config ASPEED_HACE > > + bool "ASPEED Hash and Crypto Engine" > > + depends on ASPEED_AST2600 > > + imply SHA_HW_ACCEL > > + imply SHA_PROG_HW_ACCEL > > + imply SHA512 > > + imply SHA512_ALGO > > + imply CMD_HASH > > + help > > + Select this option to enable a driver for using the SHA engine in > > + the ASPEED BMC SoCs. > > + > > + Enabling this allows the use of SHA operations in hardware without requiring the > > + SHA software implementations, saving code size. > > + > > + Due to hardware limitations it cannot be used with a FIT placed in SPI > > + FLASH. Data can only be hashed if it is in SDRAM, making this relevant > > + for MMC and network boot only. > > + > > endmenu > > diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile > > index efbd1d3fca05..ac93b1295954 100644 > > --- a/drivers/crypto/Makefile > > +++ b/drivers/crypto/Makefile > > @@ -4,5 +4,6 @@ > > # http://www.samsung.com > > > > obj-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.o > > +obj-$(CONFIG_ASPEED_HACE) += aspeed_hace.o > > obj-y += rsa_mod_exp/ > > obj-y += fsl/ > > diff --git a/drivers/crypto/aspeed_hace.c b/drivers/crypto/aspeed_hace.c > > new file mode 100644 > > index 000000000000..941c1555fe5c > > --- /dev/null > > +++ b/drivers/crypto/aspeed_hace.c > > @@ -0,0 +1,256 @@ > > +// SPDX-License-Identifier: GPL-2.0-or-later > > +// > > +// (C) Copyright ASPEED Technology Inc. > > +// Copyright 2021 IBM Corp. > > + > > +#include <common.h> > > +#include <clk.h> > > + > > +#include <log.h> > > +#include <asm/io.h> > > +#include <malloc.h> > > +#include <hash.h> > > + > > +#include <dm/device.h> > > +#include <dm/fdtaddr.h> > > + > > +#include <linux/bitops.h> > > +#include <linux/delay.h> > > +#include <linux/kernel.h> > > +#include <linux/iopoll.h> > > + > > +#define ASPEED_HACE_STS 0x1C > > +#define HACE_RSA_ISR BIT(13) > > +#define HACE_CRYPTO_ISR BIT(12) > > +#define HACE_HASH_ISR BIT(9) > > +#define HACE_RSA_BUSY BIT(2) > > +#define HACE_CRYPTO_BUSY BIT(1) > > +#define HACE_HASH_BUSY BIT(0) > > +#define ASPEED_HACE_HASH_SRC 0x20 > > +#define ASPEED_HACE_HASH_DIGEST_BUFF 0x24 > > +#define ASPEED_HACE_HASH_KEY_BUFF 0x28 > > +#define ASPEED_HACE_HASH_DATA_LEN 0x2C > > +#define HACE_SG_LAST BIT(31) > > +#define ASPEED_HACE_HASH_CMD 0x30 > > +#define HACE_SHA_BE_EN BIT(3) > > +#define HACE_MD5_LE_EN BIT(2) > > +#define HACE_ALGO_MD5 0 > > +#define HACE_ALGO_SHA1 BIT(5) > > +#define HACE_ALGO_SHA224 BIT(6) > > +#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) > > +#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) > > +#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) > > +#define HACE_SG_EN BIT(18) > > + > > +#define ASPEED_MAX_SG 32 > > It would nice to have a struct aspeed_hace describing the register layout. > > > +struct aspeed_sg { > > + u32 len; > > + u32 addr; > > +}; > > + > > +struct aspeed_hash_ctx { Needs a comment...what is this struct for? > > + u32 method; > > + u32 digest_size; > > + u32 len; > > + u32 count; > > + struct aspeed_sg list[ASPEED_MAX_SG]; /* Must be 8 byte aligned */ comments for other struct members > > +}; > > > Shouldn't we use __aligned(8); ? > > > > +struct aspeed_hace { > > + struct clk clk; > > +}; > > + > > +static phys_addr_t base; > > + > > +static int aspeed_hace_wait_completion(u32 reg, u32 flag, int timeout_us) > > +{ > > + u32 val; > > + > > + return readl_poll_timeout(reg, val, (val & flag) == flag, timeout_us); > > +} > > + > > +static int digest_object(const void *src, unsigned int length, void *digest, u32 method) You should document the args and return values, including alignment constraints. > > +{ > > + if (!((u32)src & BIT(31))) { Can you use ulong instead of u32, so it will build on 64-bit machines? > > + debug("HACE src out of bounds: can only copy from SDRAM\n"); > > + return -EINVAL; > > + } > > + > > + if ((u32)digest & 0x7) { > > + debug("HACE dest alignment incorrect: %p\n", digest); > > + return -EINVAL; > > + } > > + > > + if (readl(base + ASPEED_HACE_STS) & HACE_HASH_BUSY) { > > + debug("HACE error: engine busy\n"); > > + return -EBUSY; > > + } > > + > > + /* Clear pending completion status */ > > + writel(HACE_HASH_ISR, base + ASPEED_HACE_STS); > > + > > + writel((u32)src, base + ASPEED_HACE_HASH_SRC); > > + writel((u32)digest, base + ASPEED_HACE_HASH_DIGEST_BUFF); > > + writel(length, base + ASPEED_HACE_HASH_DATA_LEN); > > + writel(HACE_SHA_BE_EN | method, base + ASPEED_HACE_HASH_CMD); > > + > > + /* SHA512 hashing appears to have a througput of about 12MB/s */ spelling > > + return aspeed_hace_wait_completion(base + ASPEED_HACE_STS, > > + HACE_HASH_ISR, > > + 1000 + (length >> 3)); > > +} > > + > > +void hw_sha1(const unsigned char *pbuf, unsigned int buf_len, > > + unsigned char *pout, unsigned int chunk_size) > > +{ > > + int rc; > > + > > + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA1); > > + if (rc) > > + debug("HACE failure: %d\n", rc); > > +} > > + > > +void hw_sha256(const unsigned char *pbuf, unsigned int buf_len, > > + unsigned char *pout, unsigned int chunk_size) > > +{ > > + int rc; > > + > > + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA256); > > + if (rc) > > + debug("HACE failure: %d\n", rc); > > +} > > + > > +void hw_sha512(const unsigned char *pbuf, unsigned int buf_len, > > + unsigned char *pout, unsigned int chunk_size) > > +{ > > + int rc; > > + > > + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA512); > > + if (rc) > > + debug("HACE failure: %d\n", rc); > > +} > > + > > +int hw_sha_init(struct hash_algo *algo, void **ctxp) > > +{ > > + struct aspeed_hash_ctx *ctx; > > + u32 method; > > + > > + if (!strcmp(algo->name, "sha1")) > > + method = HACE_ALGO_SHA1; > > + else if (!strcmp(algo->name, "sha256")) > > + method = HACE_ALGO_SHA256; > > + else if (!strcmp(algo->name, "sha512")) > > + method = HACE_ALGO_SHA512; > > + else > > + return -ENOTSUPP; > > + } > > + > > + ctx = memalign(8, sizeof(*ctx)); > > ARCH_DMA_MINALIGN ? If you use the priv_auto thing you can use the DM_FLAG_ALLOC_PRIV_DMA flag. > > > + if (!ctx) { > > + debug("HACE error: Cannot allocate memory for context\n"); > > + return -ENOMEM; > > + } > > + > > + memset(ctx, '\0', sizeof(*ctx)); > > + > > + if (((uintptr_t)ctx->list & 0x3) != 0) { Drop != 0 > > + printf("HACE error: Invalid alignment for input data\n"); > > + return -EINVAL; > > + } > > + > > + ctx->method = method | HACE_SG_EN; > > + ctx->digest_size = algo->digest_size; > > + *ctxp = ctx; > > + > > + return 0; > > +} > > + > > +int hw_sha_update(struct hash_algo *algo, void *hash_ctx, const void *buf, > > + unsigned int size, int is_last) > > +{ > > + struct aspeed_hash_ctx *ctx = hash_ctx; > > + struct aspeed_sg *sg = &ctx->list[ctx->count]; > > + > > + if (ctx->count >= ARRAY_SIZE(ctx->list)) { > > + debug("HACE error: Reached maximum number of hash segments\n"); > > + free(ctx); > > + return -EINVAL; > > + } > > + > > + sg->addr = (u32)buf; > > + sg->len = size; > > + if (is_last) > > + sg->len |= HACE_SG_LAST; > > + > > + ctx->count++; > > + ctx->len += size; > > + > > + return 0; > > +} > > + > > +int hw_sha_finish(struct hash_algo *algo, void *hash_ctx, void *dest_buf, int size) > > +{ > > + struct aspeed_hash_ctx *ctx = hash_ctx; > > + int rc; > > + > > + if (size < ctx->digest_size) { > > + debug("HACE error: insufficient size on destination buffer\n"); > > + free(ctx); > > + return -EINVAL; > > + } > > + > > + rc = digest_object(ctx->list, ctx->len, dest_buf, ctx->method); > > + if (rc) > > + debug("HACE Scatter-Gather failure\n"); > > + > > + free(ctx); > > + > > + return rc; > > +} > > + > > +static int aspeed_hace_probe(struct udevice *dev) > > +{ > > + struct aspeed_hace *hace = dev_get_priv(dev); > > + int ret; > > + > > + ret = clk_get_by_index(dev, 0, &hace->clk); > > + if (ret < 0) { > > + debug("Can't get clock for %s: %d\n", dev->name, ret); > > + return ret; > > + } > > + > > + ret = clk_enable(&hace->clk); > > + if (ret) { > > + debug("Failed to enable fsi clock (%d)\n", ret); > > + return ret; > > + } > > + > > + /* As the crypto code does not pass us any driver state */ > > + base = devfdt_get_addr(dev); > > + > > + return ret; > > +} > > + > > +static int aspeed_hace_remove(struct udevice *dev) > > +{ > > + struct aspeed_hace *hace = dev_get_priv(dev); > > + > > + clk_disable(&hace->clk); > > + > > + return 0; > > +} > > + > > +static const struct udevice_id aspeed_hace_ids[] = { > > + { .compatible = "aspeed,ast2600-hace" }, > > + { } > > +}; > > + > > +U_BOOT_DRIVER(aspeed_hace) = { > > + .name = "aspeed_hace", > > + .id = UCLASS_MISC, > > + .of_match = aspeed_hace_ids, > > + .probe = aspeed_hace_probe, > > + .remove = aspeed_hace_remove, > > + .priv_auto = sizeof(struct aspeed_hace), > > +}; > > > Regards, Simon
diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig index 91518dbe3589..52ae8e423bfd 100644 --- a/configs/evb-ast2600_defconfig +++ b/configs/evb-ast2600_defconfig @@ -45,6 +45,7 @@ CONFIG_REGMAP=y CONFIG_SPL_OF_TRANSLATE=y CONFIG_CLK=y CONFIG_SPL_CLK=y +CONFIG_ASPEED_HACE=y CONFIG_DM_I2C=y CONFIG_MISC=y CONFIG_SPL_MISC=y diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 1ea116be7503..e92037d88906 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -2,4 +2,23 @@ menu "Hardware crypto devices" source drivers/crypto/fsl/Kconfig +config ASPEED_HACE + bool "ASPEED Hash and Crypto Engine" + depends on ASPEED_AST2600 + imply SHA_HW_ACCEL + imply SHA_PROG_HW_ACCEL + imply SHA512 + imply SHA512_ALGO + imply CMD_HASH + help + Select this option to enable a driver for using the SHA engine in + the ASPEED BMC SoCs. + + Enabling this allows the use of SHA operations in hardware without requiring the + SHA software implementations, saving code size. + + Due to hardware limitations it cannot be used with a FIT placed in SPI + FLASH. Data can only be hashed if it is in SDRAM, making this relevant + for MMC and network boot only. + endmenu diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index efbd1d3fca05..ac93b1295954 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -4,5 +4,6 @@ # http://www.samsung.com obj-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.o +obj-$(CONFIG_ASPEED_HACE) += aspeed_hace.o obj-y += rsa_mod_exp/ obj-y += fsl/ diff --git a/drivers/crypto/aspeed_hace.c b/drivers/crypto/aspeed_hace.c new file mode 100644 index 000000000000..941c1555fe5c --- /dev/null +++ b/drivers/crypto/aspeed_hace.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// (C) Copyright ASPEED Technology Inc. +// Copyright 2021 IBM Corp. + +#include <common.h> +#include <clk.h> + +#include <log.h> +#include <asm/io.h> +#include <malloc.h> +#include <hash.h> + +#include <dm/device.h> +#include <dm/fdtaddr.h> + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/iopoll.h> + +#define ASPEED_HACE_STS 0x1C +#define HACE_RSA_ISR BIT(13) +#define HACE_CRYPTO_ISR BIT(12) +#define HACE_HASH_ISR BIT(9) +#define HACE_RSA_BUSY BIT(2) +#define HACE_CRYPTO_BUSY BIT(1) +#define HACE_HASH_BUSY BIT(0) +#define ASPEED_HACE_HASH_SRC 0x20 +#define ASPEED_HACE_HASH_DIGEST_BUFF 0x24 +#define ASPEED_HACE_HASH_KEY_BUFF 0x28 +#define ASPEED_HACE_HASH_DATA_LEN 0x2C +#define HACE_SG_LAST BIT(31) +#define ASPEED_HACE_HASH_CMD 0x30 +#define HACE_SHA_BE_EN BIT(3) +#define HACE_MD5_LE_EN BIT(2) +#define HACE_ALGO_MD5 0 +#define HACE_ALGO_SHA1 BIT(5) +#define HACE_ALGO_SHA224 BIT(6) +#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) +#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) +#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) +#define HACE_SG_EN BIT(18) + +#define ASPEED_MAX_SG 32 + +struct aspeed_sg { + u32 len; + u32 addr; +}; + +struct aspeed_hash_ctx { + u32 method; + u32 digest_size; + u32 len; + u32 count; + struct aspeed_sg list[ASPEED_MAX_SG]; /* Must be 8 byte aligned */ +}; + +struct aspeed_hace { + struct clk clk; +}; + +static phys_addr_t base; + +static int aspeed_hace_wait_completion(u32 reg, u32 flag, int timeout_us) +{ + u32 val; + + return readl_poll_timeout(reg, val, (val & flag) == flag, timeout_us); +} + +static int digest_object(const void *src, unsigned int length, void *digest, u32 method) +{ + if (!((u32)src & BIT(31))) { + debug("HACE src out of bounds: can only copy from SDRAM\n"); + return -EINVAL; + } + + if ((u32)digest & 0x7) { + debug("HACE dest alignment incorrect: %p\n", digest); + return -EINVAL; + } + + if (readl(base + ASPEED_HACE_STS) & HACE_HASH_BUSY) { + debug("HACE error: engine busy\n"); + return -EBUSY; + } + + /* Clear pending completion status */ + writel(HACE_HASH_ISR, base + ASPEED_HACE_STS); + + writel((u32)src, base + ASPEED_HACE_HASH_SRC); + writel((u32)digest, base + ASPEED_HACE_HASH_DIGEST_BUFF); + writel(length, base + ASPEED_HACE_HASH_DATA_LEN); + writel(HACE_SHA_BE_EN | method, base + ASPEED_HACE_HASH_CMD); + + /* SHA512 hashing appears to have a througput of about 12MB/s */ + return aspeed_hace_wait_completion(base + ASPEED_HACE_STS, + HACE_HASH_ISR, + 1000 + (length >> 3)); +} + +void hw_sha1(const unsigned char *pbuf, unsigned int buf_len, + unsigned char *pout, unsigned int chunk_size) +{ + int rc; + + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA1); + if (rc) + debug("HACE failure: %d\n", rc); +} + +void hw_sha256(const unsigned char *pbuf, unsigned int buf_len, + unsigned char *pout, unsigned int chunk_size) +{ + int rc; + + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA256); + if (rc) + debug("HACE failure: %d\n", rc); +} + +void hw_sha512(const unsigned char *pbuf, unsigned int buf_len, + unsigned char *pout, unsigned int chunk_size) +{ + int rc; + + rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA512); + if (rc) + debug("HACE failure: %d\n", rc); +} + +int hw_sha_init(struct hash_algo *algo, void **ctxp) +{ + struct aspeed_hash_ctx *ctx; + u32 method; + + if (!strcmp(algo->name, "sha1")) + method = HACE_ALGO_SHA1; + else if (!strcmp(algo->name, "sha256")) + method = HACE_ALGO_SHA256; + else if (!strcmp(algo->name, "sha512")) + method = HACE_ALGO_SHA512; + else + return -ENOTSUPP; + } + + ctx = memalign(8, sizeof(*ctx)); + if (!ctx) { + debug("HACE error: Cannot allocate memory for context\n"); + return -ENOMEM; + } + + memset(ctx, '\0', sizeof(*ctx)); + + if (((uintptr_t)ctx->list & 0x3) != 0) { + printf("HACE error: Invalid alignment for input data\n"); + return -EINVAL; + } + + ctx->method = method | HACE_SG_EN; + ctx->digest_size = algo->digest_size; + *ctxp = ctx; + + return 0; +} + +int hw_sha_update(struct hash_algo *algo, void *hash_ctx, const void *buf, + unsigned int size, int is_last) +{ + struct aspeed_hash_ctx *ctx = hash_ctx; + struct aspeed_sg *sg = &ctx->list[ctx->count]; + + if (ctx->count >= ARRAY_SIZE(ctx->list)) { + debug("HACE error: Reached maximum number of hash segments\n"); + free(ctx); + return -EINVAL; + } + + sg->addr = (u32)buf; + sg->len = size; + if (is_last) + sg->len |= HACE_SG_LAST; + + ctx->count++; + ctx->len += size; + + return 0; +} + +int hw_sha_finish(struct hash_algo *algo, void *hash_ctx, void *dest_buf, int size) +{ + struct aspeed_hash_ctx *ctx = hash_ctx; + int rc; + + if (size < ctx->digest_size) { + debug("HACE error: insufficient size on destination buffer\n"); + free(ctx); + return -EINVAL; + } + + rc = digest_object(ctx->list, ctx->len, dest_buf, ctx->method); + if (rc) + debug("HACE Scatter-Gather failure\n"); + + free(ctx); + + return rc; +} + +static int aspeed_hace_probe(struct udevice *dev) +{ + struct aspeed_hace *hace = dev_get_priv(dev); + int ret; + + ret = clk_get_by_index(dev, 0, &hace->clk); + if (ret < 0) { + debug("Can't get clock for %s: %d\n", dev->name, ret); + return ret; + } + + ret = clk_enable(&hace->clk); + if (ret) { + debug("Failed to enable fsi clock (%d)\n", ret); + return ret; + } + + /* As the crypto code does not pass us any driver state */ + base = devfdt_get_addr(dev); + + return ret; +} + +static int aspeed_hace_remove(struct udevice *dev) +{ + struct aspeed_hace *hace = dev_get_priv(dev); + + clk_disable(&hace->clk); + + return 0; +} + +static const struct udevice_id aspeed_hace_ids[] = { + { .compatible = "aspeed,ast2600-hace" }, + { } +}; + +U_BOOT_DRIVER(aspeed_hace) = { + .name = "aspeed_hace", + .id = UCLASS_MISC, + .of_match = aspeed_hace_ids, + .probe = aspeed_hace_probe, + .remove = aspeed_hace_remove, + .priv_auto = sizeof(struct aspeed_hace), +};