mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-04-18 03:23:53 -04:00
crypto: caam - fix S/G table passing page boundary
According to CAAM RM: -crypto engine reads 4 S/G entries (64 bytes) at a time, even if the S/G table has fewer entries -it's the responsibility of the user / programmer to make sure this HW behaviour has no side effect The drivers do not take care of this currently, leading to IOMMU faults when the S/G table ends close to a page boundary - since only one page is DMA mapped, while CAAM's DMA engine accesses two pages. Fix this by rounding up the number of allocated S/G table entries to a multiple of 4. Note that in case of two *contiguous* S/G tables, only the last table might needs extra entries. Signed-off-by: Horia Geantă <horia.geanta@nxp.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* Copyright 2015-2016 Freescale Semiconductor Inc.
|
||||
* Copyright 2017-2018 NXP
|
||||
* Copyright 2017-2019 NXP
|
||||
*/
|
||||
|
||||
#include "compat.h"
|
||||
@@ -460,9 +460,25 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
|
||||
/*
|
||||
* Create S/G table: req->assoclen, [IV,] req->src [, req->dst].
|
||||
* Input is not contiguous.
|
||||
* HW reads 4 S/G entries at a time; make sure the reads don't go beyond
|
||||
* the end of the table by allocating more S/G entries. Logic:
|
||||
* if (src != dst && output S/G)
|
||||
* pad output S/G, if needed
|
||||
* else if (src == dst && S/G)
|
||||
* overlapping S/Gs; pad one of them
|
||||
* else if (input S/G) ...
|
||||
* pad input S/G, if needed
|
||||
*/
|
||||
qm_sg_nents = 1 + !!ivsize + mapped_src_nents +
|
||||
(mapped_dst_nents > 1 ? mapped_dst_nents : 0);
|
||||
qm_sg_nents = 1 + !!ivsize + mapped_src_nents;
|
||||
if (mapped_dst_nents > 1)
|
||||
qm_sg_nents += pad_sg_nents(mapped_dst_nents);
|
||||
else if ((req->src == req->dst) && (mapped_src_nents > 1))
|
||||
qm_sg_nents = max(pad_sg_nents(qm_sg_nents),
|
||||
1 + !!ivsize +
|
||||
pad_sg_nents(mapped_src_nents));
|
||||
else
|
||||
qm_sg_nents = pad_sg_nents(qm_sg_nents);
|
||||
|
||||
sg_table = &edesc->sgt[0];
|
||||
qm_sg_bytes = qm_sg_nents * sizeof(*sg_table);
|
||||
if (unlikely(offsetof(struct aead_edesc, sgt) + qm_sg_bytes + ivsize >
|
||||
@@ -1086,7 +1102,24 @@ static struct skcipher_edesc *skcipher_edesc_alloc(struct skcipher_request *req)
|
||||
qm_sg_ents = 1 + mapped_src_nents;
|
||||
dst_sg_idx = qm_sg_ents;
|
||||
|
||||
qm_sg_ents += mapped_dst_nents > 1 ? mapped_dst_nents : 0;
|
||||
/*
|
||||
* HW reads 4 S/G entries at a time; make sure the reads don't go beyond
|
||||
* the end of the table by allocating more S/G entries. Logic:
|
||||
* if (src != dst && output S/G)
|
||||
* pad output S/G, if needed
|
||||
* else if (src == dst && S/G)
|
||||
* overlapping S/Gs; pad one of them
|
||||
* else if (input S/G) ...
|
||||
* pad input S/G, if needed
|
||||
*/
|
||||
if (mapped_dst_nents > 1)
|
||||
qm_sg_ents += pad_sg_nents(mapped_dst_nents);
|
||||
else if ((req->src == req->dst) && (mapped_src_nents > 1))
|
||||
qm_sg_ents = max(pad_sg_nents(qm_sg_ents),
|
||||
1 + pad_sg_nents(mapped_src_nents));
|
||||
else
|
||||
qm_sg_ents = pad_sg_nents(qm_sg_ents);
|
||||
|
||||
qm_sg_bytes = qm_sg_ents * sizeof(struct dpaa2_sg_entry);
|
||||
if (unlikely(offsetof(struct skcipher_edesc, sgt) + qm_sg_bytes +
|
||||
ivsize > CAAM_QI_MEMCACHE_SIZE)) {
|
||||
@@ -3418,7 +3451,7 @@ static int ahash_update_ctx(struct ahash_request *req)
|
||||
|
||||
edesc->src_nents = src_nents;
|
||||
qm_sg_src_index = 1 + (*buflen ? 1 : 0);
|
||||
qm_sg_bytes = (qm_sg_src_index + mapped_nents) *
|
||||
qm_sg_bytes = pad_sg_nents(qm_sg_src_index + mapped_nents) *
|
||||
sizeof(*sg_table);
|
||||
sg_table = &edesc->sgt[0];
|
||||
|
||||
@@ -3503,7 +3536,7 @@ static int ahash_final_ctx(struct ahash_request *req)
|
||||
gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
|
||||
GFP_KERNEL : GFP_ATOMIC;
|
||||
int buflen = *current_buflen(state);
|
||||
int qm_sg_bytes, qm_sg_src_index;
|
||||
int qm_sg_bytes;
|
||||
int digestsize = crypto_ahash_digestsize(ahash);
|
||||
struct ahash_edesc *edesc;
|
||||
struct dpaa2_sg_entry *sg_table;
|
||||
@@ -3514,8 +3547,7 @@ static int ahash_final_ctx(struct ahash_request *req)
|
||||
if (!edesc)
|
||||
return -ENOMEM;
|
||||
|
||||
qm_sg_src_index = 1 + (buflen ? 1 : 0);
|
||||
qm_sg_bytes = qm_sg_src_index * sizeof(*sg_table);
|
||||
qm_sg_bytes = pad_sg_nents(1 + (buflen ? 1 : 0)) * sizeof(*sg_table);
|
||||
sg_table = &edesc->sgt[0];
|
||||
|
||||
ret = ctx_map_to_qm_sg(ctx->dev, state, ctx->ctx_len, sg_table,
|
||||
@@ -3527,7 +3559,7 @@ static int ahash_final_ctx(struct ahash_request *req)
|
||||
if (ret)
|
||||
goto unmap_ctx;
|
||||
|
||||
dpaa2_sg_set_final(sg_table + qm_sg_src_index - 1, true);
|
||||
dpaa2_sg_set_final(sg_table + (buflen ? 1 : 0), true);
|
||||
|
||||
edesc->qm_sg_dma = dma_map_single(ctx->dev, sg_table, qm_sg_bytes,
|
||||
DMA_TO_DEVICE);
|
||||
@@ -3608,7 +3640,8 @@ static int ahash_finup_ctx(struct ahash_request *req)
|
||||
|
||||
edesc->src_nents = src_nents;
|
||||
qm_sg_src_index = 1 + (buflen ? 1 : 0);
|
||||
qm_sg_bytes = (qm_sg_src_index + mapped_nents) * sizeof(*sg_table);
|
||||
qm_sg_bytes = pad_sg_nents(qm_sg_src_index + mapped_nents) *
|
||||
sizeof(*sg_table);
|
||||
sg_table = &edesc->sgt[0];
|
||||
|
||||
ret = ctx_map_to_qm_sg(ctx->dev, state, ctx->ctx_len, sg_table,
|
||||
@@ -3705,7 +3738,7 @@ static int ahash_digest(struct ahash_request *req)
|
||||
int qm_sg_bytes;
|
||||
struct dpaa2_sg_entry *sg_table = &edesc->sgt[0];
|
||||
|
||||
qm_sg_bytes = mapped_nents * sizeof(*sg_table);
|
||||
qm_sg_bytes = pad_sg_nents(mapped_nents) * sizeof(*sg_table);
|
||||
sg_to_qm_sg_last(req->src, mapped_nents, sg_table, 0);
|
||||
edesc->qm_sg_dma = dma_map_single(ctx->dev, sg_table,
|
||||
qm_sg_bytes, DMA_TO_DEVICE);
|
||||
@@ -3877,7 +3910,8 @@ static int ahash_update_no_ctx(struct ahash_request *req)
|
||||
}
|
||||
|
||||
edesc->src_nents = src_nents;
|
||||
qm_sg_bytes = (1 + mapped_nents) * sizeof(*sg_table);
|
||||
qm_sg_bytes = pad_sg_nents(1 + mapped_nents) *
|
||||
sizeof(*sg_table);
|
||||
sg_table = &edesc->sgt[0];
|
||||
|
||||
ret = buf_map_to_qm_sg(ctx->dev, sg_table, state);
|
||||
@@ -3996,7 +4030,7 @@ static int ahash_finup_no_ctx(struct ahash_request *req)
|
||||
}
|
||||
|
||||
edesc->src_nents = src_nents;
|
||||
qm_sg_bytes = (2 + mapped_nents) * sizeof(*sg_table);
|
||||
qm_sg_bytes = pad_sg_nents(2 + mapped_nents) * sizeof(*sg_table);
|
||||
sg_table = &edesc->sgt[0];
|
||||
|
||||
ret = buf_map_to_qm_sg(ctx->dev, sg_table, state);
|
||||
@@ -4111,7 +4145,8 @@ static int ahash_update_first(struct ahash_request *req)
|
||||
int qm_sg_bytes;
|
||||
|
||||
sg_to_qm_sg_last(req->src, mapped_nents, sg_table, 0);
|
||||
qm_sg_bytes = mapped_nents * sizeof(*sg_table);
|
||||
qm_sg_bytes = pad_sg_nents(mapped_nents) *
|
||||
sizeof(*sg_table);
|
||||
edesc->qm_sg_dma = dma_map_single(ctx->dev, sg_table,
|
||||
qm_sg_bytes,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
Reference in New Issue
Block a user