drivers/tee/optee/ffa_abi.c | 22 ++++++++++++++++++---- drivers/tee/optee/optee_msg.h | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-)
OP-TEE FF-A memory objects use 4 KiB pages, while the kernel page
size may be larger. Consequently, tee_shm->offset can be greater than
or equal to FFA_PAGE_SIZE, but OP-TEE rejects such a value in
internal_offs.
Do not encode the excess page offset in offs_low/offs_high. Those
fields describe the logical memref offset and are copied back into
tee_param->shm_offs on return. Folding the page offset into them breaks
parameter round trips when a memref is reused. They are also ignored by
the OPTEE_RPC_CMD_SHM_ALLOC response path, which uses only global_id and
internal_offs to construct the shared-memory mobj.
Instead, start the FF-A descriptor at the 4 KiB page containing the
shared buffer, the same approach as optee_fill_pages_list() in the SMC
ABI. Store the remaining in-page offset in internal_offs and preserve
shm_offs in offs_low/offs_high. This keeps internal_offs within the
FF-A page size, maps RPC allocations at the correct address, and
preserves normal memref offsets across repeated invocations.
Tested on ARMv8-A with 64 KiB PAGE_SIZE. OP-TEE OS ran as a secure
partition under Hafnium (SPMC) over FF-A. Verified registered shared
memory with tee_shm->offset >= 4 KiB, memref reuse on the same
TEEC_Operation, and RPC OPTEE_RPC_CMD_SHM_ALLOC (xtest regression
6007-6009). optee_hello_world, optee_aes, and xtest regression 1005,
1007, 1008, 4001-4003 and 6001-6003 also passed.
Fixes: 4615e5a34b95 ("optee: add FF-A support")
Acked-by: Liming Sun <limings@nvidia.com>
Acked-by: James Hurley <jahurley@nvidia.com>
Acked-by: Dave Thompson <davthompson@nvidia.com>
Signed-off-by: Mahantesh Salimath <mahantesh@nvidia.com>
---
v2:
- Drop helper indirection; mask internal_offs inline (Sumit Garg)
- Keep a single ffa_offs local in optee_ffa_shm_register()
Link: https://lore.kernel.org/lkml/20260904134732.1072541-1-mahantesh@nvidia.com/
drivers/tee/optee/ffa_abi.c | 22 ++++++++++++++++++----
drivers/tee/optee/optee_msg.h | 4 ++--
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index 633715b98625..fdedcab50f23 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -198,7 +198,8 @@ static int to_msg_param_ffa_mem(struct optee_msg_param *mp,
if (shm) {
u64 shm_offs = p->u.memref.shm_offs;
- mp->u.fmem.internal_offs = shm->offset;
+ mp->u.fmem.internal_offs = tee_shm_get_page_offset(shm) &
+ (FFA_PAGE_SIZE - 1);
mp->u.fmem.offs_low = shm_offs;
mp->u.fmem.offs_high = shm_offs >> 32;
@@ -284,14 +285,26 @@ static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm,
.nattrs = 1,
};
struct sg_table sgt;
+ size_t ffa_offs;
int rc;
+ if (!num_pages)
+ return -EINVAL;
+
rc = optee_check_mem_type(start, num_pages);
if (rc)
return rc;
- rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, 0,
- num_pages * PAGE_SIZE, GFP_KERNEL);
+ /*
+ * Start the FF-A descriptor at the 4 KiB page containing the shared
+ * buffer, skipping unused leading 4 KiB pages when PAGE_SIZE is
+ * larger. Same approach as optee_fill_pages_list() in the SMC ABI.
+ * This leaves only the offset into that 4 KiB page for internal_offs.
+ */
+ ffa_offs = round_down(tee_shm_get_page_offset(shm), FFA_PAGE_SIZE);
+ rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, ffa_offs,
+ num_pages * PAGE_SIZE - ffa_offs,
+ GFP_KERNEL);
if (rc)
return rc;
args.sg = sgt.sgl;
@@ -458,7 +471,8 @@ static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
.attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT,
.u.fmem.size = tee_shm_get_size(shm),
.u.fmem.global_id = shm->sec_world_id,
- .u.fmem.internal_offs = shm->offset,
+ .u.fmem.internal_offs = tee_shm_get_page_offset(shm) &
+ (FFA_PAGE_SIZE - 1),
};
arg->ret = TEEC_SUCCESS;
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
index 7d9b12e71c03..6c3043f8da33 100644
--- a/drivers/tee/optee/optee_msg.h
+++ b/drivers/tee/optee/optee_msg.h
@@ -136,8 +136,8 @@ struct optee_msg_param_rmem {
* struct optee_msg_param_fmem - FF-A memory reference parameter
* @offs_low: lower bits of offset into shared memory reference
* @offs_high: higher bits of offset into shared memory reference
- * @internal_offs: internal offset into the first page of shared memory
- * reference
+ * @internal_offs: offset into the first 4 KiB page of the FF-A shared
+ * memory region
* @size: size of the buffer
* @global_id: global identifier of the shared memory
*/
--
2.43.0
On Thu, 10 Sep 2026 at 16:11:24 +0000, Mahantesh Salimath wrote:
>OP-TEE FF-A memory objects use 4 KiB pages, while the kernel page
>size may be larger. Consequently, tee_shm->offset can be greater than
>or equal to FFA_PAGE_SIZE, but OP-TEE rejects such a value in
>internal_offs.
>
>Do not encode the excess page offset in offs_low/offs_high. Those
>fields describe the logical memref offset and are copied back into
>tee_param->shm_offs on return. Folding the page offset into them breaks
>parameter round trips when a memref is reused. They are also ignored by
>the OPTEE_RPC_CMD_SHM_ALLOC response path, which uses only global_id and
>internal_offs to construct the shared-memory mobj.
>
>Instead, start the FF-A descriptor at the 4 KiB page containing the
>shared buffer, the same approach as optee_fill_pages_list() in the SMC
>ABI. Store the remaining in-page offset in internal_offs and preserve
>shm_offs in offs_low/offs_high. This keeps internal_offs within the
>FF-A page size, maps RPC allocations at the correct address, and
>preserves normal memref offsets across repeated invocations.
>
>Tested on ARMv8-A with 64 KiB PAGE_SIZE. OP-TEE OS ran as a secure
>partition under Hafnium (SPMC) over FF-A. Verified registered shared
>memory with tee_shm->offset >= 4 KiB, memref reuse on the same
>TEEC_Operation, and RPC OPTEE_RPC_CMD_SHM_ALLOC (xtest regression
>6007-6009). optee_hello_world, optee_aes, and xtest regression 1005,
>1007, 1008, 4001-4003 and 6001-6003 also passed.
>
>Fixes: 4615e5a34b95 ("optee: add FF-A support")
>Acked-by: Liming Sun <limings@nvidia.com>
>Acked-by: James Hurley <jahurley@nvidia.com>
>Acked-by: Dave Thompson <davthompson@nvidia.com>
>Signed-off-by: Mahantesh Salimath <mahantesh@nvidia.com>
>---
>v2:
>- Drop helper indirection; mask internal_offs inline (Sumit Garg)
>- Keep a single ffa_offs local in optee_ffa_shm_register()
>
>Link: https://lore.kernel.org/lkml/20260904134732.1072541-1-mahantesh@nvidia.com/
>
> drivers/tee/optee/ffa_abi.c | 22 ++++++++++++++++++----
> drivers/tee/optee/optee_msg.h | 4 ++--
> 2 files changed, 20 insertions(+), 6 deletions(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
>diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
>index 633715b98625..fdedcab50f23 100644
>--- a/drivers/tee/optee/ffa_abi.c
>+++ b/drivers/tee/optee/ffa_abi.c
>@@ -198,7 +198,8 @@ static int to_msg_param_ffa_mem(struct optee_msg_param *mp,
> if (shm) {
> u64 shm_offs = p->u.memref.shm_offs;
>
>- mp->u.fmem.internal_offs = shm->offset;
>+ mp->u.fmem.internal_offs = tee_shm_get_page_offset(shm) &
>+ (FFA_PAGE_SIZE - 1);
>
> mp->u.fmem.offs_low = shm_offs;
> mp->u.fmem.offs_high = shm_offs >> 32;
>@@ -284,14 +285,26 @@ static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm,
> .nattrs = 1,
> };
> struct sg_table sgt;
>+ size_t ffa_offs;
> int rc;
>
>+ if (!num_pages)
>+ return -EINVAL;
>+
> rc = optee_check_mem_type(start, num_pages);
> if (rc)
> return rc;
>
>- rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, 0,
>- num_pages * PAGE_SIZE, GFP_KERNEL);
>+ /*
>+ * Start the FF-A descriptor at the 4 KiB page containing the shared
>+ * buffer, skipping unused leading 4 KiB pages when PAGE_SIZE is
>+ * larger. Same approach as optee_fill_pages_list() in the SMC ABI.
>+ * This leaves only the offset into that 4 KiB page for internal_offs.
>+ */
>+ ffa_offs = round_down(tee_shm_get_page_offset(shm), FFA_PAGE_SIZE);
>+ rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, ffa_offs,
>+ num_pages * PAGE_SIZE - ffa_offs,
>+ GFP_KERNEL);
> if (rc)
> return rc;
> args.sg = sgt.sgl;
>@@ -458,7 +471,8 @@ static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
> .attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT,
> .u.fmem.size = tee_shm_get_size(shm),
> .u.fmem.global_id = shm->sec_world_id,
>- .u.fmem.internal_offs = shm->offset,
>+ .u.fmem.internal_offs = tee_shm_get_page_offset(shm) &
>+ (FFA_PAGE_SIZE - 1),
> };
>
> arg->ret = TEEC_SUCCESS;
>diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
>index 7d9b12e71c03..6c3043f8da33 100644
>--- a/drivers/tee/optee/optee_msg.h
>+++ b/drivers/tee/optee/optee_msg.h
>@@ -136,8 +136,8 @@ struct optee_msg_param_rmem {
> * struct optee_msg_param_fmem - FF-A memory reference parameter
> * @offs_low: lower bits of offset into shared memory reference
> * @offs_high: higher bits of offset into shared memory reference
>- * @internal_offs: internal offset into the first page of shared memory
>- * reference
>+ * @internal_offs: offset into the first 4 KiB page of the FF-A shared
>+ * memory region
> * @size: size of the buffer
> * @global_id: global identifier of the shared memory
> */
>--
>2.43.0
>
© 2016 - 2026 Red Hat, Inc.