* [PATCH 0/2] crypto: caam - Fix rsa-caam unmap and DMA alignment
@ 2026-08-21 4:55 Changwei Zou
2026-08-21 4:55 ` [PATCH 1/2] crypto: caam - Fix wrong unmap operations Changwei Zou
2026-08-21 4:55 ` [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers Changwei Zou
0 siblings, 2 replies; 7+ messages in thread
From: Changwei Zou @ 2026-08-21 4:55 UTC (permalink / raw)
To: horia.geanta, pankaj.gupta, gaurav.jain, herbert, davem
Cc: linux-crypto, linux-kernel, lukas, changwei.zou
This series fixes two issues in the rsa-caam driver.
The first patch fixes incorrect unmap operations in akcipher_do_one_req()
and akcipher_enqueue_req(), where rsa_pub_unmap() was called regardless
of the key type.
The second patch fixes an intermittent -EKEYREJECTED error observed on
i.MX8 when loading signed kernel modules. The root cause is that the
rsa-caam driver DMA-maps the destination buffer directly without checking
cacheline alignment. On non-coherent DMA systems this can corrupt
adjacent memory. A bounce buffer is introduced for destination buffers
that are not cacheline-aligned.
Changwei Zou (2):
crypto: caam - Fix wrong unmap operations
crypto: caam - Use bounce buffer for unaligned RSA destination buffers
drivers/crypto/caam/caampkc.c | 120 ++++++++++++++++++++++++++++++----
drivers/crypto/caam/caampkc.h | 6 ++
2 files changed, 113 insertions(+), 13 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] crypto: caam - Fix wrong unmap operations
2026-08-21 4:55 [PATCH 0/2] crypto: caam - Fix rsa-caam unmap and DMA alignment Changwei Zou
@ 2026-08-21 4:55 ` Changwei Zou
2026-09-29 15:19 ` [EXT] " Sahil Malhotra (OSS)
2026-08-21 4:55 ` [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers Changwei Zou
1 sibling, 1 reply; 7+ messages in thread
From: Changwei Zou @ 2026-08-21 4:55 UTC (permalink / raw)
To: horia.geanta, pankaj.gupta, gaurav.jain, herbert, davem
Cc: linux-crypto, linux-kernel, lukas, changwei.zou
Both akcipher_do_one_req() and akcipher_enqueue_req() call rsa_pub_unmap()
regardless of the key type. priv_form only takes values FORM1/FORM2/FORM3
with no distinct public key enumerator.
Use key->d to distinguish public from private key operations, then
dispatch to the correct unmap function based on key->priv_form.
caam_rsa_set_priv_key_form() implicitly relies on zero-initialization
for priv_form. Set priv_form = FORM1 explicitly at the head of the function
for clarity and robustness.
Signed-off-by: Changwei Zou <changwei.zou@canonical.com>
Assisted-by: OpenCode:claude-sonnet-4.6
---
drivers/crypto/caam/caampkc.c | 43 +++++++++++++++++++++++++----------
1 file changed, 31 insertions(+), 12 deletions(-)
diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
index cb001aa1de66..840271840cce 100644
--- a/drivers/crypto/caam/caampkc.c
+++ b/drivers/crypto/caam/caampkc.c
@@ -379,6 +379,7 @@ static int akcipher_do_one_req(struct crypto_engine *engine, void *areq)
struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
struct caam_rsa_req_ctx *req_ctx = akcipher_request_ctx(req);
struct caam_rsa_ctx *ctx = akcipher_tfm_ctx_dma(tfm);
+ struct caam_rsa_key *key = &ctx->key;
struct device *jrdev = ctx->dev;
u32 *desc = req_ctx->edesc->hw_desc;
int ret;
@@ -391,7 +392,21 @@ static int akcipher_do_one_req(struct crypto_engine *engine, void *areq)
return ret;
if (ret != -EINPROGRESS) {
- rsa_pub_unmap(jrdev, req_ctx->edesc, req);
+ if (key->d) {
+ switch (key->priv_form) {
+ case FORM1:
+ rsa_priv_f1_unmap(jrdev, req_ctx->edesc, req);
+ break;
+ case FORM2:
+ rsa_priv_f2_unmap(jrdev, req_ctx->edesc, req);
+ break;
+ case FORM3:
+ rsa_priv_f3_unmap(jrdev, req_ctx->edesc, req);
+ break;
+ }
+ } else {
+ rsa_pub_unmap(jrdev, req_ctx->edesc, req);
+ }
rsa_io_unmap(jrdev, req_ctx->edesc, req);
kfree(req_ctx->edesc);
} else {
@@ -691,17 +706,19 @@ static int akcipher_enqueue_req(struct device *jrdev,
ret = caam_jr_enqueue(jrdev, desc, cbk, req);
if ((ret != -EINPROGRESS) && (ret != -EBUSY)) {
- switch (key->priv_form) {
- case FORM1:
- rsa_priv_f1_unmap(jrdev, edesc, req);
- break;
- case FORM2:
- rsa_priv_f2_unmap(jrdev, edesc, req);
- break;
- case FORM3:
- rsa_priv_f3_unmap(jrdev, edesc, req);
- break;
- default:
+ if (key->d) {
+ switch (key->priv_form) {
+ case FORM1:
+ rsa_priv_f1_unmap(jrdev, edesc, req);
+ break;
+ case FORM2:
+ rsa_priv_f2_unmap(jrdev, edesc, req);
+ break;
+ case FORM3:
+ rsa_priv_f3_unmap(jrdev, edesc, req);
+ break;
+ }
+ } else {
rsa_pub_unmap(jrdev, edesc, req);
}
rsa_io_unmap(jrdev, edesc, req);
@@ -992,6 +1009,8 @@ static int caam_rsa_set_priv_key_form(struct caam_rsa_ctx *ctx,
size_t q_sz = raw_key->q_sz;
unsigned aligned_size;
+ rsa_key->priv_form = FORM1;
+
rsa_key->p = caam_read_raw_data(raw_key->p, &p_sz);
if (!rsa_key->p)
return -ENOMEM;
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers
2026-08-21 4:55 [PATCH 0/2] crypto: caam - Fix rsa-caam unmap and DMA alignment Changwei Zou
2026-08-21 4:55 ` [PATCH 1/2] crypto: caam - Fix wrong unmap operations Changwei Zou
@ 2026-08-21 4:55 ` Changwei Zou
2026-09-29 11:52 ` [EXT] " Sahil Malhotra (OSS)
2026-09-29 12:28 ` Kepplinger-Novakovic Martin
1 sibling, 2 replies; 7+ messages in thread
From: Changwei Zou @ 2026-08-21 4:55 UTC (permalink / raw)
To: horia.geanta, pankaj.gupta, gaurav.jain, herbert, davem
Cc: linux-crypto, linux-kernel, lukas, changwei.zou
The rsa-caam driver directly DMA-maps the destination buffer supplied by
the caller via req->dst without checking whether it meets the cacheline
alignment requirements of DMA-incoherent hardware such as i.MX8.
On CPUs with non-coherent DMA caches, if the destination buffer shares a
cacheline with other data (i.e. it is not cacheline-aligned), cache
writeback/invalidation during DMA can corrupt adjacent memory or cause
stale data to be read back. This manifests as intermittent -EKEYREJECTED
errors when loading signed kernel modules.
When any segment of req->dst is not cacheline-aligned in either its
start offset or length, allocate a single contiguous aligned bounce
buffer covering the full dst_len rounded up to a cacheline multiple,
redirect the operation to it, and copy the result back to the original
destination once the hardware has completed successfully.
Introduce a helper sg_is_dma_aligned() that checks both sg->offset and
sg->length for cacheline alignment. Checking sg->offset suffices for
the start address since physical pages are always page-aligned and
PAGE_SIZE is a multiple of dma_get_cache_alignment(). sg->length is
also checked to ensure the buffer end does not share a cacheline with
adjacent memory.
The intermittent error 'Key was rejected by service' on i.MX8 with CAAM
can be triggered when loading signed kernel modules:
for i in $(seq 1 100); do
sudo modprobe xfs 2>&1 && echo "SUCCESS on attempt $i" \
&& sudo rmmod xfs || echo "FAILED on attempt $i"
done
Signed-off-by: Changwei Zou <changwei.zou@canonical.com>
Assisted-by: OpenCode:claude-sonnet-4.6
---
drivers/crypto/caam/caampkc.c | 77 ++++++++++++++++++++++++++++++++++-
drivers/crypto/caam/caampkc.h | 6 +++
2 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
index 840271840cce..11c6b07f5dad 100644
--- a/drivers/crypto/caam/caampkc.c
+++ b/drivers/crypto/caam/caampkc.c
@@ -59,6 +59,40 @@ static void rsa_io_unmap(struct device *dev, struct rsa_edesc *edesc,
DMA_TO_DEVICE);
}
+static int do_rsa_bounce_buf(struct akcipher_request *req, int req_err)
+{
+ struct caam_rsa_req_ctx *req_ctx = akcipher_request_ctx(req);
+ int nents, err = 0;
+
+ if (!req_ctx->bounce_buf)
+ return 0;
+
+ /* Only copy back to the original destination on success */
+ if (!req_err) {
+ nents = sg_nents_for_len(req_ctx->orig_dst, req->dst_len);
+ if (nents < 0)
+ err = nents;
+ else if (sg_copy_from_buffer(req_ctx->orig_dst, nents,
+ req_ctx->bounce_buf,
+ req->dst_len) != req->dst_len)
+ err = -EFAULT;
+ }
+
+ kfree(req_ctx->bounce_buf);
+ req_ctx->bounce_buf = NULL;
+ req->dst = req_ctx->orig_dst;
+
+ return err;
+}
+
+static inline void rsa_bounce_buf_done(struct akcipher_request *req, int *err)
+{
+ int cperr = do_rsa_bounce_buf(req, *err);
+
+ if (!*err)
+ *err = cperr;
+}
+
static void rsa_pub_unmap(struct device *dev, struct rsa_edesc *edesc,
struct akcipher_request *req)
{
@@ -138,6 +172,7 @@ static void rsa_pub_done(struct device *dev, u32 *desc, u32 err, void *context)
rsa_pub_unmap(dev, edesc, req);
rsa_io_unmap(dev, edesc, req);
kfree(edesc);
+ rsa_bounce_buf_done(req, &ecode);
/*
* If no backlog flag, the completion of the request is done
@@ -181,6 +216,7 @@ static void rsa_priv_f_done(struct device *dev, u32 *desc, u32 err,
rsa_io_unmap(dev, edesc, req);
kfree(edesc);
+ rsa_bounce_buf_done(req, &ecode);
/*
* If no backlog flag, the completion of the request is done
@@ -246,6 +282,12 @@ static int caam_rsa_count_leading_zeros(struct scatterlist *sgl,
return tbytes - nbytes;
}
+static inline bool sg_is_dma_aligned(struct scatterlist *sg)
+{
+ return IS_ALIGNED(sg->offset, dma_get_cache_alignment()) &&
+ IS_ALIGNED(sg->length, dma_get_cache_alignment());
+}
+
static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
size_t desclen)
{
@@ -291,11 +333,34 @@ static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
req_ctx->fixup_src_len);
dst_nents = sg_nents_for_len(req->dst, req->dst_len);
+ req_ctx->bounce_buf = NULL;
+ req_ctx->orig_dst = req->dst;
+ if (req->dst_len > 0) {
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(req->dst, sg, dst_nents, i) {
+ if (!sg_is_dma_aligned(sg)) {
+ req_ctx->bounce_buf =
+ kzalloc(ALIGN(req->dst_len,
+ dma_get_cache_alignment()),
+ flags);
+ if (!req_ctx->bounce_buf)
+ return ERR_PTR(-ENOMEM);
+ sg_init_one(&req_ctx->dst, req_ctx->bounce_buf,
+ req->dst_len);
+ req->dst = &req_ctx->dst;
+ dst_nents = 1;
+ break;
+ }
+ }
+ }
+
mapped_src_nents = dma_map_sg(dev, req_ctx->fixup_src, src_nents,
DMA_TO_DEVICE);
if (unlikely(!mapped_src_nents)) {
dev_err(dev, "unable to map source\n");
- return ERR_PTR(-ENOMEM);
+ goto bounce_fail;
}
mapped_dst_nents = dma_map_sg(dev, req->dst, dst_nents,
DMA_FROM_DEVICE);
@@ -368,6 +433,10 @@ static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
dma_unmap_sg(dev, req->dst, dst_nents, DMA_FROM_DEVICE);
src_fail:
dma_unmap_sg(dev, req_ctx->fixup_src, src_nents, DMA_TO_DEVICE);
+bounce_fail:
+ kfree(req_ctx->bounce_buf);
+ req_ctx->bounce_buf = NULL;
+ req->dst = req_ctx->orig_dst;
return ERR_PTR(-ENOMEM);
}
@@ -409,6 +478,7 @@ static int akcipher_do_one_req(struct crypto_engine *engine, void *areq)
}
rsa_io_unmap(jrdev, req_ctx->edesc, req);
kfree(req_ctx->edesc);
+ rsa_bounce_buf_done(req, &ret);
} else {
ret = 0;
}
@@ -723,6 +793,7 @@ static int akcipher_enqueue_req(struct device *jrdev,
}
rsa_io_unmap(jrdev, edesc, req);
kfree(edesc);
+ rsa_bounce_buf_done(req, &ret);
}
return ret;
@@ -764,6 +835,7 @@ static int caam_rsa_enc(struct akcipher_request *req)
init_fail:
rsa_io_unmap(jrdev, edesc, req);
kfree(edesc);
+ rsa_bounce_buf_done(req, &ret);
return ret;
}
@@ -793,6 +865,7 @@ static int caam_rsa_dec_priv_f1(struct akcipher_request *req)
init_fail:
rsa_io_unmap(jrdev, edesc, req);
kfree(edesc);
+ rsa_bounce_buf_done(req, &ret);
return ret;
}
@@ -822,6 +895,7 @@ static int caam_rsa_dec_priv_f2(struct akcipher_request *req)
init_fail:
rsa_io_unmap(jrdev, edesc, req);
kfree(edesc);
+ rsa_bounce_buf_done(req, &ret);
return ret;
}
@@ -851,6 +925,7 @@ static int caam_rsa_dec_priv_f3(struct akcipher_request *req)
init_fail:
rsa_io_unmap(jrdev, edesc, req);
kfree(edesc);
+ rsa_bounce_buf_done(req, &ret);
return ret;
}
diff --git a/drivers/crypto/caam/caampkc.h b/drivers/crypto/caam/caampkc.h
index 96d03704c9be..efad91d6058f 100644
--- a/drivers/crypto/caam/caampkc.h
+++ b/drivers/crypto/caam/caampkc.h
@@ -103,6 +103,9 @@ struct caam_rsa_ctx {
* @src : input scatterlist (stripped of leading zeros)
* @fixup_src : input scatterlist (that might be stripped of leading zeros)
* @fixup_src_len : length of the fixup_src input scatterlist
+ * @dst : destination scatterlist backed by bounce buffer (if needed)
+ * @bounce_buf : DMA-aligned bounce buffer for destination (or NULL)
+ * @orig_dst : original destination scatterlist (before bounce substitution)
* @edesc : s/w-extended rsa descriptor
* @akcipher_op_done : callback used when operation is done
*/
@@ -110,6 +113,9 @@ struct caam_rsa_req_ctx {
struct scatterlist src[2];
struct scatterlist *fixup_src;
unsigned int fixup_src_len;
+ struct scatterlist dst;
+ u8 *bounce_buf;
+ struct scatterlist *orig_dst;
struct rsa_edesc *edesc;
void (*akcipher_op_done)(struct device *jrdev, u32 *desc, u32 err,
void *context);
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: [EXT] [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers
2026-08-21 4:55 ` [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers Changwei Zou
@ 2026-09-29 11:52 ` Sahil Malhotra (OSS)
2026-09-29 12:28 ` Kepplinger-Novakovic Martin
1 sibling, 0 replies; 7+ messages in thread
From: Sahil Malhotra (OSS) @ 2026-09-29 11:52 UTC (permalink / raw)
To: Changwei Zou, Horia Geanta, Pankaj Gupta, gaurav.jain, herbert, davem
Cc: linux-crypto, linux-kernel, lukas
Hi Changwei,
Please find my comment inline.
NXP Confidential
> -----Original Message-----
> From: Changwei Zou <changwei.zou@canonical.com>
> Sent: 21 August 2026 10:26
> To: Horia Geanta <horia.geanta@nxp.com>; Pankaj Gupta
> <pankaj.gupta@nxp.com>; gaurav.jain@nxp.com;
> herbert@gondor.apana.org.au; davem@davemloft.net
> Cc: linux-crypto@vger.kernel.org; linux-kernel@vger.kernel.org;
> lukas@wunner.de; changwei.zou@canonical.com
> Subject: [EXT] [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned
> RSA destination buffers
>
> Caution: This is an external email. Please take care when clicking links or
> opening attachments. When in doubt, report the message using the 'Report
> this email' button
>
>
> The rsa-caam driver directly DMA-maps the destination buffer supplied by the
> caller via req->dst without checking whether it meets the cacheline alignment
> requirements of DMA-incoherent hardware such as i.MX8.
>
> On CPUs with non-coherent DMA caches, if the destination buffer shares a
> cacheline with other data (i.e. it is not cacheline-aligned), cache
> writeback/invalidation during DMA can corrupt adjacent memory or cause
> stale data to be read back. This manifests as intermittent -EKEYREJECTED errors
> when loading signed kernel modules.
>
> When any segment of req->dst is not cacheline-aligned in either its start offset
> or length, allocate a single contiguous aligned bounce buffer covering the full
> dst_len rounded up to a cacheline multiple, redirect the operation to it, and
> copy the result back to the original destination once the hardware has
> completed successfully.
>
> Introduce a helper sg_is_dma_aligned() that checks both sg->offset and
> sg->length for cacheline alignment. Checking sg->offset suffices for
> the start address since physical pages are always page-aligned and PAGE_SIZE
> is a multiple of dma_get_cache_alignment(). sg->length is also checked to
> ensure the buffer end does not share a cacheline with adjacent memory.
>
> The intermittent error 'Key was rejected by service' on i.MX8 with CAAM can
> be triggered when loading signed kernel modules:
>
> for i in $(seq 1 100); do
> sudo modprobe xfs 2>&1 && echo "SUCCESS on attempt $i" \
> && sudo rmmod xfs || echo "FAILED on attempt $i"
> done
>
> Signed-off-by: Changwei Zou <changwei.zou@canonical.com>
> Assisted-by: OpenCode:claude-sonnet-4.6
> ---
> drivers/crypto/caam/caampkc.c | 77
> ++++++++++++++++++++++++++++++++++-
> drivers/crypto/caam/caampkc.h | 6 +++
> 2 files changed, 82 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
> index 840271840cce..11c6b07f5dad 100644
> --- a/drivers/crypto/caam/caampkc.c
> +++ b/drivers/crypto/caam/caampkc.c
> @@ -59,6 +59,40 @@ static void rsa_io_unmap(struct device *dev, struct
> rsa_edesc *edesc,
> DMA_TO_DEVICE); }
>
> +static int do_rsa_bounce_buf(struct akcipher_request *req, int req_err)
> +{
> + struct caam_rsa_req_ctx *req_ctx = akcipher_request_ctx(req);
> + int nents, err = 0;
> +
> + if (!req_ctx->bounce_buf)
> + return 0;
Please add this check in rsa_bounce_buf_done() and return from there only.
We need not to come here to return.
> +
> + /* Only copy back to the original destination on success */
> + if (!req_err) {
> + nents = sg_nents_for_len(req_ctx->orig_dst, req->dst_len);
> + if (nents < 0)
> + err = nents;
> + else if (sg_copy_from_buffer(req_ctx->orig_dst, nents,
> + req_ctx->bounce_buf,
> + req->dst_len) != req->dst_len)
> + err = -EFAULT;
> + }
> +
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers
2026-08-21 4:55 ` [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers Changwei Zou
2026-09-29 11:52 ` [EXT] " Sahil Malhotra (OSS)
@ 2026-09-29 12:28 ` Kepplinger-Novakovic Martin
1 sibling, 0 replies; 7+ messages in thread
From: Kepplinger-Novakovic Martin @ 2026-09-29 12:28 UTC (permalink / raw)
To: Changwei Zou
Cc: davem, gaurav.jain, herbert, horia.geanta, linux-crypto,
linux-kernel, lukas, pankaj.gupta
Am Freitag, dem 21.08.2026 um 14:55 +1000 schrieb Changwei Zou:
> The rsa-caam driver directly DMA-maps the destination buffer supplied by
> the caller via req->dst without checking whether it meets the cacheline
> alignment requirements of DMA-incoherent hardware such as i.MX8.
>
> On CPUs with non-coherent DMA caches, if the destination buffer shares a
> cacheline with other data (i.e. it is not cacheline-aligned), cache
> writeback/invalidation during DMA can corrupt adjacent memory or cause
> stale data to be read back. This manifests as intermittent -EKEYREJECTED
> errors when loading signed kernel modules.
>
> When any segment of req->dst is not cacheline-aligned in either its
> start offset or length, allocate a single contiguous aligned bounce
> buffer covering the full dst_len rounded up to a cacheline multiple,
> redirect the operation to it, and copy the result back to the original
> destination once the hardware has completed successfully.
>
> Introduce a helper sg_is_dma_aligned() that checks both sg->offset and
> sg->length for cacheline alignment. Checking sg->offset suffices for
> the start address since physical pages are always page-aligned and
> PAGE_SIZE is a multiple of dma_get_cache_alignment(). sg->length is
> also checked to ensure the buffer end does not share a cacheline with
> adjacent memory.
>
> The intermittent error 'Key was rejected by service' on i.MX8 with CAAM
> can be triggered when loading signed kernel modules:
>
> for i in $(seq 1 100); do
> sudo modprobe xfs 2>&1 && echo "SUCCESS on attempt $i" \
> && sudo rmmod xfs || echo "FAILED on attempt $i"
> done
>
> Signed-off-by: Changwei Zou <changwei.zou@canonical.com>
> Assisted-by: OpenCode:claude-sonnet-4.6
> ---
> drivers/crypto/caam/caampkc.c | 77 ++++++++++++++++++++++++++++++++++-
> drivers/crypto/caam/caampkc.h | 6 +++
> 2 files changed, 82 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
> index 840271840cce..11c6b07f5dad 100644
> --- a/drivers/crypto/caam/caampkc.c
> +++ b/drivers/crypto/caam/caampkc.c
> @@ -59,6 +59,40 @@ static void rsa_io_unmap(struct device *dev, struct rsa_edesc *edesc,
> DMA_TO_DEVICE);
> }
>
> +static int do_rsa_bounce_buf(struct akcipher_request *req, int req_err)
> +{
> + struct caam_rsa_req_ctx *req_ctx = akcipher_request_ctx(req);
> + int nents, err = 0;
> +
> + if (!req_ctx->bounce_buf)
> + return 0;
> +
> + /* Only copy back to the original destination on success */
> + if (!req_err) {
> + nents = sg_nents_for_len(req_ctx->orig_dst, req->dst_len);
> + if (nents < 0)
> + err = nents;
> + else if (sg_copy_from_buffer(req_ctx->orig_dst, nents,
> + req_ctx->bounce_buf,
> + req->dst_len) != req->dst_len)
> + err = -EFAULT;
> + }
> +
> + kfree(req_ctx->bounce_buf);
> + req_ctx->bounce_buf = NULL;
> + req->dst = req_ctx->orig_dst;
> +
> + return err;
> +}
> +
> +static inline void rsa_bounce_buf_done(struct akcipher_request *req, int *err)
> +{
> + int cperr = do_rsa_bounce_buf(req, *err);
> +
> + if (!*err)
> + *err = cperr;
> +}
> +
> static void rsa_pub_unmap(struct device *dev, struct rsa_edesc *edesc,
> struct akcipher_request *req)
> {
> @@ -138,6 +172,7 @@ static void rsa_pub_done(struct device *dev, u32 *desc, u32 err, void *context)
> rsa_pub_unmap(dev, edesc, req);
> rsa_io_unmap(dev, edesc, req);
> kfree(edesc);
> + rsa_bounce_buf_done(req, &ecode);
>
> /*
> * If no backlog flag, the completion of the request is done
> @@ -181,6 +216,7 @@ static void rsa_priv_f_done(struct device *dev, u32 *desc, u32 err,
>
> rsa_io_unmap(dev, edesc, req);
> kfree(edesc);
> + rsa_bounce_buf_done(req, &ecode);
>
> /*
> * If no backlog flag, the completion of the request is done
> @@ -246,6 +282,12 @@ static int caam_rsa_count_leading_zeros(struct scatterlist *sgl,
> return tbytes - nbytes;
> }
>
> +static inline bool sg_is_dma_aligned(struct scatterlist *sg)
> +{
> + return IS_ALIGNED(sg->offset, dma_get_cache_alignment()) &&
> + IS_ALIGNED(sg->length, dma_get_cache_alignment());
> +}
> +
> static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
> size_t desclen)
> {
> @@ -291,11 +333,34 @@ static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
> req_ctx->fixup_src_len);
> dst_nents = sg_nents_for_len(req->dst, req->dst_len);
>
> + req_ctx->bounce_buf = NULL;
> + req_ctx->orig_dst = req->dst;
> + if (req->dst_len > 0) {
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(req->dst, sg, dst_nents, i) {
> + if (!sg_is_dma_aligned(sg)) {
> + req_ctx->bounce_buf =
> + kzalloc(ALIGN(req->dst_len,
> + dma_get_cache_alignment()),
> + flags);
> + if (!req_ctx->bounce_buf)
> + return ERR_PTR(-ENOMEM);
> + sg_init_one(&req_ctx->dst, req_ctx->bounce_buf,
> + req->dst_len);
> + req->dst = &req_ctx->dst;
> + dst_nents = 1;
> + break;
> + }
> + }
> + }
> +
> mapped_src_nents = dma_map_sg(dev, req_ctx->fixup_src, src_nents,
> DMA_TO_DEVICE);
> if (unlikely(!mapped_src_nents)) {
> dev_err(dev, "unable to map source\n");
> - return ERR_PTR(-ENOMEM);
> + goto bounce_fail;
> }
> mapped_dst_nents = dma_map_sg(dev, req->dst, dst_nents,
> DMA_FROM_DEVICE);
> @@ -368,6 +433,10 @@ static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
> dma_unmap_sg(dev, req->dst, dst_nents, DMA_FROM_DEVICE);
> src_fail:
> dma_unmap_sg(dev, req_ctx->fixup_src, src_nents, DMA_TO_DEVICE);
> +bounce_fail:
> + kfree(req_ctx->bounce_buf);
> + req_ctx->bounce_buf = NULL;
> + req->dst = req_ctx->orig_dst;
> return ERR_PTR(-ENOMEM);
> }
>
> @@ -409,6 +478,7 @@ static int akcipher_do_one_req(struct crypto_engine *engine, void *areq)
> }
> rsa_io_unmap(jrdev, req_ctx->edesc, req);
> kfree(req_ctx->edesc);
> + rsa_bounce_buf_done(req, &ret);
> } else {
> ret = 0;
> }
> @@ -723,6 +793,7 @@ static int akcipher_enqueue_req(struct device *jrdev,
> }
> rsa_io_unmap(jrdev, edesc, req);
> kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
> }
>
> return ret;
> @@ -764,6 +835,7 @@ static int caam_rsa_enc(struct akcipher_request *req)
> init_fail:
> rsa_io_unmap(jrdev, edesc, req);
> kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
> return ret;
> }
>
> @@ -793,6 +865,7 @@ static int caam_rsa_dec_priv_f1(struct akcipher_request *req)
> init_fail:
> rsa_io_unmap(jrdev, edesc, req);
> kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
> return ret;
> }
>
> @@ -822,6 +895,7 @@ static int caam_rsa_dec_priv_f2(struct akcipher_request *req)
> init_fail:
> rsa_io_unmap(jrdev, edesc, req);
> kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
> return ret;
> }
>
> @@ -851,6 +925,7 @@ static int caam_rsa_dec_priv_f3(struct akcipher_request *req)
> init_fail:
> rsa_io_unmap(jrdev, edesc, req);
> kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
> return ret;
> }
>
> diff --git a/drivers/crypto/caam/caampkc.h b/drivers/crypto/caam/caampkc.h
> index 96d03704c9be..efad91d6058f 100644
> --- a/drivers/crypto/caam/caampkc.h
> +++ b/drivers/crypto/caam/caampkc.h
> @@ -103,6 +103,9 @@ struct caam_rsa_ctx {
> * @src : input scatterlist (stripped of leading zeros)
> * @fixup_src : input scatterlist (that might be stripped of leading zeros)
> * @fixup_src_len : length of the fixup_src input scatterlist
> + * @dst : destination scatterlist backed by bounce buffer (if needed)
> + * @bounce_buf : DMA-aligned bounce buffer for destination (or NULL)
> + * @orig_dst : original destination scatterlist (before bounce substitution)
> * @edesc : s/w-extended rsa descriptor
> * @akcipher_op_done : callback used when operation is done
> */
> @@ -110,6 +113,9 @@ struct caam_rsa_req_ctx {
> struct scatterlist src[2];
> struct scatterlist *fixup_src;
> unsigned int fixup_src_len;
> + struct scatterlist dst;
> + u8 *bounce_buf;
> + struct scatterlist *orig_dst;
> struct rsa_edesc *edesc;
> void (*akcipher_op_done)(struct device *jrdev, u32 *desc, u32 err,
> void *context);
hi Changwei,
I now test booting a signed squashfs rootfs using DM_VERITY on imx8mp and basically get similar issues like I did
during my tests on imx6ul earlier: https://lore.kernel.org/linux-crypto/6029acc0f0ddfe25e2537c2866d54fd7f54bc182.camel@ginzinger.com/T/#u
Your 2 patches applied fix this for me.
It's new to me, but quite simple if you know secureboot better than I do:
I create a keypair. I create a hashtree and append it to the squashfs. And after adding cert.pem to
CONFIG_SYSTEM_TRUSTED_KEYS it should verify and mount successfully.
the image is ok, and with crypto-debug enabled, I saw errors like this:
PKCS7: Sig 1: Issuing X.509 cert not found (#0da1e6a5e8314eb32932bc88de47509476e4ec85 ... "Ginzinger GESB rootfs signing key")
device-mapper: table: 254:0: verity: Root hash verification failed (-ENOKEY)
But also the regdb cert failed to load during boot:
[ 0.283510] Loading compiled-in X.509 certificates
[ 0.288607] Problem loading in-kernel X.509 certificate (-22)
which works now as well.
Tested-by: Martin Kepplinger-Novakovic <martin.kepplinger-novakovic@ginzinger.com>
Do you think this can be merged or should I disabled CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API for now?
thank you!
martin
^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: [EXT] [PATCH 1/2] crypto: caam - Fix wrong unmap operations
2026-08-21 4:55 ` [PATCH 1/2] crypto: caam - Fix wrong unmap operations Changwei Zou
@ 2026-09-29 15:19 ` Sahil Malhotra (OSS)
2026-10-05 2:19 ` Changwei Zou
0 siblings, 1 reply; 7+ messages in thread
From: Sahil Malhotra (OSS) @ 2026-09-29 15:19 UTC (permalink / raw)
To: Changwei Zou, Horia Geanta, Pankaj Gupta, gaurav.jain, herbert, davem
Cc: linux-crypto, linux-kernel, lukas
Hi Changwei,
Honestly, I'd prefer an explicit request-type marker over inferring it from
key->d, but every cleaner option (e.g. adding FORM_PUBLIC=0 and switching on
priv_form everywhere) ripples across the driver and isn't worth it for a
targeted. So key->d check is ok from my side.
Please also add Fixes: bf53795025a2 ("crypto: caam - add crypto_engine support for RSA algorithms") in this commit.
Reviewed-by: Sahil Malhotra <sahil.malhotra@nxp.com>
Regards,
Sahil Malhotra
NXP Confidential
> -----Original Message-----
> From: Changwei Zou <changwei.zou@canonical.com>
> Sent: 21 August 2026 10:26
> To: Horia Geanta <horia.geanta@nxp.com>; Pankaj Gupta
> <pankaj.gupta@nxp.com>; gaurav.jain@nxp.com;
> herbert@gondor.apana.org.au; davem@davemloft.net
> Cc: linux-crypto@vger.kernel.org; linux-kernel@vger.kernel.org;
> lukas@wunner.de; changwei.zou@canonical.com
> Subject: [EXT] [PATCH 1/2] crypto: caam - Fix wrong unmap operations
>
> Caution: This is an external email. Please take care when clicking links or
> opening attachments. When in doubt, report the message using the 'Report
> this email' button
>
>
> Both akcipher_do_one_req() and akcipher_enqueue_req() call
> rsa_pub_unmap() regardless of the key type. priv_form only takes values
> FORM1/FORM2/FORM3 with no distinct public key enumerator.
>
> Use key->d to distinguish public from private key operations, then dispatch to
> the correct unmap function based on key->priv_form.
>
> caam_rsa_set_priv_key_form() implicitly relies on zero-initialization for
> priv_form. Set priv_form = FORM1 explicitly at the head of the function for
> clarity and robustness.
>
> Signed-off-by: Changwei Zou <changwei.zou@canonical.com>
> Assisted-by: OpenCode:claude-sonnet-4.6
> ---
> drivers/crypto/caam/caampkc.c | 43 +++++++++++++++++++++++++--------
> --
> 1 file changed, 31 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
> index cb001aa1de66..840271840cce 100644
> --- a/drivers/crypto/caam/caampkc.c
> +++ b/drivers/crypto/caam/caampkc.c
> @@ -379,6 +379,7 @@ static int akcipher_do_one_req(struct crypto_engine
> *engine, void *areq)
> struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
> struct caam_rsa_req_ctx *req_ctx = akcipher_request_ctx(req);
> struct caam_rsa_ctx *ctx = akcipher_tfm_ctx_dma(tfm);
> + struct caam_rsa_key *key = &ctx->key;
> struct device *jrdev = ctx->dev;
> u32 *desc = req_ctx->edesc->hw_desc;
> int ret;
> @@ -391,7 +392,21 @@ static int akcipher_do_one_req(struct crypto_engine
> *engine, void *areq)
> return ret;
>
> if (ret != -EINPROGRESS) {
> - rsa_pub_unmap(jrdev, req_ctx->edesc, req);
> + if (key->d) {
> + switch (key->priv_form) {
> + case FORM1:
> + rsa_priv_f1_unmap(jrdev, req_ctx->edesc, req);
> + break;
> + case FORM2:
> + rsa_priv_f2_unmap(jrdev, req_ctx->edesc, req);
> + break;
> + case FORM3:
> + rsa_priv_f3_unmap(jrdev, req_ctx->edesc, req);
> + break;
> + }
> + } else {
> + rsa_pub_unmap(jrdev, req_ctx->edesc, req);
> + }
> rsa_io_unmap(jrdev, req_ctx->edesc, req);
> kfree(req_ctx->edesc);
> } else {
> @@ -691,17 +706,19 @@ static int akcipher_enqueue_req(struct device
> *jrdev,
> ret = caam_jr_enqueue(jrdev, desc, cbk, req);
>
> if ((ret != -EINPROGRESS) && (ret != -EBUSY)) {
> - switch (key->priv_form) {
> - case FORM1:
> - rsa_priv_f1_unmap(jrdev, edesc, req);
> - break;
> - case FORM2:
> - rsa_priv_f2_unmap(jrdev, edesc, req);
> - break;
> - case FORM3:
> - rsa_priv_f3_unmap(jrdev, edesc, req);
> - break;
> - default:
> + if (key->d) {
> + switch (key->priv_form) {
> + case FORM1:
> + rsa_priv_f1_unmap(jrdev, edesc, req);
> + break;
> + case FORM2:
> + rsa_priv_f2_unmap(jrdev, edesc, req);
> + break;
> + case FORM3:
> + rsa_priv_f3_unmap(jrdev, edesc, req);
> + break;
> + }
> + } else {
> rsa_pub_unmap(jrdev, edesc, req);
> }
> rsa_io_unmap(jrdev, edesc, req); @@ -992,6 +1009,8 @@ static int
> caam_rsa_set_priv_key_form(struct caam_rsa_ctx *ctx,
> size_t q_sz = raw_key->q_sz;
> unsigned aligned_size;
>
> + rsa_key->priv_form = FORM1;
> +
> rsa_key->p = caam_read_raw_data(raw_key->p, &p_sz);
> if (!rsa_key->p)
> return -ENOMEM;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [EXT] [PATCH 1/2] crypto: caam - Fix wrong unmap operations
2026-09-29 15:19 ` [EXT] " Sahil Malhotra (OSS)
@ 2026-10-05 2:19 ` Changwei Zou
0 siblings, 0 replies; 7+ messages in thread
From: Changwei Zou @ 2026-10-05 2:19 UTC (permalink / raw)
To: sahil.malhotra
Cc: changwei.zou, davem, gaurav.jain, herbert, horia.geanta,
martin.kepplinger-novakovic, linux-crypto, linux-kernel, lukas,
pankaj.gupta
Hi Sahil and Martin,
Thank you very much.
A new version (PATCH v2) is available at the following link.
https://lore.kernel.org/linux-crypto/20261004210200.290119-1-changwei.zou@canonical.com/
Kind regards,
Changwei
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-05 2:19 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-21 4:55 [PATCH 0/2] crypto: caam - Fix rsa-caam unmap and DMA alignment Changwei Zou
2026-08-21 4:55 ` [PATCH 1/2] crypto: caam - Fix wrong unmap operations Changwei Zou
2026-09-29 15:19 ` [EXT] " Sahil Malhotra (OSS)
2026-10-05 2:19 ` Changwei Zou
2026-08-21 4:55 ` [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers Changwei Zou
2026-09-29 11:52 ` [EXT] " Sahil Malhotra (OSS)
2026-09-29 12:28 ` Kepplinger-Novakovic Martin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®