All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] Freescale DCP crypto acceleration
@ 2013-05-08 18:41 Tobias Rauter
  2013-05-08 18:41 ` [PATCH 1/1] crypto: Added support for Freescale's DCP co-processor Tobias Rauter
  2013-05-10  6:27 ` [RFC] Freescale DCP crypto acceleration Christoph G. Baumann
  0 siblings, 2 replies; 3+ messages in thread
From: Tobias Rauter @ 2013-05-08 18:41 UTC (permalink / raw)
  To: Tobias Rauter, linux-crypto, Herbert Xu; +Cc: Christoph G. Baumann

Hi!

I am working on a driver for the data co processor of freescale's
imx28 (should also work on mx23 and mx5).  I noticed that Christoph
sent a patch for this device some days ago to the crypto mailing
list. As far as I see this is basically the Freescale-SDK driver with
some modifications for mainline. This was also my first
approach. Since this driver has some problems (duplicated code, mutex in
atomic context) I decided to get the reference manual and write the
driver from scratch. To prevent double work (in case Christoph or
someone else does the same thing), I want to share my work here now.

This is my first real kernel driver so it would be nice if someone
could take some time to find the noob-mistakes and send me feedback.

Things I'm working on:
- get rid of the global device
- at the moment the number of entries of the in/out scatterlist is
  limited to 20. I am working on a ringbuffer where the CPU fills the
  co-processors buffer while it iterates through the pages.
- overall performance
- ATM each scatterlist entry of the request has to be aligned to
  AES_BLOCK_SIZE.  Since I was not able to find a documentation, I
  want to ask if my idea is the right approach to solve this problem:
  use a ablkcipher walk ,iterate through this walk and do
  dma_map_single for each buffer instead of the whole scatterlist.

regards,
Tobias

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/1] crypto: Added support for Freescale's DCP co-processor
  2013-05-08 18:41 [RFC] Freescale DCP crypto acceleration Tobias Rauter
@ 2013-05-08 18:41 ` Tobias Rauter
  2013-05-10  6:27 ` [RFC] Freescale DCP crypto acceleration Christoph G. Baumann
  1 sibling, 0 replies; 3+ messages in thread
From: Tobias Rauter @ 2013-05-08 18:41 UTC (permalink / raw)
  To: Tobias Rauter, linux-crypto, Herbert Xu
  Cc: Christoph G. Baumann, Tobias Rauter


Signed-off-by: Tobias Rauter <tobias.rauter@gmail.com>
---
 arch/arm/boot/dts/imx28.dtsi |   2 +-
 drivers/crypto/Kconfig       |  10 +
 drivers/crypto/Makefile      |   1 +
 drivers/crypto/dcp.c         | 752 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 764 insertions(+), 1 deletion(-)
 create mode 100644 drivers/crypto/dcp.c

diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index 7ba4966..cd428d6 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -666,7 +666,7 @@
 			dcp@80028000 {
 				reg = <0x80028000 0x2000>;
 				interrupts = <52 53 54>;
-				status = "disabled";
+				compatible = "fsl-dcp";
 			};
 
 			pxp@8002a000 {
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 87ec4d0..1f98eca 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -276,6 +276,16 @@ config CRYPTO_DEV_PICOXCELL
 
 	  Saying m here will build a module named pipcoxcell_crypto.
 
+config CRYPTO_DEV_DCP
+	tristate "Support for the DCP engine"
+	depends on ARCH_MXS && OF
+	select CRYPTO_BLKCIPHER
+	select CRYPTO_AES
+	select CRYPTO_CBC
+	help
+	  This options enables support for the hardware crypto-acceleration
+	  capabilities of the DCP co-processor
+
 config CRYPTO_DEV_S5P
 	tristate "Support for Samsung S5PV210 crypto accelerator"
 	depends on ARCH_S5PV210
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 880a47b..603b92e 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
 obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o
 obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o
 obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o
+obj-$(CONFIG_CRYPTO_DEV_DCP) += dcp.o
 obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o
 obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o
 obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
diff --git a/drivers/crypto/dcp.c b/drivers/crypto/dcp.c
new file mode 100644
index 0000000..ad0bae5
--- /dev/null
+++ b/drivers/crypto/dcp.c
@@ -0,0 +1,752 @@
+/*
+ * Cryptographic API.
+ *
+ * Support for DCP cryptographic accelerator.
+ *
+ * Copyright (c) 2013
+ * Author: Tobias Rauter <tobias.rauter@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Based on tegra-aes.c, dcp.c (from freescale SDK) and sahara.c
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/crypto.h>
+
+#include <crypto/scatterwalk.h>
+#include <crypto/aes.h>
+
+
+/* DCP CHANNEL USED FOR AES */
+#define USED_CHANNEL 1
+#define DCP_MAX_PKG 20
+
+/* Control Register */
+#define DCP_REG_CTRL 0x000
+#define DCP_CTRL_SFRST (1<<31)
+#define DCP_CTRL_CLKGATE (1<<30)
+#define DCP_CTRL_CRYPTO_PRESENT (1<<29)
+#define DCP_CTRL_SHA_PRESENT (1<<28)
+#define DCP_CTRL_GATHER_RES_WRITE (1<<23)
+#define DCP_CTRL_ENABLE_CONTEXT_CACHE (1<<22)
+#define DCP_CTRL_ENABLE_CONTEXT_SWITCH (1<<21)
+#define DCP_CTRL_CH_IRQ_E_0 0x01
+#define DCP_CTRL_CH_IRQ_E_1 0x02
+#define DCP_CTRL_CH_IRQ_E_2 0x04
+#define DCP_CTRL_CH_IRQ_E_3 0x08
+
+/* Status register */
+#define DCP_REG_STAT 0x010
+#define DCP_STAT_OTP_KEY_READY (1<<28)
+#define DCP_STAT_CUR_CHANNEL(stat) ((stat>>24)&0x0F)
+#define DCP_STAT_READY_CHANNEL(stat) ((stat>>16)&0x0F)
+#define DCP_STAT_IRQ(stat) (stat&0x0F)
+#define DCP_STAT_CHAN_0 (0x01)
+#define DCP_STAT_CHAN_1 (0x02)
+#define DCP_STAT_CHAN_2 (0x04)
+#define DCP_STAT_CHAN_3 (0x08)
+
+/* Channel Control Register */
+#define DCP_REG_CHAN_CTRL 0x020
+#define DCP_CHAN_CTRL_CH0_IRQ_MERGED (1<<16)
+#define DCP_CHAN_CTRL_HIGH_PRIO_0 (0x0100)
+#define DCP_CHAN_CTRL_HIGH_PRIO_1 (0x0200)
+#define DCP_CHAN_CTRL_HIGH_PRIO_2 (0x0400)
+#define DCP_CHAN_CTRL_HIGH_PRIO_3 (0x0800)
+#define DCP_CHAN_CTRL_ENABLE_0 (0x01)
+#define DCP_CHAN_CTRL_ENABLE_1 (0x02)
+#define DCP_CHAN_CTRL_ENABLE_2 (0x04)
+#define DCP_CHAN_CTRL_ENABLE_3 (0x08)
+
+/*
+ * Channel Registers:
+ * The DCP has 4 channels. Each of this channels
+ * has 4 registers (command pointer, semaphore, status and options).
+ * The address of register REG of channel CHAN is obtained by
+ * dcp_chan_reg(REG, CHAN)
+ */
+#define DCP_REG_CHAN_PTR	0x00000100
+#define DCP_REG_CHAN_SEMA	0x00000110
+#define DCP_REG_CHAN_STAT	0x00000120
+#define DCP_REG_CHAN_OPT	0x00000130
+
+#define DCP_CHAN_STAT_NEXT_CHAIN_IS_0	0x010000
+#define DCP_CHAN_STAT_NO_CHAIN		0x020000
+#define DCP_CHAN_STAT_CONTEXT_ERROR	0x030000
+#define DCP_CHAN_STAT_PAYLOAD_ERROR	0x040000
+#define DCP_CHAN_STAT_INVALID_MODE	0x050000
+#define DCP_CHAN_STAT_PAGEFAULT		0x40
+#define DCP_CHAN_STAT_DST		0x20
+#define DCP_CHAN_STAT_SRC		0x10
+#define DCP_CHAN_STAT_PACKET		0x08
+#define DCP_CHAN_STAT_SETUP		0x04
+#define DCP_CHAN_STAT_MISMATCH		0x02
+
+/* hw packet control*/
+
+#define DCP_PKT_PAYLOAD_KEY	(1<<11)
+#define DCP_PKT_CIPHER_INIT	(1<<9)
+#define DCP_PKG_CIPHER_ENCRYPT	(1<<8)
+#define DCP_PKT_CIPHER_ENABLE	(1<<5)
+#define DCP_PKT_DECR_SEM	(1<<1)
+#define DCP_PKT_CHAIN		(1<<2)
+#define DCP_PKT_IRQ		1
+
+#define CIPHER_MODE_CBC (1<<4)
+
+/* cipher flags */
+#define DCP_ENC		0x0001
+#define DCP_DEC		0x0002
+#define DCP_ECB		0x0004
+#define DCP_CBC		0x0008
+#define DCP_CBC_INIT	0x0010
+#define DCP_NEW_KEY	0x0020
+#define DCP_AES		0x1000
+
+/* clock defines */
+#define CLOCK_ON	1
+#define CLOCK_OFF	0
+
+struct dcp_dev_req_ctx {
+	int mode;
+};
+
+struct dcp_op {
+	unsigned int flags;
+	u8 key[AES_KEYSIZE_128];
+	int keylen;
+
+	struct scatterlist *in_sg;
+	struct scatterlist *out_sg;
+	int sg_in_nents;
+	int sg_out_nents;
+
+	void *iv;
+	int nbytes;
+
+	struct crypto_ablkcipher *fallback;
+};
+
+struct dcp_dev {
+	struct device *dev;
+	void __iomem *dcp_regs_base;
+
+	int dcp_vmi_irq;
+	int dcp_irq;
+
+	spinlock_t queue_lock;
+	struct crypto_queue queue;
+
+	struct mutex op_mutex;
+	struct completion op_completion;
+	struct dcp_dev_hw_packet *hw_pkg[DCP_MAX_PKG];
+	dma_addr_t hw_phys_pkg[DCP_MAX_PKG];
+
+	/* [KEY][IV] Both with 16 Bytes*/
+	u8 *key_iv_base;
+	dma_addr_t key_iv_phys_base;
+
+	struct dcp_op *ctx;
+};
+
+struct dcp_dev_hw_packet {
+	uint32_t next;
+	uint32_t pkt1;
+	uint32_t pkt2;
+	uint32_t src;
+	uint32_t dst;
+	uint32_t size;
+	uint32_t payload;
+	uint32_t stat;
+};
+
+/*FIXME: get rid of globals */
+static void dcp_workqueue_handler(struct work_struct *work);
+static DECLARE_WORK(dcp_work, dcp_workqueue_handler);
+static struct workqueue_struct *dcp_wq;
+struct dcp_dev *global_dev;
+
+static inline u32 dcp_chan_reg(u32 reg, int chan)
+{
+	return reg + (chan) * 0x40;
+}
+
+static inline void dcp_write(struct dcp_dev *dev, u32 data, u32 reg)
+{
+	writel(data, dev->dcp_regs_base + reg);
+}
+
+static inline void dcp_set(struct dcp_dev *dev, u32 data, u32 reg)
+{
+	writel(data, dev->dcp_regs_base + (reg | 0x04));
+}
+
+static inline void dcp_clear(struct dcp_dev *dev, u32 data, u32 reg)
+{
+	writel(data, dev->dcp_regs_base + (reg | 0x08));
+}
+
+static inline void dcp_toggle(struct dcp_dev *dev, u32 data, u32 reg)
+{
+	writel(data, dev->dcp_regs_base + (reg | 0x0C));
+}
+
+static inline unsigned int dcp_read(struct dcp_dev *dev, u32 reg)
+{
+	return readl(dev->dcp_regs_base + reg);
+}
+
+static int dcp_perform_op(struct dcp_dev *dev, int nbytes)
+{
+	struct dcp_op *ctx = dev->ctx;
+	struct dcp_dev_hw_packet *pkt;
+	struct scatterlist *in_sg, *out_sg;
+	int in_off, out_off;
+	int act_packet = 0;
+	u32 pkt1, pkt2;
+	u32 stat = 0;
+
+	if (ctx->flags & DCP_NEW_KEY) {
+		memcpy(dev->key_iv_base, ctx->key, ctx->keylen);
+		ctx->flags &= ~DCP_NEW_KEY;
+	}
+
+	pkt1 = 0;
+	pkt1 |= DCP_PKT_CIPHER_ENABLE;
+	pkt1 |= DCP_PKT_PAYLOAD_KEY;
+
+	if (ctx->flags & DCP_ENC)
+		pkt1 |= DCP_PKG_CIPHER_ENCRYPT;
+
+	pkt2 = 0;
+	if (ctx->flags & DCP_CBC)
+		pkt2 |= CIPHER_MODE_CBC;
+
+	pkt = dev->hw_pkg[act_packet];
+	in_sg = ctx->in_sg;
+	out_sg = ctx->out_sg;
+	in_off = 0;
+	out_off = 0;
+
+	while (nbytes > 0) {
+		int len = min(in_sg->length - in_off, out_sg->length - out_off);
+
+		pkt->pkt1 = pkt1;
+		pkt->pkt2 = pkt2;
+
+		pkt->payload = (u32) dev->key_iv_phys_base;
+		pkt->stat = 0;
+
+		if (ctx->flags & DCP_CBC_INIT) {
+			pkt->pkt1 |= DCP_PKT_CIPHER_INIT;
+			ctx->flags &= ~DCP_CBC_INIT;
+		}
+
+		pkt->src = in_sg->dma_address + in_off;
+		pkt->dst = out_sg->dma_address + out_off;
+		pkt->size = len;
+		nbytes -= len;
+
+		if (nbytes <= 0) {
+			pkt->next = 0;
+			pkt->pkt1 |= DCP_PKT_IRQ;
+			pkt->pkt1 |= DCP_PKT_DECR_SEM;
+		} else {
+			pkt->next = dev->hw_phys_pkg[++act_packet];
+			pkt->pkt1 |= DCP_PKT_CHAIN;
+			pkt = dev->hw_pkg[act_packet];
+			in_off += len;
+			out_off += len;
+			if (in_sg->length == in_off) {
+				in_off = 0;
+				in_sg = sg_next(in_sg);
+			}
+			if (out_sg->length == out_off) {
+				out_off = 0;
+				out_sg = sg_next(out_sg);
+			}
+		}
+	}
+
+	dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
+
+	dcp_write(dev, (u32) dev->hw_phys_pkg[0],
+			dcp_chan_reg(DCP_REG_CHAN_PTR, USED_CHANNEL));
+
+
+	dcp_write(dev, 1, dcp_chan_reg(DCP_REG_CHAN_SEMA, USED_CHANNEL));
+
+	wait_for_completion_timeout(&dev->op_completion,
+			msecs_to_jiffies(1000));
+
+	stat = dcp_read(dev, dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
+
+	if (stat)
+		dev_err(dev->dev, "Channel stat error 0x%x\n", stat);
+
+	return stat;
+}
+
+
+static irqreturn_t dcp_common_irq(int irq, void *context)
+{
+	u32 msk;
+	struct dcp_dev *dev = (struct dcp_dev *) context;
+
+	msk = DCP_STAT_IRQ(dcp_read(dev, DCP_REG_STAT));
+	dcp_clear(dev, msk, DCP_REG_STAT);
+
+	if (msk == 0)
+		return IRQ_NONE;
+
+	if (msk & DCP_STAT_CHAN_1)
+		complete(&dev->op_completion);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcp_vmi_irq(int irq, void *context)
+{
+	return dcp_common_irq(irq, context);
+}
+
+static irqreturn_t dcp_irq(int irq, void *context)
+{
+	return dcp_common_irq(irq, context);
+}
+
+static int dcp_sg_nents(struct scatterlist *sg, int nbytes)
+{
+	int nents;
+	for (nents = 0; sg && nbytes > 0; sg = sg_next(sg)) {
+		nbytes -= sg->length;
+		nents++;
+	}
+	return nents;
+}
+
+static int dcp_crypt(struct dcp_dev *dev, struct dcp_op *ctx)
+{
+	int ret = 0;
+
+	ctx->sg_in_nents = dcp_sg_nents(ctx->in_sg, ctx->nbytes);
+	ctx->sg_out_nents = dcp_sg_nents(ctx->out_sg, ctx->nbytes);
+
+	mutex_lock(&dev->op_mutex);
+	dev->ctx = ctx;
+
+	if ((ctx->flags & DCP_CBC) && ctx->iv) {
+		ctx->flags |= DCP_CBC_INIT;
+		memcpy(dev->key_iv_base + AES_KEYSIZE_128,
+			ctx->iv, AES_KEYSIZE_128);
+	}
+
+	ret = dma_map_sg(dev->dev, ctx->in_sg, ctx->sg_in_nents, DMA_TO_DEVICE);
+	if (ret != ctx->sg_in_nents) {
+		dev_err(dev->dev, "couldn't map src sg\n");
+		goto out;
+	}
+
+	ret = dma_map_sg(dev->dev, ctx->out_sg, ctx->sg_out_nents,
+		DMA_FROM_DEVICE);
+	if (ret != ctx->sg_out_nents) {
+		dev_err(dev->dev, "couldn't map dest sg\n");
+		goto unmap_in;
+	}
+
+	ret = dcp_perform_op(dev, ctx->nbytes);
+
+	dma_unmap_sg(dev->dev, ctx->out_sg, ctx->sg_out_nents, DMA_FROM_DEVICE);
+unmap_in:
+	dma_unmap_sg(dev->dev, ctx->in_sg, ctx->sg_in_nents, DMA_TO_DEVICE);
+out:
+
+	mutex_unlock(&dev->op_mutex);
+
+	return ret;
+
+}
+
+static void dcp_workqueue_handler(struct work_struct *work)
+{
+	struct dcp_dev *dev = global_dev;
+	struct crypto_async_request *async_req, *backlog;
+	struct crypto_ablkcipher *tfm;
+	struct dcp_op *ctx;
+	struct dcp_dev_req_ctx *rctx;
+	struct ablkcipher_request *req;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->queue_lock, flags);
+
+	backlog = crypto_get_backlog(&dev->queue);
+	async_req = crypto_dequeue_request(&dev->queue);
+
+	spin_unlock_irqrestore(&dev->queue_lock, flags);
+
+	if (!async_req)
+		return;
+
+	if (backlog)
+		backlog->complete(backlog, -EINPROGRESS);
+
+	req = ablkcipher_request_cast(async_req);
+	tfm = crypto_ablkcipher_reqtfm(req);
+	rctx = ablkcipher_request_ctx(req);
+	ctx = crypto_ablkcipher_ctx(tfm);
+
+	if (!req->src || !req->dst)
+		return;
+
+	ctx->nbytes = req->nbytes;
+	ctx->in_sg = req->src;
+	ctx->out_sg = req->dst;
+	ctx->iv = req->info;
+	ctx->flags |= rctx->mode;
+
+	async_req->complete(async_req, dcp_crypt(dev, ctx));
+}
+
+
+static int dcp_cra_init(struct crypto_tfm *tfm)
+{
+	const char *name = tfm->__crt_alg->cra_name;
+	struct dcp_op *ctx = crypto_tfm_ctx(tfm);
+
+	tfm->crt_ablkcipher.reqsize = sizeof(struct dcp_dev_req_ctx);
+
+	ctx->fallback = crypto_alloc_ablkcipher(name, 0,
+				CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+
+	if (IS_ERR(ctx->fallback)) {
+		dev_err(global_dev->dev, "Error allocating fallback algo %s\n",
+			name);
+		return PTR_ERR(ctx->fallback);
+	}
+
+	return 0;
+}
+
+static void dcp_cra_exit(struct crypto_tfm *tfm)
+{
+	struct dcp_op *ctx = crypto_tfm_ctx(tfm);
+
+	if (ctx->fallback)
+		crypto_free_ablkcipher(ctx->fallback);
+
+	ctx->fallback = NULL;
+}
+
+/* async interface */
+static int dcp_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
+		unsigned int len)
+{
+	struct dcp_op *ctx = crypto_ablkcipher_ctx(tfm);
+	unsigned int ret = 0;
+	ctx->keylen = len;
+	ctx->flags = 0;
+	if (len == AES_KEYSIZE_128) {
+		memcpy(ctx->key, key, len);
+		ctx->flags |= DCP_NEW_KEY;
+		return 0;
+	}
+
+	dev_dbg(global_dev->dev, "key size %d not supported, using fallback ",
+		len);
+
+	ctx->fallback->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+	ctx->fallback->base.crt_flags |=
+		(tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK);
+
+	ret = crypto_ablkcipher_setkey(ctx->fallback, key, len);
+	if (ret) {
+		struct crypto_tfm *tfm_aux = crypto_ablkcipher_tfm(tfm);
+
+		tfm_aux->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+		tfm_aux->crt_flags |=
+			(ctx->fallback->base.crt_flags & CRYPTO_TFM_RES_MASK);
+	}
+	return ret;
+}
+
+static int dcp_aes_cbc_crypt(struct ablkcipher_request *req, int mode)
+{
+	struct dcp_dev_req_ctx *rctx = ablkcipher_request_ctx(req);
+	struct dcp_dev *dcp = global_dev;
+	unsigned long flags;
+	int err = 0;
+	rctx->mode = mode;
+	spin_lock_irqsave(&dcp->queue_lock, flags);
+	err = ablkcipher_enqueue_request(&dcp->queue, req);
+	spin_unlock_irqrestore(&dcp->queue_lock, flags);
+
+	if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE))
+		return -EINVAL;
+
+	queue_work(dcp_wq, &dcp_work);
+
+	return err;
+}
+
+static int dcp_aes_cbc_encrypt(struct ablkcipher_request *req)
+{
+	struct crypto_tfm *tfm =
+		crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req));
+	struct dcp_op *ctx = crypto_ablkcipher_ctx(
+		crypto_ablkcipher_reqtfm(req));
+
+	if (unlikely(ctx->keylen != AES_KEYSIZE_128)) {
+		int err = 0;
+		ablkcipher_request_set_tfm(req, ctx->fallback);
+		err = crypto_ablkcipher_encrypt(req);
+		ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm));
+		return err;
+	}
+
+	return dcp_aes_cbc_crypt(req, DCP_AES | DCP_ENC | DCP_CBC);
+}
+
+static int dcp_aes_cbc_decrypt(struct ablkcipher_request *req)
+{
+	struct crypto_tfm *tfm =
+		crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req));
+	struct dcp_op *ctx = crypto_ablkcipher_ctx(
+		crypto_ablkcipher_reqtfm(req));
+
+	if (unlikely(ctx->keylen != AES_KEYSIZE_128)) {
+		int err = 0;
+		ablkcipher_request_set_tfm(req, ctx->fallback);
+		err = crypto_ablkcipher_decrypt(req);
+		ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm));
+		return err;
+	}
+	return dcp_aes_cbc_crypt(req, DCP_AES | DCP_DEC | DCP_CBC);
+}
+
+static struct crypto_alg algs[] = {
+{
+	.cra_name = "cbc(aes)",
+	.cra_driver_name = "dcp-cbc-aes",
+	.cra_alignmask = 15,
+	.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC |
+	  CRYPTO_ALG_NEED_FALLBACK,
+	.cra_blocksize = AES_KEYSIZE_128,
+	.cra_type = &crypto_ablkcipher_type,
+	.cra_priority = 300,
+	.cra_u.ablkcipher = {
+		.min_keysize =	AES_KEYSIZE_128,
+		.max_keysize = AES_KEYSIZE_128,
+		.setkey = dcp_aes_setkey,
+		.encrypt = dcp_aes_cbc_encrypt,
+		.decrypt = dcp_aes_cbc_decrypt,
+		.ivsize = AES_KEYSIZE_128,
+	}
+
+},
+};
+
+static int dcp_probe(struct platform_device *pdev)
+{
+	struct dcp_dev *dev = NULL;
+	struct resource *r;
+	int i, ret, j;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate structure\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	global_dev = dev;
+	dev->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, dev);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n");
+		ret = -ENXIO;
+		goto err_dev;
+	}
+	dev->dcp_regs_base = ioremap(r->start, resource_size(r));
+
+
+	dcp_set(dev, DCP_CTRL_SFRST, DCP_REG_CTRL);
+	udelay(10);
+	dcp_clear(dev, DCP_CTRL_SFRST | DCP_CTRL_CLKGATE, DCP_REG_CTRL);
+
+	dcp_write(dev, DCP_CTRL_GATHER_RES_WRITE |
+		DCP_CTRL_ENABLE_CONTEXT_CACHE | DCP_CTRL_CH_IRQ_E_1,
+		DCP_REG_CTRL);
+
+	dcp_write(dev, DCP_CHAN_CTRL_ENABLE_1, DCP_REG_CHAN_CTRL);
+
+	for (i = 0; i < 4; i++)
+		dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, i));
+
+	dcp_clear(dev, -1, DCP_REG_STAT);
+
+
+	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "can't get IRQ resource (0)\n");
+		ret = -EIO;
+		goto err_unmap_mem;
+	}
+	dev->dcp_vmi_irq = r->start;
+	ret = request_irq(dev->dcp_vmi_irq, dcp_vmi_irq, 0, "dcp", dev);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't request_irq (0)\n");
+		ret = -EIO;
+		goto err_unmap_mem;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+	if (!r) {
+		dev_err(&pdev->dev, "can't get IRQ resource (1)\n");
+		ret = -EIO;
+		goto err_free_irq0;
+	}
+	dev->dcp_irq = r->start;
+	ret = request_irq(dev->dcp_irq, dcp_irq, 0, "dcp", dev);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't request_irq (1)\n");
+		ret = -EIO;
+		goto err_free_irq0;
+	}
+
+	dev->hw_pkg[0] = dma_alloc_coherent(&pdev->dev,
+			DCP_MAX_PKG * sizeof(struct dcp_dev_hw_packet),
+			&dev->hw_phys_pkg[0],
+			GFP_KERNEL);
+	if (!dev->hw_pkg[0]) {
+		dev_err(&pdev->dev, "Could not allocate hw descriptors\n");
+		ret = -ENOMEM;
+		goto err_free_irq1;
+	}
+
+	for (i = 1; i < DCP_MAX_PKG; i++) {
+		dev->hw_phys_pkg[i] = dev->hw_phys_pkg[i - 1]
+				+ sizeof(struct dcp_dev_hw_packet);
+		dev->hw_pkg[i] = dev->hw_pkg[i - 1] + 1;
+
+	}
+
+	dev->key_iv_base = dma_alloc_coherent(&pdev->dev, 2 * AES_KEYSIZE_128,
+			&dev->key_iv_phys_base, GFP_KERNEL);
+	if (!dev->key_iv_base) {
+		dev_err(&pdev->dev, "Could not allocate memory for key\n");
+		ret = -ENOMEM;
+		goto err_free_hw_packet;
+	}
+
+	spin_lock_init(&dev->queue_lock);
+	crypto_init_queue(&dev->queue, 10);
+	mutex_init(&dev->op_mutex);
+	init_completion(&dev->op_completion);
+
+	dcp_wq = alloc_workqueue("dcp_wq", WQ_HIGHPRI | WQ_UNBOUND, 1);
+	if (!dcp_wq) {
+		dev_err(&pdev->dev, "alloc_workqueue failed\n");
+		ret = -ENOMEM;
+		goto err_free_key_iv;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(algs); i++) {
+		algs[i].cra_priority = 500;
+		algs[i].cra_ctxsize = sizeof(struct dcp_op);
+		algs[i].cra_module = THIS_MODULE;
+		algs[i].cra_init = dcp_cra_init;
+		algs[i].cra_exit = dcp_cra_exit;
+		if (crypto_register_alg(&algs[i])) {
+			dev_err(&pdev->dev, "register algorithm failed\n");
+			ret = -ENOMEM;
+			goto err_unregister;
+		}
+	}
+	dev_notice(&pdev->dev, "DCP crypto enabled.!\n");
+
+	return 0;
+
+err_unregister:
+	for (j = 0; j < i; j++)
+		crypto_unregister_alg(&algs[j]);
+err_free_key_iv:
+	dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->key_iv_base,
+			dev->key_iv_phys_base);
+err_free_hw_packet:
+	dma_free_coherent(&pdev->dev, DCP_MAX_PKG *
+		sizeof(struct dcp_dev_hw_packet), dev->hw_pkg[0],
+		dev->hw_phys_pkg[0]);
+err_free_irq1:
+	free_irq(dev->dcp_irq, dev);
+err_free_irq0:
+	free_irq(dev->dcp_vmi_irq, dev);
+err_unmap_mem:
+	iounmap((void *) dev->dcp_regs_base);
+err_dev:
+	kfree(dev);
+err:
+	return ret;
+}
+
+static int dcp_remove(struct platform_device *pdev)
+{
+	struct dcp_dev *dev;
+	int j;
+	dev = platform_get_drvdata(pdev);
+	platform_set_drvdata(pdev, NULL);
+
+	dma_free_coherent(&pdev->dev,
+			DCP_MAX_PKG * sizeof(struct dcp_dev_hw_packet),
+			dev->hw_pkg[0],	dev->hw_phys_pkg[0]);
+
+	dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->key_iv_base,
+			dev->key_iv_phys_base);
+
+	free_irq(dev->dcp_irq, dev);
+	free_irq(dev->dcp_vmi_irq, dev);
+
+	iounmap((void *) dev->dcp_regs_base);
+
+	for (j = 0; j < ARRAY_SIZE(algs); j++)
+		crypto_unregister_alg(&algs[j]);
+
+	kfree(dev);
+	return 0;
+}
+
+static struct of_device_id fs_dcp_of_match[] = {
+	{	.compatible = "fsl-dcp"},
+	{},
+};
+
+static struct platform_driver fs_dcp_driver = {
+	.probe = dcp_probe,
+	.remove = dcp_remove,
+	.driver = {
+		.name = "fsl-dcp",
+		.owner = THIS_MODULE,
+		.of_match_table = fs_dcp_of_match
+	}
+};
+
+module_platform_driver(fs_dcp_driver);
+
+
+MODULE_AUTHOR("Tobias Rauter <tobias.rauter@gmail.com>");
+MODULE_DESCRIPTION("Freescale DCP Crypto Driver");
+MODULE_LICENSE("GPL");
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [RFC] Freescale DCP crypto acceleration
  2013-05-08 18:41 [RFC] Freescale DCP crypto acceleration Tobias Rauter
  2013-05-08 18:41 ` [PATCH 1/1] crypto: Added support for Freescale's DCP co-processor Tobias Rauter
@ 2013-05-10  6:27 ` Christoph G. Baumann
  1 sibling, 0 replies; 3+ messages in thread
From: Christoph G. Baumann @ 2013-05-10  6:27 UTC (permalink / raw)
  To: linux-crypto; +Cc: Tobias Rauter, Tobias Rauter

Hello Tobias,

good to see that someone else is also working on this. As I don't have the time
to do a complete rewrite it's all yours :-)
Two remarks:
1. don't forget the SHA hashes
2. try to keep it compatible with the "old" IOCTL interface as tools like
kobs-ng rely on it

Regards
Christoph

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2013-05-10  6:27 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-08 18:41 [RFC] Freescale DCP crypto acceleration Tobias Rauter
2013-05-08 18:41 ` [PATCH 1/1] crypto: Added support for Freescale's DCP co-processor Tobias Rauter
2013-05-10  6:27 ` [RFC] Freescale DCP crypto acceleration Christoph G. Baumann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.