[RFC PATCH V3 3/4] crypto: Add Xilinx SHA3 driver

From: Kalyani Akula
Date: Thu May 02 2019 - 06:35:31 EST


This patch adds SHA3 driver support for the Xilinx
ZynqMP SoC.

Signed-off-by: Kalyani Akula <kalyani.akula@xxxxxxxxxx>
---
drivers/crypto/Kconfig | 10 ++
drivers/crypto/Makefile | 1 +
drivers/crypto/zynqmp-sha.c | 240 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 251 insertions(+)
create mode 100644 drivers/crypto/zynqmp-sha.c

diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 0be55fc..d9647a0 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -667,6 +667,16 @@ config CRYPTO_DEV_ROCKCHIP
This driver interfaces with the hardware crypto accelerator.
Supporting cbc/ecb chainmode, and aes/des/des3_ede cipher mode.

+config CRYPTO_DEV_ZYNQMP_SHA3
+ tristate "Support for Xilinx ZynqMP SHA3 hw accelerator"
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ select CRYPTO_HASH
+ help
+ Xilinx ZynqMP has SHA3 engine used for secure hash calculation.
+ This driver interfaces with SHA3 hw engine.
+ Select this if you want to use the ZynqMP module
+ for SHA3 hash computation.
+
config CRYPTO_DEV_MEDIATEK
tristate "MediaTek's EIP97 Cryptographic Engine driver"
depends on (ARM && ARCH_MEDIATEK) || COMPILE_TEST
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 8e7e225..64c29fe 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -47,3 +47,4 @@ obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) += bcm/
obj-$(CONFIG_CRYPTO_DEV_SAFEXCEL) += inside-secure/
obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/
obj-y += hisilicon/
+obj-$(CONFIG_CRYPTO_DEV_ZYNQMP_SHA3) += zynqmp-sha.o
diff --git a/drivers/crypto/zynqmp-sha.c b/drivers/crypto/zynqmp-sha.c
new file mode 100644
index 0000000..e8efe17
--- /dev/null
+++ b/drivers/crypto/zynqmp-sha.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cryptographic API.
+ *
+ * Support for Xilinx ZynqMP SHA3 HW Acceleration.
+ *
+ * Copyright (c) 2018 Xilinx Inc.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+#include <linux/crypto.h>
+#include <linux/cryptohash.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/algapi.h>
+#include <crypto/sha.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+
+#define ZYNQMP_SHA3_INIT 1
+#define ZYNQMP_SHA3_UPDATE 2
+#define ZYNQMP_SHA3_FINAL 4
+
+static const struct zynqmp_eemi_ops *eemi_ops;
+struct zynqmp_sha_dev *sha_dd;
+
+struct zynqmp_sha_reqctx {
+ struct zynqmp_sha_dev *dd;
+ unsigned long flags;
+};
+
+struct zynqmp_sha_ctx {
+ struct zynqmp_sha_dev *dd;
+ unsigned long flags;
+};
+
+struct zynqmp_sha_dev {
+ struct device *dev;
+ int err;
+
+ unsigned long flags;
+ struct ahash_request *req;
+};
+
+static int zynqmp_sha_init(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct zynqmp_sha_ctx *tctx = crypto_ahash_ctx(tfm);
+ struct zynqmp_sha_reqctx *ctx = ahash_request_ctx(req);
+ struct zynqmp_sha_dev *dd = sha_dd;
+ int ret;
+
+ if (!eemi_ops->sha_hash)
+ return -ENOTSUPP;
+
+ if (!tctx->dd)
+ tctx->dd = dd;
+ else
+ dd = tctx->dd;
+
+ ctx->dd = dd;
+ dev_dbg(dd->dev, "init: digest size: %d\n",
+ crypto_ahash_digestsize(tfm));
+
+ ret = eemi_ops->sha_hash(0, 0, ZYNQMP_SHA3_INIT);
+
+ return ret;
+}
+
+static int zynqmp_sha_update(struct ahash_request *req)
+{
+ struct zynqmp_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+ struct zynqmp_sha_dev *dd = tctx->dd;
+ size_t dma_size = req->nbytes;
+ dma_addr_t dma_addr;
+ char *kbuf;
+ int ret;
+
+ if (!req->nbytes)
+ return 0;
+
+ if (!eemi_ops->sha_hash)
+ return -ENOTSUPP;
+
+ kbuf = dma_alloc_coherent(dd->dev, dma_size, &dma_addr, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ scatterwalk_map_and_copy(kbuf, req->src, 0, req->nbytes, 0);
+ ret = eemi_ops->sha_hash(dma_addr, req->nbytes, ZYNQMP_SHA3_UPDATE);
+ dma_free_coherent(dd->dev, dma_size, kbuf, dma_addr);
+
+ return ret;
+}
+
+static int zynqmp_sha_final(struct ahash_request *req)
+{
+ struct zynqmp_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+ struct zynqmp_sha_dev *dd = tctx->dd;
+ size_t dma_size = SHA384_DIGEST_SIZE;
+ dma_addr_t dma_addr;
+ char *kbuf;
+ int ret;
+
+ if (!eemi_ops->sha_hash)
+ return -ENOTSUPP;
+
+ kbuf = dma_alloc_coherent(dd->dev, dma_size, &dma_addr, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ ret = eemi_ops->sha_hash(dma_addr, dma_size, ZYNQMP_SHA3_FINAL);
+ memcpy(req->result, kbuf, SHA384_DIGEST_SIZE);
+ dma_free_coherent(dd->dev, dma_size, kbuf, dma_addr);
+
+ return ret;
+}
+
+static int zynqmp_sha_finup(struct ahash_request *req)
+{
+ zynqmp_sha_update(req);
+ zynqmp_sha_final(req);
+
+ return 0;
+}
+
+static int zynqmp_sha_digest(struct ahash_request *req)
+{
+ zynqmp_sha_init(req);
+ zynqmp_sha_update(req);
+ zynqmp_sha_final(req);
+
+ return 0;
+}
+
+static int zynqmp_sha_cra_init(struct crypto_tfm *tfm)
+{
+ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+ sizeof(struct zynqmp_sha_reqctx));
+
+ return 0;
+}
+
+static struct ahash_alg sha3_alg = {
+ .init = zynqmp_sha_init,
+ .update = zynqmp_sha_update,
+ .final = zynqmp_sha_final,
+ .finup = zynqmp_sha_finup,
+ .digest = zynqmp_sha_digest,
+ .halg = {
+ .digestsize = SHA384_DIGEST_SIZE,
+ .statesize = sizeof(struct sha256_state),
+ .base = {
+ .cra_name = "xilinx-sha3-384",
+ .cra_driver_name = "zynqmp-sha3-384",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct zynqmp_sha_ctx),
+ .cra_alignmask = 0,
+ .cra_module = THIS_MODULE,
+ .cra_init = zynqmp_sha_cra_init,
+ }
+ }
+};
+
+static const struct of_device_id zynqmp_sha_dt_ids[] = {
+ { .compatible = "xlnx,zynqmp-sha3-384" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, zynqmp_sha_dt_ids);
+
+static int zynqmp_sha_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int err;
+
+ eemi_ops = zynqmp_pm_get_eemi_ops();
+ if (IS_ERR(eemi_ops))
+ return PTR_ERR(eemi_ops);
+
+ sha_dd = devm_kzalloc(&pdev->dev, sizeof(*sha_dd), GFP_KERNEL);
+ if (!sha_dd)
+ return -ENOMEM;
+
+ sha_dd->dev = dev;
+ platform_set_drvdata(pdev, sha_dd);
+
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(44));
+ if (err < 0) {
+ dev_err(dev, "no usable DMA configuration");
+ goto err_algs;
+ }
+
+ err = crypto_register_ahash(&sha3_alg);
+ if (err)
+ goto err_algs;
+
+ return 0;
+
+err_algs:
+ return err;
+}
+
+static int zynqmp_sha_remove(struct platform_device *pdev)
+{
+ sha_dd = platform_get_drvdata(pdev);
+
+ if (!sha_dd)
+ return -ENODEV;
+
+ crypto_unregister_ahash(&sha3_alg);
+
+ return 0;
+}
+
+static struct platform_driver zynqmp_sha_driver = {
+ .probe = zynqmp_sha_probe,
+ .remove = zynqmp_sha_remove,
+ .driver = {
+ .name = "zynqmp-keccak-384",
+ .of_match_table = of_match_ptr(zynqmp_sha_dt_ids),
+ },
+};
+
+module_platform_driver(zynqmp_sha_driver);
+
+MODULE_DESCRIPTION("ZynqMP SHA3 hw acceleration support.");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nava kishore Manne <navam@xxxxxxxxxx>");
--
1.9.5