2014-10-24 14:21:13

by Steffen Trumtrar

[permalink] [raw]
Subject: [PATCH v3 0/3] crypto: sahara - SHA1/256 support

Hi!

Changes since v2:
- added a patch to fix a bug with the spinlock
- save the context in the request
- made the code threadsafe

Regards,
Steffen

Steffen Trumtrar (3):
crypto: sahara - initialize spinlock
crypto: sahara - add support for i.MX53
crypto: sahara - add support for SHA1/256

.../devicetree/bindings/crypto/fsl-imx-sahara.txt | 2 +-
drivers/crypto/sahara.c | 731 ++++++++++++++++++++-
2 files changed, 709 insertions(+), 24 deletions(-)

--
2.1.1


2014-10-24 14:21:05

by Steffen Trumtrar

[permalink] [raw]
Subject: [PATCH v3 1/3] crypto: sahara - initialize spinlock

The driver uses a spinlock, but never initializes it.
Fix this.

Signed-off-by: Steffen Trumtrar <[email protected]>
---
drivers/crypto/sahara.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c
index 164e1ec624e3..6fb16fe7eea5 100644
--- a/drivers/crypto/sahara.c
+++ b/drivers/crypto/sahara.c
@@ -956,6 +956,8 @@ static int sahara_probe(struct platform_device *pdev)

crypto_init_queue(&dev->queue, SAHARA_QUEUE_LENGTH);

+ spin_lock_init(&dev->lock);
+
dev_ptr = dev;

tasklet_init(&dev->queue_task, sahara_aes_queue_task,
--
2.1.1

2014-10-24 14:21:22

by Steffen Trumtrar

[permalink] [raw]
Subject: [PATCH v3 2/3] crypto: sahara - add support for i.MX53

The Sahara on the i.MX53 is of version 4. Add support for probing the
device.

Signed-off-by: Steffen Trumtrar <[email protected]>
---
.../devicetree/bindings/crypto/fsl-imx-sahara.txt | 2 +-
drivers/crypto/sahara.c | 17 ++++++++++++++---
2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt b/Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt
index 5c65eccd0e56..e8a35c71e947 100644
--- a/Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt
+++ b/Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt
@@ -1,5 +1,5 @@
Freescale SAHARA Cryptographic Accelerator included in some i.MX chips.
-Currently only i.MX27 is supported.
+Currently only i.MX27 and i.MX53 are supported.

Required properties:
- compatible : Should be "fsl,<soc>-sahara"
diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c
index 6fb16fe7eea5..1782fc47335f 100644
--- a/drivers/crypto/sahara.c
+++ b/drivers/crypto/sahara.c
@@ -24,10 +24,12 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>

#define SAHARA_NAME "sahara"
#define SAHARA_VERSION_3 3
+#define SAHARA_VERSION_4 4
#define SAHARA_TIMEOUT_MS 1000
#define SAHARA_MAX_HW_DESC 2
#define SAHARA_MAX_HW_LINK 20
@@ -860,6 +862,7 @@ static struct platform_device_id sahara_platform_ids[] = {
MODULE_DEVICE_TABLE(platform, sahara_platform_ids);

static struct of_device_id sahara_dt_ids[] = {
+ { .compatible = "fsl,imx53-sahara" },
{ .compatible = "fsl,imx27-sahara" },
{ /* sentinel */ }
};
@@ -973,10 +976,18 @@ static int sahara_probe(struct platform_device *pdev)
clk_prepare_enable(dev->clk_ahb);

version = sahara_read(dev, SAHARA_REG_VERSION);
- if (version != SAHARA_VERSION_3) {
+ if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx27-sahara")) {
+ if (version != SAHARA_VERSION_3)
+ err = -ENODEV;
+ } else if (of_device_is_compatible(pdev->dev.of_node,
+ "fsl,imx53-sahara")) {
+ if (((version >> 8) & 0xff) != SAHARA_VERSION_4)
+ err = -ENODEV;
+ version = (version >> 8) & 0xff;
+ }
+ if (err == -ENODEV) {
dev_err(&pdev->dev, "SAHARA version %d not supported\n",
- version);
- err = -ENODEV;
+ version);
goto err_algs;
}

--
2.1.1

2014-10-24 14:21:30

by Steffen Trumtrar

[permalink] [raw]
Subject: [PATCH v3 3/3] crypto: sahara - add support for SHA1/256

Add support for the MDHA unit in the SAHARA core.
The MDHA can generate hash digests for MD5 and SHA1 in version 3 and
additionally SHA224 and SHA256 in version 4.

Add the SHA1 and SHA256 algorithms to the driver.

The implementation was tested with the in-kernel testmgr and a userspace
testprogram using AF_ALG with+without pthreads on i.MX53.

Signed-off-by: Steffen Trumtrar <[email protected]>
---
drivers/crypto/sahara.c | 712 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 692 insertions(+), 20 deletions(-)

diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c
index 1782fc47335f..f9a1d61cc419 100644
--- a/drivers/crypto/sahara.c
+++ b/drivers/crypto/sahara.c
@@ -15,6 +15,10 @@

#include <crypto/algapi.h>
#include <crypto/aes.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/sha.h>

#include <linux/clk.h>
#include <linux/crypto.h>
@@ -27,6 +31,9 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>

+#define SHA_BUFFER_LEN PAGE_SIZE
+#define SAHARA_MAX_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
+
#define SAHARA_NAME "sahara"
#define SAHARA_VERSION_3 3
#define SAHARA_VERSION_4 4
@@ -39,6 +46,8 @@
#define FLAGS_CBC BIT(1)
#define FLAGS_NEW_KEY BIT(3)
#define FLAGS_BUSY 4
+#define FLAGS_SKHA 5
+#define FLAGS_MDHA 6

#define SAHARA_HDR_BASE 0x00800000
#define SAHARA_HDR_SKHA_ALG_AES 0
@@ -52,8 +61,26 @@
#define SAHARA_HDR_CHA_MDHA (2 << 28)
#define SAHARA_HDR_PARITY_BIT (1 << 31)

+#define SAHARA_HDR_MDHA_SET_MODE_MD_KEY 0x20880000
+#define SAHARA_HDR_MDHA_SET_MODE_HASH 0x208D0000
+#define SAHARA_HDR_MDHA_HASH 0xA0850000
+#define SAHARA_HDR_MDHA_STORE_DIGEST 0x20820000
+#define SAHARA_HDR_MDHA_ALG_SHA1 0
+#define SAHARA_HDR_MDHA_ALG_MD5 1
+#define SAHARA_HDR_MDHA_ALG_SHA256 2
+#define SAHARA_HDR_MDHA_ALG_SHA224 3
+#define SAHARA_HDR_MDHA_PDATA (1 << 2)
+#define SAHARA_HDR_MDHA_HMAC (1 << 3)
+#define SAHARA_HDR_MDHA_INIT (1 << 5)
+#define SAHARA_HDR_MDHA_IPAD (1 << 6)
+#define SAHARA_HDR_MDHA_OPAD (1 << 7)
+#define SAHARA_HDR_MDHA_SWAP (1 << 8)
+#define SAHARA_HDR_MDHA_MAC_FULL (1 << 9)
+#define SAHARA_HDR_MDHA_SSL (1 << 10)
+
/* SAHARA can only process one request at a time */
#define SAHARA_QUEUE_LENGTH 1
+#define SAHARA_CHANS 2

#define SAHARA_REG_VERSION 0x00
#define SAHARA_REG_DAR 0x04
@@ -121,28 +148,58 @@ struct sahara_hw_link {
struct sahara_ctx {
struct sahara_dev *dev;
unsigned long flags;
+ unsigned int first;
+ unsigned int last;
+ unsigned int active;
+
+ /* AES-specific context */
int keylen;
u8 key[AES_KEYSIZE_128];
struct crypto_ablkcipher *fallback;
+
+ /* SHA-specific context */
+ struct crypto_shash *shash_fallback;
+ u8 context[SHA256_DIGEST_SIZE + 4];
+};
+
+enum sahara_chan {
+ SAHARA_CHAN_SHA = 0,
+ SAHARA_CHAN_AES = 1,
};

struct sahara_aes_reqctx {
unsigned long mode;
};

+struct sahara_sha_reqctx {
+ unsigned int mode;
+ unsigned int digest_size;
+ unsigned int context_size;
+ u8 buf[SAHARA_MAX_SHA_BLOCK_SIZE];
+ u8 rembuf[SAHARA_MAX_SHA_BLOCK_SIZE];
+ unsigned int buf_cnt;
+ unsigned int sg_in_idx;
+ struct scatterlist *in_sg;
+ struct scatterlist in_sg_chain[2];
+ bool in_sg_chained;
+ size_t total;
+};
+
struct sahara_dev {
struct device *device;
+ unsigned int version;
void __iomem *regs_base;
struct clk *clk_ipg;
struct clk *clk_ahb;

struct sahara_ctx *ctx;
+ struct sahara_sha_reqctx *rctx;
spinlock_t lock;
- struct crypto_queue queue;
+ struct crypto_queue queue[SAHARA_CHANS];
unsigned long flags;

- struct tasklet_struct done_task;
- struct tasklet_struct queue_task;
+ struct tasklet_struct done_task[SAHARA_CHANS];
+ struct tasklet_struct queue_task[SAHARA_CHANS];

struct sahara_hw_desc *hw_desc[SAHARA_MAX_HW_DESC];
dma_addr_t hw_phys_desc[SAHARA_MAX_HW_DESC];
@@ -153,10 +210,13 @@ struct sahara_dev {
u8 *iv_base;
dma_addr_t iv_phys_base;

+ u8 *context_base;
+ dma_addr_t context_phys_base;
+
struct sahara_hw_link *hw_link[SAHARA_MAX_HW_LINK];
dma_addr_t hw_phys_link[SAHARA_MAX_HW_LINK];

- struct ablkcipher_request *req;
+ struct crypto_async_request *req[SAHARA_CHANS];
size_t total;
struct scatterlist *in_sg;
unsigned int nb_in_sg;
@@ -414,11 +474,57 @@ static void sahara_aes_done_task(unsigned long data)

spin_lock(&dev->lock);
clear_bit(FLAGS_BUSY, &dev->flags);
+ clear_bit(FLAGS_SKHA, &dev->flags);
spin_unlock(&dev->lock);

- dev->req->base.complete(&dev->req->base, dev->error);
+ dev->req[SAHARA_CHAN_AES]->complete(dev->req[SAHARA_CHAN_AES],
+ dev->error);
+}
+
+static void sahara_sha_unmap_sg(struct sahara_dev *dev)
+{
+ struct sahara_sha_reqctx *rctx = dev->rctx;
+ struct scatterlist *sg;
+
+ if (rctx->in_sg_chained) {
+ sg = dev->in_sg;
+ while (sg) {
+ dma_unmap_sg(dev->device, sg, 1, DMA_TO_DEVICE);
+ sg = sg_next(sg);
+ }
+ } else {
+ dma_unmap_sg(dev->device, dev->in_sg, dev->nb_in_sg,
+ DMA_TO_DEVICE);
+ }
+}
+
+static void sahara_sha_done_task(unsigned long data)
+{
+ struct sahara_dev *dev = (struct sahara_dev *)data;
+ struct sahara_ctx *ctx = dev->ctx;
+ struct sahara_sha_reqctx *rctx = dev->rctx;
+ struct crypto_async_request *async_req = dev->req[SAHARA_CHAN_SHA];
+ struct ahash_request *req = ahash_request_cast(async_req);
+
+ if (rctx->sg_in_idx)
+ sahara_sha_unmap_sg(dev);
+
+ memcpy(ctx->context, dev->context_base, rctx->context_size);
+
+ if (req->result)
+ memcpy(req->result, ctx->context, rctx->digest_size);
+
+ spin_lock_bh(&dev->lock);
+ clear_bit(FLAGS_BUSY, &dev->flags);
+ clear_bit(FLAGS_MDHA, &dev->flags);
+ spin_unlock_bh(&dev->lock);
+
+ async_req->complete(async_req, dev->error);
+
+ tasklet_schedule(&dev->queue_task[SAHARA_CHAN_SHA]);
}

+
static void sahara_watchdog(unsigned long data)
{
struct sahara_dev *dev = (struct sahara_dev *)data;
@@ -428,7 +534,11 @@ static void sahara_watchdog(unsigned long data)
sahara_decode_status(dev, stat);
sahara_decode_error(dev, err);
dev->error = -ETIMEDOUT;
- sahara_aes_done_task(data);
+
+ if (dev->flags & BIT(FLAGS_SKHA))
+ sahara_aes_done_task(data);
+ else if (dev->flags & BIT(FLAGS_MDHA))
+ sahara_sha_done_task(data);
}

static int sahara_hw_descriptor_create(struct sahara_dev *dev)
@@ -541,8 +651,8 @@ static void sahara_aes_queue_task(unsigned long data)
int ret;

spin_lock(&dev->lock);
- backlog = crypto_get_backlog(&dev->queue);
- async_req = crypto_dequeue_request(&dev->queue);
+ backlog = crypto_get_backlog(&dev->queue[SAHARA_CHAN_AES]);
+ async_req = crypto_dequeue_request(&dev->queue[SAHARA_CHAN_AES]);
if (!async_req)
clear_bit(FLAGS_BUSY, &dev->flags);
spin_unlock(&dev->lock);
@@ -561,7 +671,7 @@ static void sahara_aes_queue_task(unsigned long data)
req->nbytes, req->src, req->dst);

/* assign new request to device */
- dev->req = req;
+ dev->req[SAHARA_CHAN_AES] = async_req;
dev->total = req->nbytes;
dev->in_sg = req->src;
dev->out_sg = req->dst;
@@ -578,12 +688,15 @@ static void sahara_aes_queue_task(unsigned long data)
ctx->dev = dev;
dev->ctx = ctx;

+ set_bit(FLAGS_SKHA, &dev->flags);
+
ret = sahara_hw_descriptor_create(dev);
if (ret < 0) {
spin_lock(&dev->lock);
clear_bit(FLAGS_BUSY, &dev->flags);
spin_unlock(&dev->lock);
- dev->req->base.complete(&dev->req->base, ret);
+ dev->req[SAHARA_CHAN_AES]->complete(dev->req[SAHARA_CHAN_AES],
+ ret);
}
}

@@ -646,12 +759,12 @@ static int sahara_aes_crypt(struct ablkcipher_request *req, unsigned long mode)

rctx->mode = mode;
spin_lock_bh(&dev->lock);
- err = ablkcipher_enqueue_request(&dev->queue, req);
+ err = ablkcipher_enqueue_request(&dev->queue[SAHARA_CHAN_AES], req);
busy = test_and_set_bit(FLAGS_BUSY, &dev->flags);
spin_unlock_bh(&dev->lock);

if (!busy)
- tasklet_schedule(&dev->queue_task);
+ tasklet_schedule(&dev->queue_task[SAHARA_CHAN_AES]);

return err;
}
@@ -754,6 +867,457 @@ static void sahara_aes_cra_exit(struct crypto_tfm *tfm)
ctx->fallback = NULL;
}

+static u32 sahara_sha_init_hdr(struct sahara_dev *dev,
+ struct sahara_sha_reqctx *rctx)
+{
+ struct sahara_ctx *ctx = dev->ctx;
+ u32 hdr = 0;
+
+ hdr = rctx->mode;
+
+ if (ctx->first) {
+ hdr |= SAHARA_HDR_MDHA_SET_MODE_HASH;
+ hdr |= SAHARA_HDR_MDHA_INIT;
+ } else {
+ hdr |= SAHARA_HDR_MDHA_SET_MODE_MD_KEY;
+ }
+
+ if (ctx->last)
+ hdr |= SAHARA_HDR_MDHA_PDATA;
+
+ if (hweight_long(hdr) % 2 == 0)
+ hdr |= SAHARA_HDR_PARITY_BIT;
+
+ return hdr;
+}
+
+static int sahara_hw_links_create(struct sahara_dev *dev, int start)
+{
+ struct sahara_sha_reqctx *rctx = dev->rctx;
+ struct scatterlist *sg;
+ int ret;
+ unsigned int i;
+
+ dev->in_sg = rctx->in_sg;
+
+ dev->nb_in_sg = sahara_sg_length(dev->in_sg, rctx->total);
+ if ((dev->nb_in_sg) > SAHARA_MAX_HW_LINK) {
+ dev_err(dev->device, "not enough hw links (%d)\n",
+ dev->nb_in_sg + dev->nb_out_sg);
+ return -EINVAL;
+ }
+
+ if (rctx->in_sg_chained) {
+ i = start;
+ sg = dev->in_sg;
+ while (sg) {
+ ret = dma_map_sg(dev->device, sg, 1,
+ DMA_TO_DEVICE);
+ if (!ret)
+ return -EFAULT;
+
+ dev->hw_link[i]->len = sg->length;
+ dev->hw_link[i]->p = sg->dma_address;
+ dev->hw_link[i]->next = dev->hw_phys_link[i + 1];
+ sg = sg_next(sg);
+ i += 1;
+ }
+ dev->hw_link[i-1]->next = 0;
+ } else {
+ sg = dev->in_sg;
+ ret = dma_map_sg(dev->device, dev->in_sg, dev->nb_in_sg,
+ DMA_TO_DEVICE);
+ if (!ret)
+ return -EFAULT;
+
+ for (i = start; i < dev->nb_in_sg + start; i++) {
+ dev->hw_link[i]->len = sg->length;
+ dev->hw_link[i]->p = sg->dma_address;
+ if (i == (dev->nb_in_sg + start - 1)) {
+ dev->hw_link[i]->next = 0;
+ } else {
+ dev->hw_link[i]->next = dev->hw_phys_link[i + 1];
+ sg = sg_next(sg);
+ }
+ }
+ }
+
+ return i;
+}
+
+static int sahara_sha_hw_data_descriptor_create(struct sahara_dev *dev,
+ struct sahara_sha_reqctx *rctx,
+ struct ahash_request *req,
+ int index)
+{
+ struct sahara_ctx *ctx = dev->ctx;
+ unsigned result_len;
+ int i = index;
+
+ if (ctx->first)
+ /* Create initial descriptor: #8*/
+ dev->hw_desc[index]->hdr = sahara_sha_init_hdr(dev, rctx);
+ else
+ /* Create hash descriptor: #10. Must follow #6. */
+ dev->hw_desc[index]->hdr = SAHARA_HDR_MDHA_HASH;
+
+ dev->hw_desc[index]->len1 = dev->total;
+ if (dev->hw_desc[index]->len1 == 0) {
+ /* if len1 is 0, p1 must be 0, too */
+ dev->hw_desc[index]->p1 = 0;
+ rctx->sg_in_idx = 0;
+ } else {
+ /* Create input links */
+ dev->hw_desc[index]->p1 = dev->hw_phys_link[index];
+ i = sahara_hw_links_create(dev, index);
+
+ rctx->sg_in_idx = index;
+ if (i < 0)
+ return i;
+ }
+
+ dev->hw_desc[index]->p2 = dev->hw_phys_link[i];
+
+ /* Save the context for the next operation */
+ result_len = rctx->context_size;
+ dev->hw_link[i]->p = dev->context_phys_base;
+
+ dev->hw_link[i]->len = result_len;
+ dev->hw_desc[index]->len2 = result_len;
+
+ dev->hw_link[i]->next = 0;
+
+ return 0;
+}
+
+/*
+ * Load descriptor aka #6
+ *
+ * To load a previously saved context back to the MDHA unit
+ *
+ * p1: Saved Context
+ * p2: NULL
+ *
+ */
+static int sahara_sha_hw_context_descriptor_create(struct sahara_dev *dev,
+ struct sahara_sha_reqctx *rctx,
+ struct ahash_request *req,
+ int index)
+{
+ dev->hw_desc[index]->hdr = sahara_sha_init_hdr(dev, rctx);
+
+ dev->hw_desc[index]->len1 = rctx->context_size;
+ dev->hw_desc[index]->p1 = dev->hw_phys_link[index];
+ dev->hw_desc[index]->len2 = 0;
+ dev->hw_desc[index]->p2 = 0;
+
+ dev->hw_link[index]->len = rctx->context_size;
+ dev->hw_link[index]->p = dev->context_phys_base;
+ dev->hw_link[index]->next = 0;
+
+ return 0;
+}
+
+static int sahara_sha_hw_descriptor_create(struct sahara_dev *dev,
+ struct sahara_sha_reqctx *rctx,
+ struct ahash_request *req)
+{
+ struct sahara_ctx *ctx = dev->ctx;
+
+ if (ctx->first) {
+ sahara_sha_hw_data_descriptor_create(dev, rctx, req, 0);
+ dev->hw_desc[0]->next = 0;
+ ctx->first = 0;
+ } else {
+ memcpy(dev->context_base, ctx->context, rctx->context_size);
+
+ sahara_sha_hw_context_descriptor_create(dev, rctx, req, 0);
+ dev->hw_desc[0]->next = dev->hw_phys_desc[1];
+ sahara_sha_hw_data_descriptor_create(dev, rctx, req, 1);
+ dev->hw_desc[1]->next = 0;
+ }
+
+ sahara_dump_descriptors(dev);
+ sahara_dump_links(dev);
+
+ /* Start processing descriptor chain. */
+ mod_timer(&dev->watchdog,
+ jiffies + msecs_to_jiffies(SAHARA_TIMEOUT_MS));
+ sahara_write(dev, dev->hw_phys_desc[0], SAHARA_REG_DAR);
+
+ return 0;
+}
+
+static void sahara_sha_queue_task(unsigned long data)
+{
+ struct sahara_dev *dev = (struct sahara_dev *)data;
+ struct crypto_async_request *async_req, *backlog;
+ struct sahara_ctx *ctx;
+ struct sahara_sha_reqctx *rctx;
+ struct ahash_request *req;
+ int ret;
+ int busy;
+
+ spin_lock_bh(&dev->lock);
+ backlog = crypto_get_backlog(&dev->queue[SAHARA_CHAN_SHA]);
+ async_req = crypto_dequeue_request(&dev->queue[SAHARA_CHAN_SHA]);
+ if (!async_req)
+ clear_bit(FLAGS_BUSY, &dev->flags);
+ spin_unlock_bh(&dev->lock);
+
+ busy = test_bit(FLAGS_BUSY, &dev->flags);
+ if (busy) {
+ dev_err(dev->device, "Device busy\n");
+ return;
+ }
+
+ if (!async_req)
+ return;
+
+ if (backlog)
+ backlog->complete(backlog, -EINPROGRESS);
+
+ req = ahash_request_cast(async_req);
+ rctx = ahash_request_ctx(req);
+ ctx = crypto_tfm_ctx(req->base.tfm);
+
+ dev->req[SAHARA_CHAN_SHA] = async_req;
+ dev->ctx = ctx;
+ dev->rctx = rctx;
+ dev->total = rctx->total;
+ set_bit(FLAGS_BUSY, &dev->flags);
+ set_bit(FLAGS_MDHA, &dev->flags);
+ ret = sahara_sha_hw_descriptor_create(dev, rctx, req);
+
+ if (ret < 0) {
+ clear_bit(FLAGS_BUSY, &dev->flags);
+ clear_bit(FLAGS_MDHA, &dev->flags);
+ async_req->complete(async_req, ret);
+ }
+}
+
+static int sahara_walk_and_recalc(struct scatterlist *sg, unsigned int nbytes)
+{
+ if (!sg || !sg->length)
+ return nbytes;
+
+ while (nbytes && sg) {
+ if (nbytes <= sg->length) {
+ sg->length = nbytes;
+ sg_mark_end(sg);
+ break;
+ }
+ nbytes -= sg->length;
+ sg = scatterwalk_sg_next(sg);
+ }
+
+ return nbytes;
+}
+
+static int sahara_sha_enqueue(struct ahash_request *req, int last)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct sahara_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+ struct sahara_dev *dev = dev_ptr;
+ unsigned int hash_later;
+ int err = 0;
+ struct sahara_sha_reqctx *rctx;
+ unsigned int block_size;
+ unsigned int len;
+ int busy;
+
+ if (!req->nbytes && !last)
+ return 0;
+
+ tctx->dev = dev;
+ tctx->last = last;
+
+ if (!tctx->active) {
+ tctx->active = 1;
+ tctx->first = 1;
+ }
+
+ rctx = ahash_request_ctx(req);
+
+ block_size = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
+
+ /* append bytes from previous operation */
+ len = rctx->buf_cnt + req->nbytes;
+
+ /* only the last transfer can be padded in hardware */
+ if (!last && (len < block_size)) {
+ /* to few data, save for next operation */
+ scatterwalk_map_and_copy(rctx->buf + rctx->buf_cnt, req->src,
+ 0, req->nbytes, 0);
+ rctx->buf_cnt += req->nbytes;
+
+ return 0;
+ }
+
+ /* add data from previous operation first */
+ if (rctx->buf_cnt)
+ memcpy(rctx->rembuf, rctx->buf, rctx->buf_cnt);
+
+ /* data must always be a multiple of block_size */
+ hash_later = last ? 0 : len & (block_size - 1);
+ if (hash_later) {
+ unsigned int offset = req->nbytes - hash_later;
+ /* Save remaining bytes for later use */
+ scatterwalk_map_and_copy(rctx->buf, req->src, offset,
+ hash_later, 0);
+ }
+
+ /* nbytes should now be multiple of blocksize */
+ req->nbytes = req->nbytes - hash_later;
+
+ sahara_walk_and_recalc(req->src, req->nbytes);
+
+ /* have data from previous operation and current */
+ if (rctx->buf_cnt && req->nbytes) {
+ sg_init_table(rctx->in_sg_chain, 2);
+ sg_set_buf(rctx->in_sg_chain, rctx->rembuf, rctx->buf_cnt);
+
+ scatterwalk_sg_chain(rctx->in_sg_chain, 2, req->src);
+
+ rctx->total = req->nbytes + rctx->buf_cnt;
+ rctx->in_sg = rctx->in_sg_chain;
+
+ rctx->in_sg_chained = true;
+ req->src = rctx->in_sg_chain;
+ /* only data from previous operation */
+ } else if (rctx->buf_cnt) {
+ if (req->src)
+ rctx->in_sg = req->src;
+ else
+ rctx->in_sg = rctx->in_sg_chain;
+ /* buf was copied into rembuf above */
+ sg_init_one(rctx->in_sg, rctx->rembuf, rctx->buf_cnt);
+ rctx->total = rctx->buf_cnt;
+ rctx->in_sg_chained = false;
+ /* no data from previous operation */
+ } else {
+ rctx->in_sg = req->src;
+ rctx->total = req->nbytes;
+ req->src = rctx->in_sg;
+ rctx->in_sg_chained = false;
+ }
+
+ /* on next call, we only have the remaining data in the buffer */
+ rctx->buf_cnt = hash_later;
+
+ spin_lock_bh(&dev->lock);
+ err = crypto_enqueue_request(&dev->queue[SAHARA_CHAN_SHA], &req->base);
+ busy = test_bit(FLAGS_BUSY, &dev->flags);
+ spin_unlock_bh(&dev->lock);
+
+ if (!busy)
+ tasklet_schedule(&dev->queue_task[SAHARA_CHAN_SHA]);
+
+ return err;
+}
+
+static int sahara_sha_init(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct sahara_ctx *tctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+ struct sahara_sha_reqctx *rctx = ahash_request_ctx(req);
+
+ memset(rctx, 0, sizeof(*rctx));
+
+ switch (crypto_ahash_digestsize(tfm)) {
+ case SHA1_DIGEST_SIZE:
+ rctx->mode |= SAHARA_HDR_MDHA_ALG_SHA1;
+ rctx->digest_size = SHA1_DIGEST_SIZE;
+ break;
+ case SHA256_DIGEST_SIZE:
+ rctx->mode |= SAHARA_HDR_MDHA_ALG_SHA256;
+ rctx->digest_size = SHA256_DIGEST_SIZE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rctx->context_size = rctx->digest_size + 4;
+ tctx->active = 0;
+
+ return 0;
+}
+
+static int sahara_sha_update(struct ahash_request *req)
+{
+ return sahara_sha_enqueue(req, 0);
+}
+
+static int sahara_sha_final(struct ahash_request *req)
+{
+ req->nbytes = 0;
+ return sahara_sha_enqueue(req, 1);
+}
+
+static int sahara_sha_finup(struct ahash_request *req)
+{
+ return sahara_sha_enqueue(req, 1);
+}
+
+static int sahara_sha_digest(struct ahash_request *req)
+{
+ sahara_sha_init(req);
+
+ return sahara_sha_finup(req);
+}
+
+static int sahara_sha_export(struct ahash_request *req, void *out)
+{
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct sahara_ctx *ctx = crypto_ahash_ctx(ahash);
+ struct sahara_sha_reqctx *rctx = ahash_request_ctx(req);
+
+ memcpy(out, ctx, sizeof(struct sahara_ctx));
+ memcpy(out + sizeof(struct sahara_sha_reqctx), rctx,
+ sizeof(struct sahara_sha_reqctx));
+
+ return 0;
+}
+
+static int sahara_sha_import(struct ahash_request *req, const void *in)
+{
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct sahara_ctx *ctx = crypto_ahash_ctx(ahash);
+ struct sahara_sha_reqctx *rctx = ahash_request_ctx(req);
+
+ memcpy(ctx, in, sizeof(struct sahara_ctx));
+ memcpy(rctx, in + sizeof(struct sahara_sha_reqctx),
+ sizeof(struct sahara_sha_reqctx));
+
+ return 0;
+}
+
+static int sahara_sha_cra_init(struct crypto_tfm *tfm)
+{
+ const char *name = crypto_tfm_alg_name(tfm);
+ struct sahara_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->shash_fallback = crypto_alloc_shash(name, 0,
+ CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(ctx->shash_fallback)) {
+ pr_err("Error allocating fallback algo %s\n", name);
+ return PTR_ERR(ctx->shash_fallback);
+ }
+ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+ sizeof(struct sahara_sha_reqctx) +
+ SHA_BUFFER_LEN + SHA256_BLOCK_SIZE);
+
+ return 0;
+}
+
+static void sahara_sha_cra_exit(struct crypto_tfm *tfm)
+{
+ struct sahara_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(ctx->shash_fallback);
+ ctx->shash_fallback = NULL;
+}
+
static struct crypto_alg aes_algs[] = {
{
.cra_name = "ecb(aes)",
@@ -799,6 +1363,60 @@ static struct crypto_alg aes_algs[] = {
}
};

+static struct ahash_alg sha_v3_algs[] = {
+{
+ .init = sahara_sha_init,
+ .update = sahara_sha_update,
+ .final = sahara_sha_final,
+ .finup = sahara_sha_finup,
+ .digest = sahara_sha_digest,
+ .export = sahara_sha_export,
+ .import = sahara_sha_import,
+ .halg.digestsize = SHA1_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha1",
+ .cra_driver_name = "sahara-sha1",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_AHASH |
+ CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct sahara_ctx),
+ .cra_alignmask = 0,
+ .cra_module = THIS_MODULE,
+ .cra_init = sahara_sha_cra_init,
+ .cra_exit = sahara_sha_cra_exit,
+ }
+},
+};
+
+static struct ahash_alg sha_v4_algs[] = {
+{
+ .init = sahara_sha_init,
+ .update = sahara_sha_update,
+ .final = sahara_sha_final,
+ .finup = sahara_sha_finup,
+ .digest = sahara_sha_digest,
+ .export = sahara_sha_export,
+ .import = sahara_sha_import,
+ .halg.digestsize = SHA256_DIGEST_SIZE,
+ .halg.base = {
+ .cra_name = "sha256",
+ .cra_driver_name = "sahara-sha256",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_AHASH |
+ CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct sahara_ctx),
+ .cra_alignmask = 0,
+ .cra_module = THIS_MODULE,
+ .cra_init = sahara_sha_cra_init,
+ .cra_exit = sahara_sha_cra_exit,
+ }
+},
+};
+
static irqreturn_t sahara_irq_handler(int irq, void *data)
{
struct sahara_dev *dev = (struct sahara_dev *)data;
@@ -821,7 +1439,10 @@ static irqreturn_t sahara_irq_handler(int irq, void *data)
dev->error = -EINVAL;
}

- tasklet_schedule(&dev->done_task);
+ if (dev->flags & BIT(FLAGS_SKHA))
+ tasklet_schedule(&dev->done_task[SAHARA_CHAN_AES]);
+ else if (dev->flags & BIT(FLAGS_MDHA))
+ tasklet_schedule(&dev->done_task[SAHARA_CHAN_SHA]);

return IRQ_HANDLED;
}
@@ -829,7 +1450,8 @@ static irqreturn_t sahara_irq_handler(int irq, void *data)

static int sahara_register_algs(struct sahara_dev *dev)
{
- int err, i, j;
+ int err;
+ unsigned int i, j, k, l;

for (i = 0; i < ARRAY_SIZE(aes_algs); i++) {
INIT_LIST_HEAD(&aes_algs[i].cra_list);
@@ -838,8 +1460,29 @@ static int sahara_register_algs(struct sahara_dev *dev)
goto err_aes_algs;
}

+ for (k = 0; k < ARRAY_SIZE(sha_v3_algs); k++) {
+ err = crypto_register_ahash(&sha_v3_algs[k]);
+ if (err)
+ goto err_sha_v3_algs;
+ }
+
+ if (dev->version > SAHARA_VERSION_3)
+ for (l = 0; l < ARRAY_SIZE(sha_v4_algs); l++) {
+ err = crypto_register_ahash(&sha_v4_algs[l]);
+ if (err)
+ goto err_sha_v4_algs;
+ }
+
return 0;

+err_sha_v4_algs:
+ for (j = 0; j < l; j++)
+ crypto_unregister_ahash(&sha_v4_algs[j]);
+
+err_sha_v3_algs:
+ for (j = 0; j < k; j++)
+ crypto_unregister_ahash(&sha_v4_algs[j]);
+
err_aes_algs:
for (j = 0; j < i; j++)
crypto_unregister_alg(&aes_algs[j]);
@@ -849,10 +1492,17 @@ err_aes_algs:

static void sahara_unregister_algs(struct sahara_dev *dev)
{
- int i;
+ unsigned int i;

for (i = 0; i < ARRAY_SIZE(aes_algs); i++)
crypto_unregister_alg(&aes_algs[i]);
+
+ for (i = 0; i < ARRAY_SIZE(sha_v4_algs); i++)
+ crypto_unregister_ahash(&sha_v3_algs[i]);
+
+ if (dev->version > SAHARA_VERSION_3)
+ for (i = 0; i < ARRAY_SIZE(sha_v4_algs); i++)
+ crypto_unregister_ahash(&sha_v4_algs[i]);
}

static struct platform_device_id sahara_platform_ids[] = {
@@ -942,6 +1592,16 @@ static int sahara_probe(struct platform_device *pdev)
dev->iv_base = dev->key_base + AES_KEYSIZE_128;
dev->iv_phys_base = dev->key_phys_base + AES_KEYSIZE_128;

+ /* Allocate space for context: largest digest + message length field */
+ dev->context_base = dma_alloc_coherent(&pdev->dev,
+ SHA256_DIGEST_SIZE + 4,
+ &dev->context_phys_base, GFP_KERNEL);
+ if (!dev->context_base) {
+ dev_err(&pdev->dev, "Could not allocate memory for MDHA context\n");
+ err = -ENOMEM;
+ goto err_key;
+ }
+
/* Allocate space for HW links */
dev->hw_link[0] = dma_alloc_coherent(&pdev->dev,
SAHARA_MAX_HW_LINK * sizeof(struct sahara_hw_link),
@@ -957,15 +1617,20 @@ static int sahara_probe(struct platform_device *pdev)
dev->hw_link[i] = dev->hw_link[i - 1] + 1;
}

- crypto_init_queue(&dev->queue, SAHARA_QUEUE_LENGTH);
+ crypto_init_queue(&dev->queue[SAHARA_CHAN_AES], SAHARA_QUEUE_LENGTH);
+ crypto_init_queue(&dev->queue[SAHARA_CHAN_SHA], SAHARA_QUEUE_LENGTH);

spin_lock_init(&dev->lock);

dev_ptr = dev;

- tasklet_init(&dev->queue_task, sahara_aes_queue_task,
+ tasklet_init(&dev->queue_task[SAHARA_CHAN_AES], sahara_aes_queue_task,
(unsigned long)dev);
- tasklet_init(&dev->done_task, sahara_aes_done_task,
+ tasklet_init(&dev->done_task[SAHARA_CHAN_AES], sahara_aes_done_task,
+ (unsigned long)dev);
+ tasklet_init(&dev->queue_task[SAHARA_CHAN_SHA], sahara_sha_queue_task,
+ (unsigned long)dev);
+ tasklet_init(&dev->done_task[SAHARA_CHAN_SHA], sahara_sha_done_task,
(unsigned long)dev);

init_timer(&dev->watchdog);
@@ -991,6 +1656,8 @@ static int sahara_probe(struct platform_device *pdev)
goto err_algs;
}

+ dev->version = version;
+
sahara_write(dev, SAHARA_CMD_RESET | SAHARA_CMD_MODE_BATCH,
SAHARA_REG_CMD);
sahara_write(dev, SAHARA_CONTROL_SET_THROTTLE(0) |
@@ -1018,6 +1685,9 @@ err_link:
dma_free_coherent(&pdev->dev,
2 * AES_KEYSIZE_128,
dev->key_base, dev->key_phys_base);
+ dma_free_coherent(&pdev->dev,
+ SHA256_DIGEST_SIZE,
+ dev->context_base, dev->context_phys_base);
err_key:
dma_free_coherent(&pdev->dev,
SAHARA_MAX_HW_DESC * sizeof(struct sahara_hw_desc),
@@ -1040,8 +1710,10 @@ static int sahara_remove(struct platform_device *pdev)
SAHARA_MAX_HW_DESC * sizeof(struct sahara_hw_desc),
dev->hw_desc[0], dev->hw_phys_desc[0]);

- tasklet_kill(&dev->done_task);
- tasklet_kill(&dev->queue_task);
+ tasklet_kill(&dev->done_task[SAHARA_CHAN_AES]);
+ tasklet_kill(&dev->queue_task[SAHARA_CHAN_AES]);
+ tasklet_kill(&dev->done_task[SAHARA_CHAN_SHA]);
+ tasklet_kill(&dev->queue_task[SAHARA_CHAN_SHA]);

sahara_unregister_algs(dev);

--
2.1.1

2014-11-06 14:59:08

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] crypto: sahara - add support for SHA1/256

On Fri, Oct 24, 2014 at 04:20:58PM +0200, Steffen Trumtrar wrote:
> +struct sahara_sha_reqctx {
> + unsigned int mode;
> + unsigned int digest_size;
> + unsigned int context_size;
> + u8 buf[SAHARA_MAX_SHA_BLOCK_SIZE];
> + u8 rembuf[SAHARA_MAX_SHA_BLOCK_SIZE];
> + unsigned int buf_cnt;
> + unsigned int sg_in_idx;
> + struct scatterlist *in_sg;
> + struct scatterlist in_sg_chain[2];
> + bool in_sg_chained;
> + size_t total;
> +};

Sorry but this is still broken as you don't seem to be storing
the hash state in this structure. Unless I'm misreading your
code buf and rembuf are simply leftover plain-text that is to
be hashed, rather than the hash state.

This implies that the hash state is still being stored in some
other structure that will be overwritten if you receive another
hash request before the previous one has been finalised (i.e.,
someone calls final/finup) or exported.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt