Merge branches 'bugzilla-13121+', 'bugzilla-13233', 'redhat-bugzilla-500311', 'pci...
[safe/jmp/linux-2.6] / drivers / crypto / talitos.c
index c429f68..a3918c1 100644 (file)
@@ -137,6 +137,7 @@ struct talitos_private {
 
 /* .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
@@ -183,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;
 }
 
@@ -238,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;
 }
 
@@ -369,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);
 }
 
 /*
@@ -556,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, 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;
 }
@@ -798,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)
 {
@@ -830,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
@@ -883,6 +924,7 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        unsigned int authsize = ctx->authsize;
        unsigned int ivsize;
        int sg_count, ret;
+       int sg_link_tbl_len;
 
        /* hmac key */
        map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
@@ -920,33 +962,19 @@ 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) {
-                       struct talitos_ptr *link_tbl_ptr =
-                               &edesc->link_tbl[sg_count-1];
-                       struct scatterlist *sg;
-                       struct talitos_private *priv = dev_get_drvdata(dev);
-
                        desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
                        desc->ptr[4].ptr = cpu_to_be32(edesc->dma_link_tbl);
                        dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
                                                   edesc->dma_len, DMA_BIDIRECTIONAL);
-                       /* If necessary for this SEC revision,
-                        * add a link table entry for ICV.
-                        */
-                       if ((priv->features &
-                            TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT) &&
-                           (edesc->desc.hdr & DESC_HDR_MODE0_ENCRYPT) == 0) {
-                               link_tbl_ptr->j_extent = 0;
-                               link_tbl_ptr++;
-                               link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
-                               link_tbl_ptr->len = cpu_to_be16(authsize);
-                               sg = sg_last(areq->src, edesc->src_nents ? : 1);
-                               link_tbl_ptr->ptr = cpu_to_be32(
-                                               (char *)sg_dma_address(sg)
-                                               + sg->length - authsize);
-                       }
                } else {
                        /* Only one segment now, so no link tbl needed */
                        desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
@@ -971,13 +999,9 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
                desc->ptr[5].ptr = cpu_to_be32((struct talitos_ptr *)
                                               edesc->dma_link_tbl +
                                               edesc->src_nents + 1);
-               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);
-               }
+               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;
@@ -1102,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;
@@ -1118,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 + 2];
-       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))) {
 
-       sg = sg_last(req->src, edesc->src_nents ? : 1);
+               /* decrypt and check the ICV */
+               edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND |
+                                 DESC_HDR_MODE1_MDEU_CICV;
 
-       memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
-              ctx->authsize);
+               /* reset integrity check result bits */
+               edesc->desc.hdr_lo = 0;
 
-       /* decrypt */
-       edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+               return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_hwauth_done);
+
+       } else {
 
-       return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_done);
+               /* Have to check the ICV with software */
+
+               edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+
+               /* 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(
@@ -1446,10 +1490,10 @@ static int talitos_probe(struct of_device *ofdev,
 
        priv->ofdev = ofdev;
 
-       INIT_LIST_HEAD(&priv->alg_list);
-
        tasklet_init(&priv->done_task, talitos_done, (unsigned long)dev);
 
+       INIT_LIST_HEAD(&priv->alg_list);
+
        priv->irq = irq_of_parse_and_map(np, 0);
 
        if (priv->irq == NO_IRQ) {
@@ -1502,6 +1546,9 @@ static int talitos_probe(struct of_device *ofdev,
        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);
        priv->tail_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
@@ -1545,7 +1592,7 @@ static int talitos_probe(struct of_device *ofdev,
                goto err_out;
        }
        for (i = 0; i < priv->num_channels; i++)
-               atomic_set(&priv->submit_count[i], -priv->chfifo_len);
+               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);