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(a)nvidia.com>
Acked-by: James Hurley <jahurley(a)nvidia.com>
Acked-by: Dave Thompson <davthompson(a)nvidia.com>
Signed-off-by: Mahantesh Salimath <mahantesh(a)nvidia.com>
---
drivers/tee/optee/ffa_abi.c | 66 +++++++++++++++++++++++++++++------
drivers/tee/optee/optee_msg.h | 4 +--
2 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index 633715b98625..2a37e4899dc6 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -187,6 +187,40 @@ static int optee_ffa_from_msg_param(struct optee *optee,
return 0;
}
+/*
+ * OP-TEE FF-A memory objects use 4 KiB pages while the kernel page size may
+ * be larger, for example 64 KiB on arm64. The FF-A descriptor is registered
+ * from the 4 KiB page containing the start of the shared buffer, so
+ * internal_offs is the offset into that page.
+ */
+static void optee_ffa_set_internal_offs(struct optee_msg_param_fmem *fmem,
+ struct tee_shm *shm)
+{
+ size_t page_offs = tee_shm_get_page_offset(shm);
+
+ BUILD_BUG_ON(PAGE_SIZE < FFA_PAGE_SIZE);
+
+ fmem->internal_offs = page_offs & (FFA_PAGE_SIZE - 1);
+}
+
+/*
+ * Keep shm_offs unchanged in offs_low/offs_high: it is returned to callers
+ * and may be reused for a subsequent invocation.
+ */
+static int optee_ffa_set_fmem_offsets(struct optee_msg_param_fmem *fmem,
+ struct tee_shm *shm, u64 shm_offs)
+{
+ optee_ffa_set_internal_offs(fmem, shm);
+
+ fmem->offs_low = shm_offs;
+ fmem->offs_high = shm_offs >> 32;
+ /* Check that the entire offset could be stored. */
+ if (fmem->offs_high != shm_offs >> 32)
+ return -EINVAL;
+
+ return 0;
+}
+
static int to_msg_param_ffa_mem(struct optee_msg_param *mp,
const struct tee_param *p)
{
@@ -196,14 +230,8 @@ static int to_msg_param_ffa_mem(struct optee_msg_param *mp,
TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
if (shm) {
- u64 shm_offs = p->u.memref.shm_offs;
-
- mp->u.fmem.internal_offs = shm->offset;
-
- mp->u.fmem.offs_low = shm_offs;
- mp->u.fmem.offs_high = shm_offs >> 32;
- /* Check that the entire offset could be stored. */
- if (mp->u.fmem.offs_high != shm_offs >> 32)
+ if (optee_ffa_set_fmem_offsets(&mp->u.fmem, shm,
+ p->u.memref.shm_offs))
return -EINVAL;
mp->u.fmem.global_id = shm->sec_world_id;
@@ -284,14 +312,30 @@ static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm,
.nattrs = 1,
};
struct sg_table sgt;
+ size_t page_offs;
+ size_t ffa_offs;
+ size_t ffa_size;
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);
+ page_offs = tee_shm_get_page_offset(shm);
+ ffa_offs = round_down(page_offs, FFA_PAGE_SIZE);
+ ffa_size = num_pages * PAGE_SIZE - ffa_offs;
+
+ /*
+ * 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 page_offs & (FFA_PAGE_SIZE - 1) for internal_offs.
+ */
+ rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, ffa_offs,
+ ffa_size, GFP_KERNEL);
if (rc)
return rc;
args.sg = sgt.sgl;
@@ -458,8 +502,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,
};
+ optee_ffa_set_internal_offs(&arg->params[0].u.fmem, shm);
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
tee_dyn_shm_alloc_helper() derives nr_pages from a caller-supplied size
and passes it to alloc_pages_exact() without checking it. For size == 0
nr_pages is 0, and alloc_pages_exact(0) calls get_order(0), which is
documented as undefined and returns BITS_PER_LONG - PAGE_SHIFT. The page
allocator then trips its order > MAX_PAGE_ORDER warning and fails the
allocation; on a panic_on_warn kernel that ends the boot.
This can be triggered by TEE_IOC_SHM_ALLOC with struct
tee_ioctl_shm_alloc_data where size is 0.
Reject a zero page count, as register_shm_helper() already does for the
register path.
Fixes: cf4441503e20 ("tee: optee: Move pool_op helper functions")
Cc: stable(a)vger.kernel.org
Cc: lvc-project(a)linuxtesting.org
Signed-off-by: Georgiy Osokin <g.osokin(a)auroraos.dev>
---
drivers/tee/tee_shm.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 6742b3579..daa4af1e0 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -343,6 +343,10 @@ int tee_dyn_shm_alloc_helper(struct tee_shm *shm, size_t size, size_t align,
unsigned int i;
int rc = 0;
+ /* get_order(0) is undefined and exceeds MAX_PAGE_ORDER. */
+ if (!nr_pages)
+ return -EINVAL;
+
/*
* Ignore alignment since this is already going to be page aligned
* and there's no need for any larger alignment.
base-commit: 72d3fcf802c45d00b300f25b848a93c3a2bd7c7e
--
2.54.0
From: Marouene Boubakri <marouene.boubakri(a)oss.nxp.com>
The OP-TEE driver reaches OP-TEE through the SMC ABI or the FF-A ABI,
both specific to Arm, yet builds both unconditionally together with the
SMC Calling Convention definitions they rely on: ffa_abi.c is always
compiled and only its registration is conditioned on
IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT), and optee_private.h includes
<linux/arm-smccc.h> and defines the SMC and FF-A specific types for
every file of the driver. This is fine as long as the driver depends on
HAVE_ARM_SMCCC, but it keeps the driver from being built for an
architecture without SMCCC, such as RISC-V.
Build smc_abi.c only when HAVE_ARM_SMCCC is set and ffa_abi.c only when
the FF-A transport is enabled, and provide stubs for their registration
otherwise, so that it fails with -EOPNOTSUPP as the FF-A ABI already
does when the FF-A transport is not reachable. Keep the SMCCC header,
the SMC invoke function type, the SMC and FF-A specific structures and
the SMC RPC register parameters in optee_private.h under the same
conditions, and drop the unused <linux/arm-smccc.h> include from
notif.c.
Make OPTEE depend on ARM_FFA_TRANSPORT || !ARM_FFA_TRANSPORT, as it
already does for RPMB, so that the driver is limited to a module when
the FF-A transport is one, rather than built in without FF-A support.
ffa_abi.c is thus built exactly when the FF-A transport is reachable
from the driver, and the IS_REACHABLE() checks in
optee_ffa_abi_register() and optee_ffa_abi_unregister() are always
true, so drop them.
OPTEE still depends on HAVE_ARM_SMCCC, so smc_abi.c is still always
built. The only visible change is that OPTEE=y can no longer be
combined with ARM_FFA_TRANSPORT=m: such a configuration now resolves to
OPTEE=m, with the FF-A ABI available.
Signed-off-by: Marouene Boubakri <marouene.boubakri(a)oss.nxp.com>
---
Changes in v4:
- Make OPTEE depend on ARM_FFA_TRANSPORT || !ARM_FFA_TRANSPORT so that
the driver is limited to =m when the FF-A transport is =m (Jens).
- Update the commit message accordingly.
v3: https://lore.kernel.org/all/20260922131735.524635-1-marouene.boubakri@oss.n…
Changes in v3:
- Dropped the RPMI ABI placeholder and the RISC-V enablement patches,
this is now a single patch (Jens).
- Key the FF-A parts of optee_private.h on IS_REACHABLE() instead of
IS_ENABLED(): with OPTEE=y and ARM_FFA_TRANSPORT=m kbuild drops
ffa_abi.o from the built-in optee.o while optee_ffa_abi_register()
was still declared, which does not link.
- Drop the now always true IS_REACHABLE() checks in
optee_ffa_abi_register() and optee_ffa_abi_unregister().
- Describe the current FF-A conditional compilation accurately in the
commit message.
- Posted as a new thread with a proper subject prefix.
v2: https://lore.kernel.org/op-tee/20260915020235.507302-2-marouene.boubakri@os…
Changes in v2:
- No code change, the testing section of the cover letter was completed.
v1: https://lore.kernel.org/op-tee/20260914175435.118303-2-marouene.boubakri@os…
drivers/tee/optee/Kconfig | 1 +
drivers/tee/optee/Makefile | 4 ++--
drivers/tee/optee/ffa_abi.c | 8 ++-----
drivers/tee/optee/notif.c | 1 -
drivers/tee/optee/optee_private.h | 39 ++++++++++++++++++++++++++++++-
5 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
index 50d2051..891dac6 100644
--- a/drivers/tee/optee/Kconfig
+++ b/drivers/tee/optee/Kconfig
@@ -5,6 +5,7 @@ config OPTEE
depends on HAVE_ARM_SMCCC
depends on MMU
depends on RPMB || !RPMB
+ depends on ARM_FFA_TRANSPORT || !ARM_FFA_TRANSPORT
help
This implements the OP-TEE Trusted Execution Environment (TEE)
driver.
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index ad7049c..183cdde 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -7,8 +7,8 @@ optee-objs += rpc.o
optee-objs += protmem.o
optee-objs += supp.o
optee-objs += device.o
-optee-objs += smc_abi.o
-optee-objs += ffa_abi.o
+optee-$(CONFIG_HAVE_ARM_SMCCC) += smc_abi.o
+optee-$(CONFIG_ARM_FFA_TRANSPORT) += ffa_abi.o
# for tracing framework to find optee_trace.h
CFLAGS_smc_abi.o := -I$(src)
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index 633715b..08236d8 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -1212,14 +1212,10 @@ static struct ffa_driver optee_ffa_driver = {
int optee_ffa_abi_register(void)
{
- if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT))
- return ffa_register(&optee_ffa_driver);
- else
- return -EOPNOTSUPP;
+ return ffa_register(&optee_ffa_driver);
}
void optee_ffa_abi_unregister(void)
{
- if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT))
- ffa_unregister(&optee_ffa_driver);
+ ffa_unregister(&optee_ffa_driver);
}
diff --git a/drivers/tee/optee/notif.c b/drivers/tee/optee/notif.c
index 6e85f2f..6801422 100644
--- a/drivers/tee/optee/notif.c
+++ b/drivers/tee/optee/notif.c
@@ -5,7 +5,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/arm-smccc.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index aefe1e6..02d6f79 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -6,7 +6,6 @@
#ifndef OPTEE_PRIVATE_H
#define OPTEE_PRIVATE_H
-#include <linux/arm-smccc.h>
#include <linux/notifier.h>
#include <linux/rhashtable.h>
#include <linux/rpmb.h>
@@ -15,6 +14,10 @@
#include <linux/types.h>
#include "optee_msg.h"
+#ifdef CONFIG_HAVE_ARM_SMCCC
+#include <linux/arm-smccc.h>
+#endif
+
#define DRIVER_NAME "optee"
#define OPTEE_MAX_ARG_SIZE 1024
@@ -42,10 +45,12 @@
*/
#define OPTEE_DEFAULT_MAX_NOTIF_VALUE 255
+#ifdef CONFIG_HAVE_ARM_SMCCC
typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long,
struct arm_smccc_res *);
+#endif
/**
* struct optee_call_waiter - TEE entry may need to wait for a free TEE thread
@@ -119,6 +124,7 @@ struct optee_supp {
struct completion reqs_c;
};
+#ifdef CONFIG_HAVE_ARM_SMCCC
/**
* struct optee_pcpu - per cpu notif private struct passed to work functions
* @optee: optee device reference
@@ -149,7 +155,9 @@ struct optee_smc {
struct work_struct notif_pcpu_work;
unsigned int notif_cpuhp_state;
};
+#endif
+#if IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT)
/**
* struct optee_ffa - FFA communication struct
* @ffa_dev: FFA device, contains the destination id, the id of
@@ -170,6 +178,7 @@ struct optee_ffa {
struct workqueue_struct *notif_wq;
struct work_struct notif_work;
};
+#endif
struct optee;
@@ -257,8 +266,12 @@ struct optee {
const struct optee_ops *ops;
struct tee_context *ctx;
union {
+#ifdef CONFIG_HAVE_ARM_SMCCC
struct optee_smc smc;
+#endif
+#if IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT)
struct optee_ffa ffa;
+#endif
};
struct optee_shm_arg_cache shm_arg_cache;
struct optee_call_queue call_queue;
@@ -290,6 +303,7 @@ struct optee_context_data {
struct list_head sess_list;
};
+#ifdef CONFIG_HAVE_ARM_SMCCC
struct optee_rpc_param {
u32 a0;
u32 a1;
@@ -300,6 +314,7 @@ struct optee_rpc_param {
u32 a6;
u32 a7;
};
+#endif
/* Holds context that is preserved during one STD call */
struct optee_call_ctx {
@@ -422,9 +437,31 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
}
/* Registration of the ABIs */
+#ifdef CONFIG_HAVE_ARM_SMCCC
int optee_smc_abi_register(void);
void optee_smc_abi_unregister(void);
+#else
+static inline int optee_smc_abi_register(void)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void optee_smc_abi_unregister(void)
+{
+}
+#endif
+#if IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT)
int optee_ffa_abi_register(void);
void optee_ffa_abi_unregister(void);
+#else
+static inline int optee_ffa_abi_register(void)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void optee_ffa_abi_unregister(void)
+{
+}
+#endif
#endif /*OPTEE_PRIVATE_H*/
base-commit: 827751b699b79a6e569983359c02dce67f81b94c
--
2.43.0