Merge branches 'bugzilla-13121+', 'bugzilla-13233', 'redhat-bugzilla-500311', 'pci...
[safe/jmp/linux-2.6] / drivers / crypto / talitos.c
index b11943d..a3918c1 100644 (file)
@@ -96,9 +96,15 @@ struct talitos_private {
        unsigned int exec_units;
        unsigned int desc_types;
 
+       /* SEC Compatibility info */
+       unsigned long features;
+
        /* next channel to be assigned next incoming descriptor */
        atomic_t last_chan;
 
+       /* per-channel number of requests pending in channel h/w fifo */
+       atomic_t *submit_count;
+
        /* per-channel request fifo */
        struct talitos_request **fifo;
 
@@ -121,7 +127,6 @@ struct talitos_private {
 
        /* request callback tasklet */
        struct tasklet_struct done_task;
-       struct tasklet_struct error_task;
 
        /* list of registered algorithms */
        struct list_head alg_list;
@@ -130,6 +135,10 @@ struct talitos_private {
        struct hwrng rng;
 };
 
+/* .features flag */
+#define TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT 0x00000001
+#define TALITOS_FTR_HW_AUTH_CHECK 0x00000002
+
 /*
  * map virtual single (contiguous) pointer to h/w descriptor pointer
  */
@@ -175,6 +184,11 @@ static int reset_channel(struct device *dev, int ch)
        setbits32(priv->reg + TALITOS_CCCR_LO(ch), TALITOS_CCCR_LO_CDWE |
                  TALITOS_CCCR_LO_CDIE);
 
+       /* and ICCR writeback, if available */
+       if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
+               setbits32(priv->reg + TALITOS_CCCR_LO(ch),
+                         TALITOS_CCCR_LO_IWSE);
+
        return 0;
 }
 
@@ -230,6 +244,11 @@ static int init_device(struct device *dev)
        setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
        setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
 
+       /* disable integrity check error interrupts (use writeback instead) */
+       if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
+               setbits32(priv->reg + TALITOS_MDEUICR_LO,
+                         TALITOS_MDEUICR_LO_ICE);
+
        return 0;
 }
 
@@ -263,15 +282,15 @@ static int talitos_submit(struct device *dev, struct talitos_desc *desc,
 
        spin_lock_irqsave(&priv->head_lock[ch], flags);
 
-       head = priv->head[ch];
-       request = &priv->fifo[ch][head];
-
-       if (request->desc) {
-               /* request queue is full */
+       if (!atomic_inc_not_zero(&priv->submit_count[ch])) {
+               /* h/w fifo is full */
                spin_unlock_irqrestore(&priv->head_lock[ch], flags);
                return -EAGAIN;
        }
 
+       head = priv->head[ch];
+       request = &priv->fifo[ch][head];
+
        /* map descriptor and save caller data */
        request->dma_desc = dma_map_single(dev, desc, sizeof(*desc),
                                           DMA_BIDIRECTIONAL);
@@ -335,6 +354,9 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
                priv->tail[ch] = (tail + 1) & (priv->fifo_len - 1);
 
                spin_unlock_irqrestore(&priv->tail_lock[ch], flags);
+
+               atomic_dec(&priv->submit_count[ch]);
+
                saved_req.callback(dev, saved_req.desc, saved_req.context,
                                   status);
                /* channel may resume processing in single desc error case */
@@ -358,6 +380,12 @@ static void talitos_done(unsigned long data)
 
        for (ch = 0; ch < priv->num_channels; ch++)
                flush_channel(dev, ch, 0, 0);
+
+       /* At this point, all completed channels have been processed.
+        * Unmask done interrupts for channels completed later on.
+        */
+       setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
+       setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
 }
 
 /*
@@ -457,16 +485,13 @@ static void report_eu_error(struct device *dev, int ch, struct talitos_desc *des
 /*
  * recover from error interrupts
  */
-static void talitos_error(unsigned long data)
+static void talitos_error(unsigned long data, u32 isr, u32 isr_lo)
 {
        struct device *dev = (struct device *)data;
        struct talitos_private *priv = dev_get_drvdata(dev);
        unsigned int timeout = TALITOS_TIMEOUT;
        int ch, error, reset_dev = 0, reset_ch = 0;
-       u32 isr, isr_lo, v, v_lo;
-
-       isr = in_be32(priv->reg + TALITOS_ISR);
-       isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
+       u32 v, v_lo;
 
        for (ch = 0; ch < priv->num_channels; ch++) {
                /* skip channels without errors */
@@ -548,16 +573,19 @@ static irqreturn_t talitos_interrupt(int irq, void *data)
 
        isr = in_be32(priv->reg + TALITOS_ISR);
        isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
-
-       /* ack */
+       /* Acknowledge interrupt */
        out_be32(priv->reg + TALITOS_ICR, isr);
        out_be32(priv->reg + TALITOS_ICR_LO, isr_lo);
 
        if (unlikely((isr & ~TALITOS_ISR_CHDONE) || isr_lo))
-               talitos_error((unsigned long)data);
+               talitos_error((unsigned long)data, isr, isr_lo);
        else
-               if (likely(isr & TALITOS_ISR_CHDONE))
+               if (likely(isr & TALITOS_ISR_CHDONE)) {
+                       /* mask further done interrupts. */
+                       clrbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_DONE);
+                       /* done_task will unmask done interrupts at exit */
                        tasklet_schedule(&priv->done_task);
+               }
 
        return (isr || isr_lo) ? IRQ_HANDLED : IRQ_NONE;
 }
@@ -779,7 +807,7 @@ static void ipsec_esp_encrypt_done(struct device *dev,
        /* copy the generated ICV to dst */
        if (edesc->dma_len) {
                icvdata = &edesc->link_tbl[edesc->src_nents +
-                                          edesc->dst_nents + 1];
+                                          edesc->dst_nents + 2];
                sg = sg_last(areq->dst, edesc->dst_nents);
                memcpy((char *)sg_virt(sg) + sg->length - ctx->authsize,
                       icvdata, ctx->authsize);
@@ -790,7 +818,7 @@ static void ipsec_esp_encrypt_done(struct device *dev,
        aead_request_complete(areq, err);
 }
 
-static void ipsec_esp_decrypt_done(struct device *dev,
+static void ipsec_esp_decrypt_swauth_done(struct device *dev,
                                   struct talitos_desc *desc, void *context,
                                   int err)
 {
@@ -808,7 +836,7 @@ static void ipsec_esp_decrypt_done(struct device *dev,
                /* auth check */
                if (edesc->dma_len)
                        icvdata = &edesc->link_tbl[edesc->src_nents +
-                                                  edesc->dst_nents + 1];
+                                                  edesc->dst_nents + 2];
                else
                        icvdata = &edesc->link_tbl[0];
 
@@ -822,6 +850,27 @@ static void ipsec_esp_decrypt_done(struct device *dev,
        aead_request_complete(req, err);
 }
 
+static void ipsec_esp_decrypt_hwauth_done(struct device *dev,
+                                  struct talitos_desc *desc, void *context,
+                                  int err)
+{
+       struct aead_request *req = context;
+       struct ipsec_esp_edesc *edesc =
+                container_of(desc, struct ipsec_esp_edesc, desc);
+
+       ipsec_esp_unmap(dev, edesc, req);
+
+       /* check ICV auth status */
+       if (!err)
+               if ((desc->hdr_lo & DESC_HDR_LO_ICCR1_MASK) !=
+                   DESC_HDR_LO_ICCR1_PASS)
+                       err = -EBADMSG;
+
+       kfree(edesc);
+
+       aead_request_complete(req, err);
+}
+
 /*
  * convert scatterlist to SEC h/w link table format
  * stop at cryptlen bytes
@@ -842,7 +891,7 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
 
        /* adjust (decrease) last one (or two) entry's len to cryptlen */
        link_tbl_ptr--;
-       while (link_tbl_ptr->len <= (-cryptlen)) {
+       while (be16_to_cpu(link_tbl_ptr->len) <= (-cryptlen)) {
                /* Empty this entry, and move to previous one */
                cryptlen += be16_to_cpu(link_tbl_ptr->len);
                link_tbl_ptr->len = 0;
@@ -874,7 +923,8 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        unsigned int cryptlen = areq->cryptlen;
        unsigned int authsize = ctx->authsize;
        unsigned int ivsize;
-       int sg_count;
+       int sg_count, ret;
+       int sg_link_tbl_len;
 
        /* hmac key */
        map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
@@ -912,7 +962,13 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        if (sg_count == 1) {
                desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
        } else {
-               sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen,
+               sg_link_tbl_len = cryptlen;
+
+               if ((edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV) &&
+                       (edesc->desc.hdr & DESC_HDR_MODE0_ENCRYPT) == 0) {
+                       sg_link_tbl_len = cryptlen + authsize;
+               }
+               sg_count = sg_to_link_tbl(areq->src, sg_count, sg_link_tbl_len,
                                          &edesc->link_tbl[0]);
                if (sg_count > 1) {
                        desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
@@ -938,27 +994,18 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
                desc->ptr[5].ptr = cpu_to_be32(sg_dma_address(areq->dst));
        } else {
                struct talitos_ptr *link_tbl_ptr =
-                       &edesc->link_tbl[edesc->src_nents];
-               struct scatterlist *sg;
+                       &edesc->link_tbl[edesc->src_nents + 1];
 
                desc->ptr[5].ptr = cpu_to_be32((struct talitos_ptr *)
                                               edesc->dma_link_tbl +
-                                              edesc->src_nents);
-               if (areq->src == areq->dst) {
-                       memcpy(link_tbl_ptr, &edesc->link_tbl[0],
-                              edesc->src_nents * sizeof(struct talitos_ptr));
-               } else {
-                       sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
-                                                 link_tbl_ptr);
-               }
-               link_tbl_ptr += sg_count - 1;
-
-               /* handle case where sg_last contains the ICV exclusively */
-               sg = sg_last(areq->dst, edesc->dst_nents);
-               if (sg->length == ctx->authsize)
-                       link_tbl_ptr--;
+                                              edesc->src_nents + 1);
+               sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
+                                         link_tbl_ptr);
 
+               /* Add an entry to the link table for ICV data */
+               link_tbl_ptr += sg_count - 1;
                link_tbl_ptr->j_extent = 0;
+               sg_count++;
                link_tbl_ptr++;
                link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
                link_tbl_ptr->len = cpu_to_be16(authsize);
@@ -967,7 +1014,7 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
                link_tbl_ptr->ptr = cpu_to_be32((struct talitos_ptr *)
                                                edesc->dma_link_tbl +
                                                edesc->src_nents +
-                                               edesc->dst_nents + 1);
+                                               edesc->dst_nents + 2);
 
                desc->ptr[5].j_extent |= DESC_PTR_LNKTBL_JUMP;
                dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
@@ -978,7 +1025,12 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, 0,
                               DMA_FROM_DEVICE);
 
-       return talitos_submit(dev, desc, callback, areq);
+       ret = talitos_submit(dev, desc, callback, areq);
+       if (ret != -EINPROGRESS) {
+               ipsec_esp_unmap(dev, edesc, areq);
+               kfree(edesc);
+       }
+       return ret;
 }
 
 
@@ -1009,6 +1061,8 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
        struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
        struct ipsec_esp_edesc *edesc;
        int src_nents, dst_nents, alloc_len, dma_len;
+       gfp_t flags = areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
+                     GFP_ATOMIC;
 
        if (areq->cryptlen + ctx->authsize > TALITOS_MAX_DATA_LEN) {
                dev_err(ctx->dev, "cryptlen exceeds h/w max limit\n");
@@ -1022,17 +1076,17 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
                dst_nents = src_nents;
        } else {
                dst_nents = sg_count(areq->dst, areq->cryptlen + ctx->authsize);
-               dst_nents = (dst_nents == 1) ? 0 : src_nents;
+               dst_nents = (dst_nents == 1) ? 0 : dst_nents;
        }
 
        /*
         * allocate space for base edesc plus the link tables,
-        * allowing for a separate entry for the generated ICV (+ 1),
+        * allowing for two separate entries for ICV and generated ICV (+ 2),
         * and the ICV data itself
         */
        alloc_len = sizeof(struct ipsec_esp_edesc);
        if (src_nents || dst_nents) {
-               dma_len = (src_nents + dst_nents + 1) *
+               dma_len = (src_nents + dst_nents + 2) *
                                 sizeof(struct talitos_ptr) + ctx->authsize;
                alloc_len += dma_len;
        } else {
@@ -1040,7 +1094,7 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
                alloc_len += icv_stashing ? ctx->authsize : 0;
        }
 
-       edesc = kmalloc(alloc_len, GFP_DMA);
+       edesc = kmalloc(alloc_len, GFP_DMA | flags);
        if (!edesc) {
                dev_err(ctx->dev, "could not allocate edescriptor\n");
                return ERR_PTR(-ENOMEM);
@@ -1072,11 +1126,14 @@ static int aead_authenc_encrypt(struct aead_request *req)
        return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_encrypt_done);
 }
 
+
+
 static int aead_authenc_decrypt(struct aead_request *req)
 {
        struct crypto_aead *authenc = crypto_aead_reqtfm(req);
        struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
        unsigned int authsize = ctx->authsize;
+       struct talitos_private *priv = dev_get_drvdata(ctx->dev);
        struct ipsec_esp_edesc *edesc;
        struct scatterlist *sg;
        void *icvdata;
@@ -1088,22 +1145,39 @@ static int aead_authenc_decrypt(struct aead_request *req)
        if (IS_ERR(edesc))
                return PTR_ERR(edesc);
 
-       /* stash incoming ICV for later cmp with ICV generated by the h/w */
-       if (edesc->dma_len)
-               icvdata = &edesc->link_tbl[edesc->src_nents +
-                                          edesc->dst_nents + 1];
-       else
-               icvdata = &edesc->link_tbl[0];
+       if ((priv->features & TALITOS_FTR_HW_AUTH_CHECK) &&
+           (((!edesc->src_nents && !edesc->dst_nents) ||
+               priv->features & TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT))) {
+
+               /* decrypt and check the ICV */
+               edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND |
+                                 DESC_HDR_MODE1_MDEU_CICV;
+
+               /* reset integrity check result bits */
+               edesc->desc.hdr_lo = 0;
+
+               return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_hwauth_done);
 
-       sg = sg_last(req->src, edesc->src_nents ? : 1);
+       } else {
 
-       memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
-              ctx->authsize);
+               /* Have to check the ICV with software */
 
-       /* decrypt */
-       edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+               edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
 
-       return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_done);
+               /* stash incoming ICV for later cmp with ICV generated by the h/w */
+               if (edesc->dma_len)
+                       icvdata = &edesc->link_tbl[edesc->src_nents +
+                                                  edesc->dst_nents + 2];
+               else
+                       icvdata = &edesc->link_tbl[0];
+
+               sg = sg_last(req->src, edesc->src_nents ? : 1);
+
+               memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
+                      ctx->authsize);
+
+               return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_swauth_done);
+       }
 }
 
 static int aead_authenc_givencrypt(
@@ -1123,6 +1197,8 @@ static int aead_authenc_givencrypt(
        edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_MODE0_ENCRYPT;
 
        memcpy(req->giv, ctx->iv, crypto_aead_ivsize(authenc));
+       /* avoid consecutive packets going out with same IV */
+       *(__be64 *)req->giv ^= cpu_to_be64(req->seq);
 
        return ipsec_esp(edesc, areq, req->giv, req->seq,
                         ipsec_esp_encrypt_done);
@@ -1321,7 +1397,7 @@ static int hw_supports(struct device *dev, __be32 desc_hdr_template)
        return ret;
 }
 
-static int __devexit talitos_remove(struct of_device *ofdev)
+static int talitos_remove(struct of_device *ofdev)
 {
        struct device *dev = &ofdev->dev;
        struct talitos_private *priv = dev_get_drvdata(dev);
@@ -1337,6 +1413,7 @@ static int __devexit talitos_remove(struct of_device *ofdev)
        if (hw_supports(dev, DESC_HDR_SEL0_RNG))
                talitos_unregister_rng(dev);
 
+       kfree(priv->submit_count);
        kfree(priv->tail);
        kfree(priv->head);
 
@@ -1354,7 +1431,6 @@ static int __devexit talitos_remove(struct of_device *ofdev)
        }
 
        tasklet_kill(&priv->done_task);
-       tasklet_kill(&priv->error_task);
 
        iounmap(priv->reg);
 
@@ -1415,7 +1491,8 @@ static int talitos_probe(struct of_device *ofdev,
        priv->ofdev = ofdev;
 
        tasklet_init(&priv->done_task, talitos_done, (unsigned long)dev);
-       tasklet_init(&priv->error_task, talitos_error, (unsigned long)dev);
+
+       INIT_LIST_HEAD(&priv->alg_list);
 
        priv->irq = irq_of_parse_and_map(np, 0);
 
@@ -1466,8 +1543,11 @@ static int talitos_probe(struct of_device *ofdev,
                goto err_out;
        }
 
-       of_node_put(np);
-       np = NULL;
+       if (of_device_is_compatible(np, "fsl,sec3.0"))
+               priv->features |= TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT;
+
+       if (of_device_is_compatible(np, "fsl,sec2.1"))
+               priv->features |= TALITOS_FTR_HW_AUTH_CHECK;
 
        priv->head_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
                                  GFP_KERNEL);
@@ -1504,6 +1584,16 @@ static int talitos_probe(struct of_device *ofdev,
                }
        }
 
+       priv->submit_count = kmalloc(sizeof(atomic_t) * priv->num_channels,
+                                    GFP_KERNEL);
+       if (!priv->submit_count) {
+               dev_err(dev, "failed to allocate fifo submit count space\n");
+               err = -ENOMEM;
+               goto err_out;
+       }
+       for (i = 0; i < priv->num_channels; i++)
+               atomic_set(&priv->submit_count[i], -(priv->chfifo_len - 1));
+
        priv->head = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
        priv->tail = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
        if (!priv->head || !priv->tail) {
@@ -1530,8 +1620,6 @@ static int talitos_probe(struct of_device *ofdev,
        }
 
        /* register crypto algorithms the device supports */
-       INIT_LIST_HEAD(&priv->alg_list);
-
        for (i = 0; i < ARRAY_SIZE(driver_algs); i++) {
                if (hw_supports(dev, driver_algs[i].desc_hdr_template)) {
                        struct talitos_crypto_alg *t_alg;
@@ -1559,8 +1647,6 @@ static int talitos_probe(struct of_device *ofdev,
 
 err_out:
        talitos_remove(ofdev);
-       if (np)
-               of_node_put(np);
 
        return err;
 }
@@ -1577,7 +1663,7 @@ static struct of_platform_driver talitos_driver = {
        .name = "talitos",
        .match_table = talitos_match,
        .probe = talitos_probe,
-       .remove = __devexit_p(talitos_remove),
+       .remove = talitos_remove,
 };
 
 static int __init talitos_init(void)