* [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V
@ 2026-10-13 5:41 Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 1/9] tee: optee: share the global shared-memory ID mapping Amirreza Zarrabi
` (8 more replies)
0 siblings, 9 replies; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
This series adds an RPMI backend to the OP-TEE driver for RISC-V.
It builds on the separately posted RPMI TEE transport series [1],
which provides service discovery, synchronous calls, memory parcels
and asynchronous signals.
This revision is effectively a full rewrite of the original RFC.
The generic RPMI transport has been separated from the OP-TEE backend,
and the backend has been reworked around the service bus and a new
OP-TEE control ABI.
Unlike v1, the OP-TEE backend no longer manages SBI MPXY mailbox
channels or implements RPMI TEE service-group operations directly.
It binds to the OP-TEE service UUID on the RPMI TEE bus and uses the
transport's public operations. No additional OP-TEE device-tree node
is required.
The backend reuses the common OP-TEE session, shared-memory, argument
cache, call queue and RPC infrastructure. Shared memory is represented
by RPMI parcel IDs and nonces. Yielding calls pass command and RPC
buffer ranges within a parcel and resume suspended execution using
an opaque token returned by OP-TEE.
The OP-TEE control ABI uses fixed-width little-endian messages carried
in TEE_CALL payloads rather than reproducing FF-A's register-based
message layout. Asynchronous notifications use an allocated TEE-to-REE
signal as a bottom-half doorbell, while ordinary logical notifications
continue through the common RPC path.
Matching OP-TEE OS support has been implemented locally. OpenSBI
integration and end-to-end validation remain pending. This remains an
RFC for review of the backend integration and Linux-to-OP-TEE protocol.
This series depends on:
- The RPMI TEE transport series [1].
- The OP-TEE notification bitmap fix, which allocates space for the
inclusive maximum notification key [2].
[1] https://lore.kernel.org/op-tee/20261007-riscv-rpmi-tee-abi-v2-0-45939a13d223@oss.qualcomm.com/
[2] https://lore.kernel.org/op-tee/20261009-fix-tee-max-keys-v1-1-1462d96bf438@oss.qualcomm.com/
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
Changes in v3:
- Use GlobalPlatform TEE result codes for OP-TEE control responses,
keeping them separate from RPMI transport errors.
- Combine the parcel ID and nonce into a single 64-bit global_id.
- Make resume tokens opaque 32-bit values and track initial versus
resumed calls separately.
- Remove unnecessary __packed annotations from the OP-TEE ABI structures.
- Share the global-ID-to-SHM mapping implementation between the FF-A
and RPMI backends.
- Replace the fixed SHM nonce with a per-instance IDA allocator; reuse
nonces after successful reclamation.
- Report the maximum notification key instead of a notification count,
and use the negotiated maximum during initialization.
- Consume capability-exchange results during probe.
- Remove cleanup macros and use explicit resource-management paths.
- Use rc consistently for Linux error returns.
- Replace unnecessary unaligned accesses with endian conversions for
naturally aligned ABI fields.
- Link to v2: https://lore.kernel.org/r/20261005-rpmi-tee-service-grp-dev-v2-0-72f222e23ec1@oss.qualcomm.com
Changes in v2:
- Effectively rewrite the backend around the separately posted RPMI
TEE transport and a new OP-TEE control ABI.
- Remove backend-owned per-hart mailbox channels and the OP-TEE-specific
device-tree binding.
- Replace the register-like control payload with fixed-width messages,
explicit command/RPC buffer ranges and opaque resume tokens.
- Add a parcel-reference parameter layout carrying the parcel ID,
nonce, and 64-bit offset and size without changing parameter size.
- Make argument-offset support part of the baseline ABI and retain
the common shared argument cache.
- Use a transport-allocated signal for asynchronous bottom-half
notifications.
- Link to v1: https://lore.kernel.org/r/20260912-rpmi-tee-service-grp-dev-v1-0-1d1d35c2a859@oss.qualcomm.com
---
Amirreza Zarrabi (9):
tee: optee: share the global shared-memory ID mapping
tee: optee: allow RPMI transport builds on RISC-V
tee: optee: define the RPMI control and parcel-reference ABI
tee: optee: add RPMI shared-memory and parameter support
tee: optee: add RPMI dynamic shared-memory pool
tee: optee: add RPMI RPC handling
tee: optee: execute yielding RPMI calls
tee: optee: bind RPMI services and negotiate backend capabilities
tee: optee: support RPMI asynchronous notification doorbells
drivers/tee/Kconfig | 3 +-
drivers/tee/optee/Kconfig | 3 +-
drivers/tee/optee/Makefile | 2 +
drivers/tee/optee/call.c | 2 +
drivers/tee/optee/core.c | 10 +-
drivers/tee/optee/ffa_abi.c | 133 +----
drivers/tee/optee/optee_msg.h | 23 +-
drivers/tee/optee/optee_private.h | 73 ++-
drivers/tee/optee/optee_rpmi.h | 263 ++++++++++
drivers/tee/optee/rpmi_abi.c | 972 +++++++++++++++++++++++++++++++++++++
drivers/tee/optee/shm_handle_map.c | 138 ++++++
11 files changed, 1498 insertions(+), 124 deletions(-)
---
base-commit: bf1ee2bd5c2da8992f33c8950ef194aaff74ed6c
change-id: 20260912-rpmi-tee-service-grp-dev-b2ce2f63e0df
Best regards,
--
Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH RFC v3 1/9] tee: optee: share the global shared-memory ID mapping
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:56 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 2/9] tee: optee: allow RPMI transport builds on RISC-V Amirreza Zarrabi
` (7 subsequent siblings)
8 siblings, 1 reply; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
The FF-A backend maintains a hash table translating secure-world memory
handles to TEE shared-memory objects. This bookkeeping does not depend
on FF-A and is also needed by the RPMI backend.
Move the mapping and its locking into common helpers and convert the
FF-A backend to use them.
This prepares the OP-TEE driver for RPMI transport support on RISC-V.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/ffa_abi.c | 133 +++++++----------------------------
drivers/tee/optee/optee_private.h | 30 ++++++--
drivers/tee/optee/shm_handle_map.c | 138 +++++++++++++++++++++++++++++++++++++
4 files changed, 188 insertions(+), 114 deletions(-)
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index 183cdde1ac04..ec2efc27ef05 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -7,6 +7,7 @@ optee-objs += rpc.o
optee-objs += protmem.o
optee-objs += supp.o
optee-objs += device.o
+optee-objs += shm_handle_map.o
optee-$(CONFIG_HAVE_ARM_SMCCC) += smc_abi.o
optee-$(CONFIG_ARM_FFA_TRANSPORT) += ffa_abi.o
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index 702da7d98fbc..5a926353c653 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -22,100 +22,15 @@
* This file implement the FF-A ABI used when communicating with secure world
* OP-TEE OS via FF-A.
* This file is divided into the following sections:
- * 1. Maintain a hash table for lookup of a global FF-A memory handle
- * 2. Convert between struct tee_param and struct optee_msg_param
- * 3. Low level support functions to register shared memory in secure world
- * 4. Dynamic shared memory pool based on alloc_pages()
- * 5. Do a normal scheduled call into secure world
- * 6. Driver initialization.
+ * 1. Convert between struct tee_param and struct optee_msg_param
+ * 2. Low level support functions to register shared memory in secure world
+ * 3. Dynamic shared memory pool based on alloc_pages()
+ * 4. Do a normal scheduled call into secure world
+ * 5. Driver initialization.
*/
/*
- * 1. Maintain a hash table for lookup of a global FF-A memory handle
- *
- * FF-A assigns a global memory handle for each piece shared memory.
- * This handle is then used when communicating with secure world.
- *
- * Main functions are optee_shm_add_ffa_handle() and optee_shm_rem_ffa_handle()
- */
-struct shm_rhash {
- struct tee_shm *shm;
- u64 global_id;
- struct rhash_head linkage;
-};
-
-static void rh_free_fn(void *ptr, void *arg)
-{
- kfree(ptr);
-}
-
-static const struct rhashtable_params shm_rhash_params = {
- .head_offset = offsetof(struct shm_rhash, linkage),
- .key_len = sizeof(u64),
- .key_offset = offsetof(struct shm_rhash, global_id),
- .automatic_shrinking = true,
-};
-
-static struct tee_shm *optee_shm_from_ffa_handle(struct optee *optee,
- u64 global_id)
-{
- struct tee_shm *shm = NULL;
- struct shm_rhash *r;
-
- mutex_lock(&optee->ffa.mutex);
- r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
- shm_rhash_params);
- if (r)
- shm = r->shm;
- mutex_unlock(&optee->ffa.mutex);
-
- return shm;
-}
-
-static int optee_shm_add_ffa_handle(struct optee *optee, struct tee_shm *shm,
- u64 global_id)
-{
- struct shm_rhash *r;
- int rc;
-
- r = kmalloc_obj(*r);
- if (!r)
- return -ENOMEM;
- r->shm = shm;
- r->global_id = global_id;
-
- mutex_lock(&optee->ffa.mutex);
- rc = rhashtable_lookup_insert_fast(&optee->ffa.global_ids, &r->linkage,
- shm_rhash_params);
- mutex_unlock(&optee->ffa.mutex);
-
- if (rc)
- kfree(r);
-
- return rc;
-}
-
-static int optee_shm_rem_ffa_handle(struct optee *optee, u64 global_id)
-{
- struct shm_rhash *r;
- int rc = -ENOENT;
-
- mutex_lock(&optee->ffa.mutex);
- r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
- shm_rhash_params);
- if (r)
- rc = rhashtable_remove_fast(&optee->ffa.global_ids,
- &r->linkage, shm_rhash_params);
- mutex_unlock(&optee->ffa.mutex);
-
- if (!rc)
- kfree(r);
-
- return rc;
-}
-
-/*
- * 2. Convert between struct tee_param and struct optee_msg_param
+ * 1. Convert between struct tee_param and struct optee_msg_param
*
* optee_ffa_from_msg_param() and optee_ffa_to_msg_param() are the main
* functions.
@@ -133,7 +48,8 @@ static void from_msg_param_ffa_mem(struct optee *optee, struct tee_param *p,
p->u.memref.size = mp->u.fmem.size;
if (mp->u.fmem.global_id != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID)
- shm = optee_shm_from_ffa_handle(optee, mp->u.fmem.global_id);
+ shm = optee_shm_handle_map_lookup(&optee->ffa.shm_map,
+ mp->u.fmem.global_id);
p->u.memref.shm = shm;
if (shm) {
@@ -262,7 +178,7 @@ static int optee_ffa_to_msg_param(struct optee *optee,
}
/*
- * 3. Low level support functions to register shared memory in secure world
+ * 2. Low level support functions to register shared memory in secure world
*
* Functions to register and unregister shared memory both for normal
* clients and for tee-supplicant.
@@ -313,7 +229,7 @@ static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm,
if (rc)
return rc;
- rc = optee_shm_add_ffa_handle(optee, shm, args.g_handle);
+ rc = optee_shm_handle_map_add(&optee->ffa.shm_map, shm, args.g_handle);
if (rc) {
mem_ops->memory_reclaim(args.g_handle, 0);
return rc;
@@ -339,7 +255,7 @@ static int optee_ffa_shm_unregister(struct tee_context *ctx,
};
int rc;
- optee_shm_rem_ffa_handle(optee, global_handle);
+ optee_shm_handle_map_remove(&optee->ffa.shm_map, global_handle);
shm->sec_world_id = 0;
rc = msg_ops->sync_send_receive(ffa_dev, &data);
@@ -367,7 +283,7 @@ static int optee_ffa_shm_unregister_supp(struct tee_context *ctx,
* this ID.
*/
- optee_shm_rem_ffa_handle(optee, global_handle);
+ optee_shm_handle_map_remove(&optee->ffa.shm_map, global_handle);
mem_ops = optee->ffa.ffa_dev->ops->mem_ops;
rc = mem_ops->memory_reclaim(global_handle, 0);
if (rc)
@@ -379,7 +295,7 @@ static int optee_ffa_shm_unregister_supp(struct tee_context *ctx,
}
/*
- * 4. Dynamic shared memory pool based on alloc_pages()
+ * 3. Dynamic shared memory pool based on alloc_pages()
*
* Implements an OP-TEE specific shared memory pool.
* The main function is optee_ffa_shm_pool_alloc_pages().
@@ -428,7 +344,7 @@ static struct tee_shm_pool *optee_ffa_shm_pool_alloc_pages(void)
}
/*
- * 5. Do a normal scheduled call into secure world
+ * 4. Do a normal scheduled call into secure world
*
* The function optee_ffa_do_call_with_arg() performs a normal scheduled
* call into secure world. During this call may normal world request help
@@ -488,7 +404,8 @@ static void handle_ffa_rpc_func_cmd_shm_free(struct tee_context *ctx,
arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
goto err_bad_param;
- shm = optee_shm_from_ffa_handle(optee, arg->params[0].u.value.b);
+ shm = optee_shm_handle_map_lookup(&optee->ffa.shm_map,
+ arg->params[0].u.value.b);
if (!shm)
goto err_bad_param;
switch (arg->params[0].u.value.a) {
@@ -736,7 +653,8 @@ static int optee_ffa_lend_protmem(struct optee *optee, struct tee_shm *protmem,
if (rc)
goto err_reclaim;
- rc = optee_shm_add_ffa_handle(optee, protmem, args.g_handle);
+ rc = optee_shm_handle_map_add(&optee->ffa.shm_map, protmem,
+ args.g_handle);
if (rc)
goto err_unreg;
@@ -770,7 +688,7 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
};
int rc;
- optee_shm_rem_ffa_handle(optee, global_handle);
+ optee_shm_handle_map_remove(&optee->ffa.shm_map, global_handle);
protmem->sec_world_id = 0;
rc = msg_ops->sync_send_receive(ffa_dev, &data);
@@ -785,9 +703,9 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
}
/*
- * 6. Driver initialization
+ * 5. Driver initialization
*
- * During driver inititialization is the OP-TEE Secure Partition is probed
+ * During driver initialization is the OP-TEE Secure Partition is probed
* to find out which features it supports so the driver can be initialized
* with a matching configuration.
*/
@@ -991,8 +909,7 @@ static void optee_ffa_remove(struct ffa_device *ffa_dev)
}
optee_remove_common(optee);
- mutex_destroy(&optee->ffa.mutex);
- rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL);
+ optee_shm_handle_map_uninit(&optee->ffa.shm_map);
kfree(optee);
}
@@ -1137,10 +1054,9 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
optee_set_dev_group(optee);
- rc = rhashtable_init(&optee->ffa.global_ids, &shm_rhash_params);
+ rc = optee_shm_handle_map_init(&optee->ffa.shm_map);
if (rc)
goto err_unreg_supp_teedev;
- mutex_init(&optee->ffa.mutex);
optee_cq_init(&optee->call_queue, 0);
optee_supp_init(&optee->supp);
optee_shm_arg_cache_init(optee, arg_cache_flags);
@@ -1193,12 +1109,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
err_close_ctx:
teedev_close_context(ctx);
err_rhashtable_free:
- rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL);
+ optee_shm_handle_map_uninit(&optee->ffa.shm_map);
rpmb_dev_put(optee->rpmb_dev);
mutex_destroy(&optee->rpmb_dev_mutex);
optee_supp_uninit(&optee->supp);
mutex_destroy(&optee->call_queue.mutex);
- mutex_destroy(&optee->ffa.mutex);
err_unreg_supp_teedev:
tee_device_unregister(optee->supp_teedev);
err_unreg_teedev:
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 02d6f79df407..92428945ee33 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -157,6 +157,29 @@ struct optee_smc {
};
#endif
+/**
+ * struct optee_shm_handle_map - Shared-memory lookup by secure-world identity
+ * @mutex: Serializes lookup, insertion and removal
+ * @rht: Maps opaque 64-bit identities to shared-memory objects
+ *
+ * Owners keep SHM objects alive while their registrations are in use.
+ * Never hold @mutex across transport calls, RPC or thread-availability waits.
+ */
+struct optee_shm_handle_map {
+ /* Protects lookup, insertion and removal. */
+ struct mutex mutex;
+ struct rhashtable rht;
+};
+
+int optee_shm_handle_map_init(struct optee_shm_handle_map *map);
+void optee_shm_handle_map_uninit(struct optee_shm_handle_map *map);
+int optee_shm_handle_map_add(struct optee_shm_handle_map *map,
+ struct tee_shm *shm, u64 global_id);
+int optee_shm_handle_map_remove(struct optee_shm_handle_map *map,
+ u64 global_id);
+struct tee_shm *
+optee_shm_handle_map_lookup(struct optee_shm_handle_map *map, u64 global_id);
+
#if IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT)
/**
* struct optee_ffa - FFA communication struct
@@ -164,17 +187,14 @@ struct optee_smc {
* OP-TEE in secure world
* @bottom_half_value: notification ID used for bottom half signalling or
* U32_MAX if unused
- * @mutex: serializes access to @global_ids
- * @global_ids: FF-A shared memory global handle translation
+ * @shm_map: FF-A shared memory global handle translation
* @notif_wq: workqueue for FF-A asynchronous notification
* @notif_work: work for FF-A asynchronous notification
*/
struct optee_ffa {
struct ffa_device *ffa_dev;
u32 bottom_half_value;
- /* Serializes access to @global_ids */
- struct mutex mutex;
- struct rhashtable global_ids;
+ struct optee_shm_handle_map shm_map;
struct workqueue_struct *notif_wq;
struct work_struct notif_work;
};
diff --git a/drivers/tee/optee/shm_handle_map.c b/drivers/tee/optee/shm_handle_map.c
new file mode 100644
index 000000000000..54324c9c3644
--- /dev/null
+++ b/drivers/tee/optee/shm_handle_map.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, 2023 Linaro Limited
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include "optee_private.h"
+
+struct optee_shm_handle_map_entry {
+ struct tee_shm *shm;
+ u64 global_id;
+ struct rhash_head node;
+};
+
+static const struct rhashtable_params shm_handle_map_params = {
+ .head_offset = offsetof(struct optee_shm_handle_map_entry, node),
+ .key_offset = offsetof(struct optee_shm_handle_map_entry, global_id),
+ .key_len = sizeof(u64),
+ .automatic_shrinking = true,
+};
+
+static void shm_handle_map_free(void *ptr, void *arg)
+{
+ kfree(ptr);
+}
+
+/**
+ * optee_shm_handle_map_init() - Initialize a shared-memory identity map
+ * @map: Map to initialize
+ *
+ * Return: 0 on success or a negative error.
+ */
+int optee_shm_handle_map_init(struct optee_shm_handle_map *map)
+{
+ int rc;
+
+ mutex_init(&map->mutex);
+ rc = rhashtable_init(&map->rht, &shm_handle_map_params);
+ if (rc)
+ mutex_destroy(&map->mutex);
+
+ return rc;
+}
+
+/**
+ * optee_shm_handle_map_uninit() - Free a shared-memory identity map
+ * @map: Initialized map with no concurrent users
+ *
+ * Free tracking entries, not SHM objects or their secure-world registrations.
+ */
+void optee_shm_handle_map_uninit(struct optee_shm_handle_map *map)
+{
+ rhashtable_free_and_destroy(&map->rht, shm_handle_map_free, NULL);
+ mutex_destroy(&map->mutex);
+}
+
+/**
+ * optee_shm_handle_map_add() - Track a registered shared-memory object
+ * @map: Initialized map
+ * @shm: Shared-memory object kept alive by its owner
+ * @global_id: Opaque secure-world identity
+ *
+ * Return: 0 on success or a negative error.
+ */
+int optee_shm_handle_map_add(struct optee_shm_handle_map *map,
+ struct tee_shm *shm, u64 global_id)
+{
+ struct optee_shm_handle_map_entry *entry;
+ int rc;
+
+ entry = kmalloc_obj(*entry);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->shm = shm;
+ entry->global_id = global_id;
+
+ mutex_lock(&map->mutex);
+ rc = rhashtable_lookup_insert_fast(&map->rht, &entry->node,
+ shm_handle_map_params);
+ mutex_unlock(&map->mutex);
+ if (rc)
+ kfree(entry);
+
+ return rc;
+}
+
+/**
+ * optee_shm_handle_map_remove() - Remove a shared-memory tracking entry
+ * @map: Initialized map
+ * @global_id: Opaque secure-world identity
+ *
+ * Return: 0 on success, -ENOENT if absent, or a negative removal error.
+ */
+int optee_shm_handle_map_remove(struct optee_shm_handle_map *map, u64 global_id)
+{
+ struct optee_shm_handle_map_entry *entry;
+ int rc = -ENOENT;
+
+ mutex_lock(&map->mutex);
+ entry = rhashtable_lookup_fast(&map->rht, &global_id,
+ shm_handle_map_params);
+ if (entry)
+ rc = rhashtable_remove_fast(&map->rht, &entry->node,
+ shm_handle_map_params);
+ mutex_unlock(&map->mutex);
+ if (!rc)
+ kfree(entry);
+
+ return rc;
+}
+
+/**
+ * optee_shm_handle_map_lookup() - Look up a registered shared-memory object
+ * @map: Initialized map
+ * @global_id: Opaque secure-world identity
+ *
+ * The caller must keep the SHM object alive; no reference is acquired here.
+ *
+ * Return: The shared-memory object or NULL if the identity is absent.
+ */
+struct tee_shm *
+optee_shm_handle_map_lookup(struct optee_shm_handle_map *map, u64 global_id)
+{
+ struct optee_shm_handle_map_entry *entry;
+ struct tee_shm *shm = NULL;
+
+ mutex_lock(&map->mutex);
+ entry = rhashtable_lookup_fast(&map->rht, &global_id,
+ shm_handle_map_params);
+ if (entry)
+ shm = entry->shm;
+ mutex_unlock(&map->mutex);
+
+ return shm;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 2/9] tee: optee: allow RPMI transport builds on RISC-V
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 1/9] tee: optee: share the global shared-memory ID mapping Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:55 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 3/9] tee: optee: define the RPMI control and parcel-reference ABI Amirreza Zarrabi
` (6 subsequent siblings)
8 siblings, 1 reply; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
Allow the TEE subsystem and OP-TEE driver to be selected on RISC-V
systems with the RPMI TEE transport enabled. Prevent OP-TEE from being
built in when the transport is modular.
Common OP-TEE code checks the memory type when registering shared
memory. Add the RISC-V implementation so that code builds without
reaching the unsupported-architecture error. Reject mappings with
explicit non-cacheable or I/O memory types.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/Kconfig | 3 ++-
drivers/tee/optee/Kconfig | 3 ++-
drivers/tee/optee/call.c | 2 ++
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig
index 98c3ad083940..ad754b647a3c 100644
--- a/drivers/tee/Kconfig
+++ b/drivers/tee/Kconfig
@@ -2,7 +2,8 @@
# Generic Trusted Execution Environment Configuration
menuconfig TEE
tristate "Trusted Execution Environment support"
- depends on HAVE_ARM_SMCCC || COMPILE_TEST || CPU_SUP_AMD
+ depends on HAVE_ARM_SMCCC || COMPILE_TEST || CPU_SUP_AMD || \
+ RISCV_RPMI_TEE_TRANSPORT
select CRYPTO_LIB_SHA1
select DMA_SHARED_BUFFER
select GENERIC_ALLOCATOR
diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
index 891dac63cab8..cc057c76de62 100644
--- a/drivers/tee/optee/Kconfig
+++ b/drivers/tee/optee/Kconfig
@@ -2,10 +2,11 @@
# OP-TEE Trusted Execution Environment Configuration
config OPTEE
tristate "OP-TEE"
- depends on HAVE_ARM_SMCCC
+ depends on HAVE_ARM_SMCCC || RISCV_RPMI_TEE_TRANSPORT
depends on MMU
depends on RPMB || !RPMB
depends on ARM_FFA_TRANSPORT || !ARM_FFA_TRANSPORT
+ depends on RISCV_RPMI_TEE_TRANSPORT || !RISCV_RPMI_TEE_TRANSPORT
help
This implements the OP-TEE Trusted Execution Environment (TEE)
driver.
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index e046aff61828..267513cf4ec0 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -604,6 +604,8 @@ static bool is_normal_memory(pgprot_t p)
#elif defined(CONFIG_ARM64)
return ((pgprot_val(p) & PTE_ATTRINDX_MASK) == PTE_ATTRINDX(MT_NORMAL)) ||
((pgprot_val(p) & PTE_ATTRINDX_MASK) == PTE_ATTRINDX(MT_NORMAL_TAGGED));
+#elif defined(CONFIG_RISCV)
+ return !(pgprot_val(p) & _PAGE_MTMASK);
#else
#error "Unsupported architecture"
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 3/9] tee: optee: define the RPMI control and parcel-reference ABI
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 1/9] tee: optee: share the global shared-memory ID mapping Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 2/9] tee: optee: allow RPMI transport builds on RISC-V Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 4/9] tee: optee: add RPMI shared-memory and parameter support Amirreza Zarrabi
` (5 subsequent siblings)
8 siblings, 0 replies; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
Define the OP-TEE service protocol carried by RPMI TEE_CALL payloads.
Add operations for version and capability queries, shared-memory
unregistration, asynchronous notification enablement, and yielding-call
start and resume. Use fixed-width little-endian control fields and TEE
result codes for control responses.
Add a parcel memory-reference layout with a full-width 64-bit offset
to the common message parameter union. Encode the RPMI parcel ID and
nonce as an opaque 64-bit global ID. A zero nonce denotes NULL, leaving
parcel ID zero usable with a nonzero nonce.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/optee_msg.h | 23 +++-
drivers/tee/optee/optee_rpmi.h | 263 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 283 insertions(+), 3 deletions(-)
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
index 6c3043f8da33..dc331cb0dace 100644
--- a/drivers/tee/optee/optee_msg.h
+++ b/drivers/tee/optee/optee_msg.h
@@ -31,6 +31,9 @@
#define OPTEE_MSG_ATTR_TYPE_FMEM_INPUT OPTEE_MSG_ATTR_TYPE_RMEM_INPUT
#define OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT
#define OPTEE_MSG_ATTR_TYPE_FMEM_INOUT OPTEE_MSG_ATTR_TYPE_RMEM_INOUT
+#define OPTEE_MSG_ATTR_TYPE_PMEM_INPUT OPTEE_MSG_ATTR_TYPE_RMEM_INPUT
+#define OPTEE_MSG_ATTR_TYPE_PMEM_OUTPUT OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT
+#define OPTEE_MSG_ATTR_TYPE_PMEM_INOUT OPTEE_MSG_ATTR_TYPE_RMEM_INOUT
#define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9
#define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa
#define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb
@@ -149,6 +152,18 @@ struct optee_msg_param_fmem {
u64 global_id;
};
+/**
+ * struct optee_msg_param_pmem - RPMI parcel memory reference
+ * @offs: Byte offset from the start of the shared parcel
+ * @size: Reference size, or required size for a short-buffer response
+ * @global_id: Parcel ID in bits 31:0 and nonce in bits 63:32
+ */
+struct optee_msg_param_pmem {
+ u64 offs;
+ u64 size;
+ u64 global_id;
+};
+
/**
* struct optee_msg_param_value - opaque value parameter
* @a: first opaque value
@@ -169,15 +184,16 @@ struct optee_msg_param_value {
* @tmem: parameter by temporary memory reference
* @rmem: parameter by registered memory reference
* @fmem: parameter by FF-A registered memory reference
+ * @pmem: parameter by RPMI parcel memory reference
* @value: parameter by opaque value
* @octets: parameter by octet string
* @u: union holding OP-TEE msg parameter
*
* @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
* the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets,
- * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
- * OPTEE_MSG_ATTR_TYPE_RMEM_* or the alias PTEE_MSG_ATTR_TYPE_FMEM_* indicates
- * @rmem or @fmem depending on the conduit.
+ * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @u.tmem. OPTEE_MSG_ATTR_TYPE_RMEM_*
+ * and its FMEM/PMEM aliases indicate @u.rmem, @u.fmem or @u.pmem depending
+ * on the conduit.
* OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
*/
struct optee_msg_param {
@@ -186,6 +202,7 @@ struct optee_msg_param {
struct optee_msg_param_tmem tmem;
struct optee_msg_param_rmem rmem;
struct optee_msg_param_fmem fmem;
+ struct optee_msg_param_pmem pmem;
struct optee_msg_param_value value;
u8 octets[24];
} u;
diff --git a/drivers/tee/optee/optee_rpmi.h b/drivers/tee/optee/optee_rpmi.h
new file mode 100644
index 000000000000..d4cf2d927a7f
--- /dev/null
+++ b/drivers/tee/optee/optee_rpmi.h
@@ -0,0 +1,263 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#ifndef OPTEE_RPMI_H
+#define OPTEE_RPMI_H
+
+#include <linux/bitops.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/wordpart.h>
+
+/*
+ * OP-TEE service ABI over RPMI TEE_CALL.
+ *
+ * Requests and responses are carried in the TEE_CALL service payload.
+ * Control fields are little-endian. Response status fields contain unsigned
+ * 32-bit TEE result codes: TEE_SUCCESS or TEE_ERROR_*. These are distinct
+ * from the outer TEE_CALL status, which uses signed RPMI error codes, and
+ * the GP command result in optee_msg_arg.ret.
+ *
+ * Memory references use a global_id combining the parcel ID and nonce.
+ * A zero nonce denotes NULL regardless of the parcel ID. A NULL reference
+ * must specify a zero buffer offset; its requested size is preserved.
+ */
+
+#define OPTEE_RPMI_SERVICE_UUID \
+ UUID_INIT(0x486178e0, 0xe7f8, 0x11e3, \
+ 0xbc, 0x5e, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b)
+
+#define OPTEE_RPMI_VERSION_MAJOR 1
+#define OPTEE_RPMI_VERSION_MINOR 0
+
+/**
+ * optee_rpmi_global_id() - encode an opaque parcel memory identity
+ * @parcel_id: firmware-assigned parcel identifier
+ * @nonce: REE nonce supplied when sharing the parcel
+ *
+ * Return: A 64-bit identity for memory parcel.
+ */
+static inline u64 optee_rpmi_global_id(u32 parcel_id, u32 nonce)
+{
+ return ((u64)nonce << 32) | parcel_id;
+}
+
+/* Extract the firmware-assigned parcel ID from a memory identity. */
+static inline u32 optee_rpmi_parcel_id(u64 global_id)
+{
+ return lower_32_bits(global_id);
+}
+
+/* Extract the nonce; zero denotes a NULL memory reference. */
+static inline u32 optee_rpmi_nonce(u64 global_id)
+{
+ return upper_32_bits(global_id);
+}
+
+/**
+ * struct optee_rpmi_probe_req - request without operation-specific arguments
+ * @op: GET_API_VERSION, GET_OS_VERSION or EXCHANGE_CAPABILITIES
+ */
+struct optee_rpmi_probe_req {
+ __le32 op;
+};
+
+/**
+ * struct optee_rpmi_status_resp - response carrying only a TEE result
+ * @status: TEE result code; TEE_SUCCESS on success
+ */
+struct optee_rpmi_status_resp {
+ __le32 status;
+};
+
+/*
+ * Return the service API version.
+ *
+ * Request: struct optee_rpmi_probe_req
+ * Response: struct optee_rpmi_api_resp
+ */
+#define OPTEE_RPMI_GET_API_VERSION 0
+
+/**
+ * struct optee_rpmi_api_resp - GET_API_VERSION response
+ * @status: TEE result code; TEE_SUCCESS on success
+ * @major: incompatible protocol revision
+ * @minor: compatible protocol revision
+ */
+struct optee_rpmi_api_resp {
+ __le32 status;
+ __le32 major;
+ __le32 minor;
+};
+
+/*
+ * Return the trusted OS revision, not the service API revision.
+ *
+ * Request: struct optee_rpmi_probe_req
+ * Response: struct optee_rpmi_os_resp
+ */
+#define OPTEE_RPMI_GET_OS_VERSION 1
+
+/**
+ * struct optee_rpmi_os_resp - GET_OS_VERSION response
+ * @status: TEE result code; TEE_SUCCESS on success
+ * @major: trusted OS major revision
+ * @minor: trusted OS minor revision
+ * @reserved: must be zero
+ * @build_id: trusted OS build identifier, zero if unspecified
+ */
+struct optee_rpmi_os_resp {
+ __le32 status;
+ __le32 major;
+ __le32 minor;
+ __le32 reserved;
+ __le64 build_id;
+};
+
+/*
+ * Query secure-world capabilities and limits.
+ *
+ * Request: struct optee_rpmi_probe_req
+ * Response: struct optee_rpmi_caps_resp
+ */
+#define OPTEE_RPMI_EXCHANGE_CAPABILITIES 2
+
+/**
+ * struct optee_rpmi_caps_resp - EXCHANGE_CAPABILITIES response
+ * @status: TEE result code; TEE_SUCCESS on success
+ * @secure_caps: reserved for future optional features; zero in version 1
+ * @rpc_param_count: nonzero parameter capacity of each RPC argument buffer
+ * @max_notification_key: inclusive maximum logical key, including synchronous
+ * keys; zero selects the default maximum of 255
+ *
+ * Version 1 defines no capability bits. Unknown bits are ignored by Linux
+ * for compatibility with future extensions.
+ */
+struct optee_rpmi_caps_resp {
+ __le32 status;
+ __le32 secure_caps;
+ __le32 rpc_param_count;
+ __le32 max_notification_key;
+};
+
+/*
+ * Unregister a shared parcel from OP-TEE.
+ *
+ * Request: struct optee_rpmi_unregister_req
+ * Response: struct optee_rpmi_status_resp
+ */
+#define OPTEE_RPMI_UNREGISTER_SHM 3
+
+/**
+ * struct optee_rpmi_unregister_req - retire a shared parcel in OP-TEE
+ * @op: OPTEE_RPMI_UNREGISTER_SHM
+ * @reserved: must be zero
+ * @global_id: identity of the parcel to retire; nonce must be nonzero
+ *
+ * Success means OP-TEE stopped using the mapping and released its receiver
+ * interest.
+ */
+struct optee_rpmi_unregister_req {
+ __le32 op;
+ __le32 reserved;
+ __le64 global_id;
+};
+
+/*
+ * Enable asynchronous notification delivery.
+ *
+ * Request: struct optee_rpmi_enable_notif_req
+ * Response: struct optee_rpmi_status_resp
+ */
+#define OPTEE_RPMI_ENABLE_ASYNC_NOTIF 4
+
+/**
+ * struct optee_rpmi_enable_notif_req - bind an incoming RPMI doorbell
+ * @op: OPTEE_RPMI_ENABLE_ASYNC_NOTIF
+ * @signal_id: allocated TEE-to-REE signal, not a logical notification key
+ *
+ * Success activates delivery and raises the doorbell for pending work.
+ * Raising the signal requests OPTEE_MSG_CMD_DO_BOTTOM_HALF.
+ * OPTEE_MSG_CMD_STOP_ASYNC_NOTIF stops future doorbell generation, but does
+ * not drain already-raised signals or release the signal ID.
+ */
+struct optee_rpmi_enable_notif_req {
+ __le32 op;
+ __le32 signal_id;
+};
+
+/*
+ * Start a yielding command using shared command and RPC arguments.
+ *
+ * Request: struct optee_rpmi_call_req
+ * Response: struct optee_rpmi_call_resp
+ */
+#define OPTEE_RPMI_YIELDING_CALL_WITH_ARG 5
+
+#define OPTEE_RPMI_YIELDING_CALL_RETURN_DONE 0
+#define OPTEE_RPMI_YIELDING_CALL_RETURN_RPC_CMD 1
+#define OPTEE_RPMI_YIELDING_CALL_RETURN_INTERRUPT 2
+
+/**
+ * struct optee_rpmi_call_req - start a yielding command
+ * @op: OPTEE_RPMI_YIELDING_CALL_WITH_ARG
+ * @flags: zero in version 1
+ * @global_id: argument parcel identity; nonce must be nonzero
+ * @arg_offset: command argument byte offset from the parcel's first byte
+ * @rpc_offset: RPC argument byte offset from the parcel's first byte
+ * @arg_size: command argument extent in bytes
+ * @rpc_size: RPC argument capacity in bytes
+ *
+ * Firmware validates ownership, RW access, alignment and disjoint ranges.
+ * TEE_ERROR_BUSY rejects an initial request without acquiring call ownership.
+ */
+struct optee_rpmi_call_req {
+ __le32 op;
+ __le32 flags;
+ __le64 global_id;
+ __le64 arg_offset;
+ __le64 rpc_offset;
+ __le32 arg_size;
+ __le32 rpc_size;
+};
+
+/**
+ * struct optee_rpmi_call_resp - yielding command response
+ * @status: TEE result code, distinct from the command's GP result
+ * @result: OPTEE_RPMI_YIELDING_CALL_RETURN_* value when status is success
+ * @resume_token: opaque token for a suspended command; ignored for DONE
+ *
+ * DONE releases all access to the call's argument and RPC ranges. RPC_CMD
+ * requests RPC command handling; INTERRUPT requests resumption without an
+ * RPC command. Tokens belong to one accepted call, service and caller.
+ */
+struct optee_rpmi_call_resp {
+ __le32 status;
+ __le32 result;
+ __le32 resume_token;
+};
+
+/*
+ * Resume a suspended yielding command.
+ *
+ * Request: struct optee_rpmi_resume_req
+ * Response: struct optee_rpmi_call_resp
+ */
+#define OPTEE_RPMI_YIELDING_CALL_RESUME 6
+
+/**
+ * struct optee_rpmi_resume_req - resume a suspended command
+ * @op: OPTEE_RPMI_YIELDING_CALL_RESUME
+ * @reserved: must be zero
+ * @resume_token: opaque token copied unchanged from the preceding response
+ *
+ * Resume must not return TEE_ERROR_BUSY.
+ */
+struct optee_rpmi_resume_req {
+ __le32 op;
+ __le32 reserved;
+ __le32 resume_token;
+};
+
+#endif /* OPTEE_RPMI_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 4/9] tee: optee: add RPMI shared-memory and parameter support
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
` (2 preceding siblings ...)
2026-10-13 5:41 ` [PATCH RFC v3 3/9] tee: optee: define the RPMI control and parcel-reference ABI Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:57 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 5/9] tee: optee: add RPMI dynamic shared-memory pool Amirreza Zarrabi
` (4 subsequent siblings)
8 siblings, 1 reply; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
OP-TEE commands and RPCs need shared memory that secure world can
identify through RPMI parcels.
Register normal-world pages as read-write parcels shared with the
OP-TEE endpoint. Track each parcel ID and nonce with the common
shared-memory map. Store the combined identity in tee_shm.sec_world_id
and allocate nonzero nonces to distinguish registered memory from NULL
references. Reuse nonces after successful parcel reclamation.
Add conversions between TEE parameters and parcel memory references.
On client memory unregistration, ask OP-TEE to release its mapping
before reclaiming the parcel. Supplicant memory has already been
released by OP-TEE through its SHM_FREE RPC and only needs reclaiming.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/optee_private.h | 19 +++
drivers/tee/optee/rpmi_abi.c | 319 ++++++++++++++++++++++++++++++++++++++
3 files changed, 339 insertions(+)
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index ec2efc27ef05..4794c7c0ef91 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -10,6 +10,7 @@ optee-objs += device.o
optee-objs += shm_handle_map.o
optee-$(CONFIG_HAVE_ARM_SMCCC) += smc_abi.o
optee-$(CONFIG_ARM_FFA_TRANSPORT) += ffa_abi.o
+optee-$(CONFIG_RISCV_RPMI_TEE_TRANSPORT) += rpmi_abi.o
# for tracing framework to find optee_trace.h
CFLAGS_smc_abi.o := -I$(src)
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 92428945ee33..efc1e37fe1bf 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -6,6 +6,7 @@
#ifndef OPTEE_PRIVATE_H
#define OPTEE_PRIVATE_H
+#include <linux/idr.h>
#include <linux/notifier.h>
#include <linux/rhashtable.h>
#include <linux/rpmb.h>
@@ -200,6 +201,20 @@ struct optee_ffa {
};
#endif
+#if IS_REACHABLE(CONFIG_RISCV_RPMI_TEE_TRANSPORT)
+/**
+ * struct optee_rpmi - RPMI transport-specific OP-TEE state
+ * @rdev: owning RPMI service device
+ * @shm_map: lookup by the host-endian parcel ID and nonce pair
+ * @nonce_ida: nonzero SHM nonces, reusable after successful parcel reclamation
+ */
+struct optee_rpmi {
+ struct rpmi_tee_device *rdev;
+ struct optee_shm_handle_map shm_map;
+ struct ida nonce_ida;
+};
+#endif
+
struct optee;
/**
@@ -260,6 +275,7 @@ struct optee_ops {
* @ctx: driver internal TEE context
* @smc: specific to SMC ABI
* @ffa: specific to FF-A ABI
+ * @rpmi: specific to RPMI ABI
* @shm_arg_cache: shared memory cache argument
* @call_queue: queue of threads waiting to call @invoke_fn
* @notif: notification synchronization struct
@@ -291,6 +307,9 @@ struct optee {
#endif
#if IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT)
struct optee_ffa ffa;
+#endif
+#if IS_REACHABLE(CONFIG_RISCV_RPMI_TEE_TRANSPORT)
+ struct optee_rpmi rpmi;
#endif
};
struct optee_shm_arg_cache shm_arg_cache;
diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
new file mode 100644
index 000000000000..940676794280
--- /dev/null
+++ b/drivers/tee/optee/rpmi_abi.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/mailbox/riscv-rpmi-message.h>
+#include <linux/overflow.h>
+#include <linux/rpmi_tee.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+#include "optee_private.h"
+#include "optee_rpmi.h"
+
+/* Keep transport errors separate from the control status in a response. */
+static int optee_rpmi_call_with_status(struct optee *optee,
+ const void *req, size_t req_len,
+ void *resp, size_t resp_size,
+ u32 *status)
+{
+ struct rpmi_tee_device *rdev = optee->rpmi.rdev;
+ size_t received = resp_size;
+ int rc;
+
+ rc = rdev->ops->msg_ops->call(rdev, req, req_len, resp, &received);
+ if (rc)
+ return rc;
+
+ if (received != resp_size)
+ return -EPROTO;
+
+ *status = get_unaligned_le32(resp);
+
+ return 0;
+}
+
+/**
+ * optee_rpmi_call - Send a control request and check its TEE result
+ * @optee: OP-TEE instance.
+ * @req: Control request, including the operation number.
+ * @req_len: Request size in bytes.
+ * @resp: Response buffer, beginning with a little-endian TEE result.
+ * @resp_size: Exact expected response size, including the status field.
+ *
+ * Return: 0 on success, a transport error, -EPROTO for an unexpected response
+ * size, or -EIO for an unsuccessful control response.
+ */
+static int optee_rpmi_call(struct optee *optee, const void *req, size_t req_len,
+ void *resp, size_t resp_size)
+{
+ u32 status;
+ int rc;
+
+ rc = optee_rpmi_call_with_status(optee, req, req_len, resp, resp_size,
+ &status);
+ if (rc)
+ return rc;
+ else if (status != TEEC_SUCCESS)
+ return -EIO;
+
+ return 0;
+}
+
+static int optee_rpmi_shm_register(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages,
+ unsigned long start)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct rpmi_tee_device *rdev = optee->rpmi.rdev;
+ struct rpmi_tee_mem_receiver receiver = {
+ .endpoint_id = rdev->endpoint_id,
+ .access = RPMI_TEE_MEM_ACCESS_READ | RPMI_TEE_MEM_ACCESS_WRITE,
+ };
+ struct rpmi_tee_mem_args args = {
+ .receivers = &receiver,
+ .receiver_count = 1,
+ .creator_access = RPMI_TEE_MEM_ACCESS_READ |
+ RPMI_TEE_MEM_ACCESS_WRITE,
+ };
+ struct sg_table sgt;
+ u64 global_id;
+ int rc;
+
+ 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);
+ if (rc)
+ return rc;
+
+ rc = ida_alloc_min(&optee->rpmi.nonce_ida, 1, GFP_KERNEL);
+ if (rc < 0) {
+ sg_free_table(&sgt);
+ return rc;
+ }
+
+ args.nonce = rc;
+ args.sg = sgt.sgl;
+ rc = rdev->ops->mem_ops->memory_share(rdev, &args);
+ sg_free_table(&sgt);
+ /* A failed exchange may leave a parcel using this nonce. */
+ if (rc)
+ return rc;
+
+ global_id = optee_rpmi_global_id(args.parcel_id, args.nonce);
+
+ rc = optee_shm_handle_map_add(&optee->rpmi.shm_map, shm, global_id);
+ if (rc) {
+ int rrc;
+
+ rrc = rdev->ops->mem_ops->memory_reclaim(rdev, args.parcel_id);
+ /* Keep the nonce if firmware may still hold the parcel. */
+ if (rrc)
+ dev_err(&rdev->dev, "reclaim parcel %#x failed: %d\n",
+ args.parcel_id, rrc);
+ else
+ ida_free(&optee->rpmi.nonce_ida, args.nonce);
+
+ return rc;
+ }
+
+ shm->sec_world_id = global_id;
+
+ return 0;
+}
+
+static int optee_rpmi_shm_unregister(struct tee_context *ctx,
+ struct tee_shm *shm)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct rpmi_tee_device *rdev = optee->rpmi.rdev;
+ struct optee_rpmi_unregister_req req = {
+ .op = cpu_to_le32(OPTEE_RPMI_UNREGISTER_SHM),
+ .global_id = cpu_to_le64(shm->sec_world_id),
+ };
+ struct optee_rpmi_status_resp resp;
+ u32 parcel_id = optee_rpmi_parcel_id(shm->sec_world_id);
+ u32 nonce = optee_rpmi_nonce(shm->sec_world_id);
+ int rc;
+
+ optee_shm_handle_map_remove(&optee->rpmi.shm_map, shm->sec_world_id);
+ shm->sec_world_id = 0;
+
+ rc = optee_rpmi_call(optee, &req, sizeof(req), &resp, sizeof(resp));
+ if (rc)
+ dev_err(&rdev->dev, "unregister parcel %#x failed: %d\n",
+ parcel_id, rc);
+
+ rc = rdev->ops->mem_ops->memory_reclaim(rdev, parcel_id);
+ /* Keep the nonce if firmware may still hold the parcel. */
+ if (rc)
+ dev_err(&rdev->dev, "reclaim parcel %#x failed: %d\n",
+ parcel_id, rc);
+ else
+ ida_free(&optee->rpmi.nonce_ida, nonce);
+
+ return rc;
+}
+
+static int optee_rpmi_shm_unregister_supp(struct tee_context *ctx,
+ struct tee_shm *shm)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct rpmi_tee_device *rdev = optee->rpmi.rdev;
+ u32 parcel_id = optee_rpmi_parcel_id(shm->sec_world_id);
+ u32 nonce = optee_rpmi_nonce(shm->sec_world_id);
+ int rc;
+
+ optee_shm_handle_map_remove(&optee->rpmi.shm_map, shm->sec_world_id);
+ shm->sec_world_id = 0;
+ /* OP-TEE has already retired the parcel through SHM_FREE RPC. */
+ rc = rdev->ops->mem_ops->memory_reclaim(rdev, parcel_id);
+ /* Keep the nonce if firmware may still hold the parcel. */
+ if (rc)
+ dev_err(&rdev->dev, "reclaim parcel %#x failed: %d\n",
+ parcel_id, rc);
+ else
+ ida_free(&optee->rpmi.nonce_ida, nonce);
+
+ return rc;
+}
+
+/* Convert a memory reference to an OP-TEE RPMI parcel reference. */
+static int to_msg_param_rpmi_mem(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ struct tee_shm *shm = p->u.memref.shm;
+
+ mp->attr = OPTEE_MSG_ATTR_TYPE_PMEM_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+ memset(&mp->u, 0, sizeof(mp->u));
+ /* For !shm, a zero global_id encodes a NULL reference. */
+ if (shm) {
+ if (check_add_overflow((u64)shm->offset,
+ (u64)p->u.memref.shm_offs,
+ &mp->u.pmem.offs))
+ return -EINVAL;
+
+ mp->u.pmem.global_id = shm->sec_world_id;
+ }
+
+ mp->u.pmem.size = p->u.memref.size;
+
+ return 0;
+}
+
+static int optee_rpmi_to_msg_param(struct optee *optee,
+ struct optee_msg_param *msg_params,
+ size_t num_params,
+ const struct tee_param *params)
+{
+ size_t n;
+
+ for (n = 0; n < num_params; n++) {
+ const struct tee_param *p = params + n;
+ struct optee_msg_param *mp = msg_params + n;
+
+ switch (p->attr) {
+ case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
+ mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
+ memset(&mp->u, 0, sizeof(mp->u));
+ break;
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+ optee_to_msg_param_value(mp, p);
+ break;
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+ if (to_msg_param_rpmi_mem(mp, p))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* Convert an RPMI parcel reference to a memref; callers own SHM lifetime. */
+static int from_msg_param_rpmi_mem(struct optee *optee, struct tee_param *p,
+ u32 attr, const struct optee_msg_param *mp)
+{
+ struct tee_shm *shm;
+ u64 offset;
+
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + attr -
+ OPTEE_MSG_ATTR_TYPE_PMEM_INPUT;
+
+ if (mp->u.pmem.size > SIZE_MAX)
+ return -EOVERFLOW;
+ p->u.memref.size = mp->u.pmem.size;
+
+ if (!optee_rpmi_nonce(mp->u.pmem.global_id)) {
+ /* Return NULL shm. */
+ if (mp->u.pmem.offs)
+ return -EINVAL;
+ p->u.memref.shm = NULL;
+ p->u.memref.shm_offs = 0;
+ return 0;
+ }
+
+ shm = optee_shm_handle_map_lookup(&optee->rpmi.shm_map,
+ mp->u.pmem.global_id);
+ if (!shm || mp->u.pmem.offs < shm->offset)
+ return -EINVAL;
+
+ offset = mp->u.pmem.offs - shm->offset;
+ if (offset > SIZE_MAX)
+ return -EOVERFLOW;
+
+ p->u.memref.shm = shm;
+ p->u.memref.shm_offs = offset;
+
+ return 0;
+}
+
+static int optee_rpmi_from_msg_param(struct optee *optee,
+ struct tee_param *params,
+ size_t num_params,
+ const struct optee_msg_param *msg_params)
+{
+ size_t n;
+
+ for (n = 0; n < num_params; n++) {
+ const struct optee_msg_param *mp = msg_params + n;
+ struct tee_param *p = params + n;
+ u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
+ int rc;
+
+ switch (attr) {
+ case OPTEE_MSG_ATTR_TYPE_NONE:
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+ memset(&p->u, 0, sizeof(p->u));
+ break;
+ case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
+ case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
+ case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
+ optee_from_msg_param_value(p, attr, mp);
+ break;
+ case OPTEE_MSG_ATTR_TYPE_PMEM_INPUT:
+ case OPTEE_MSG_ATTR_TYPE_PMEM_OUTPUT:
+ case OPTEE_MSG_ATTR_TYPE_PMEM_INOUT:
+ rc = from_msg_param_rpmi_mem(optee, p, attr, mp);
+ if (rc)
+ return rc;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 5/9] tee: optee: add RPMI dynamic shared-memory pool
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
` (3 preceding siblings ...)
2026-10-13 5:41 ` [PATCH RFC v3 4/9] tee: optee: add RPMI shared-memory and parameter support Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 6/9] tee: optee: add RPMI RPC handling Amirreza Zarrabi
` (3 subsequent siblings)
8 siblings, 0 replies; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
Add a page-based dynamic shared-memory pool for the RPMI backend using
the common TEE allocation and free helpers.
Register allocated pages as RPMI parcels so OP-TEE can access them.
Unregister the parcels before freeing their backing pages.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/rpmi_abi.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
index 940676794280..bbb219ddab38 100644
--- a/drivers/tee/optee/rpmi_abi.c
+++ b/drivers/tee/optee/rpmi_abi.c
@@ -183,6 +183,42 @@ static int optee_rpmi_shm_unregister_supp(struct tee_context *ctx,
return rc;
}
+static int optee_rpmi_pool_alloc(struct tee_shm_pool *pool, struct tee_shm *shm,
+ size_t size, size_t align)
+{
+ return tee_dyn_shm_alloc_helper(shm, size, align,
+ optee_rpmi_shm_register);
+}
+
+static void optee_rpmi_pool_free(struct tee_shm_pool *pool, struct tee_shm *shm)
+{
+ tee_dyn_shm_free_helper(shm, optee_rpmi_shm_unregister);
+}
+
+static void optee_rpmi_pool_destroy(struct tee_shm_pool *pool)
+{
+ kfree(pool);
+}
+
+static const struct tee_shm_pool_ops optee_rpmi_pool_ops = {
+ .alloc = optee_rpmi_pool_alloc,
+ .free = optee_rpmi_pool_free,
+ .destroy_pool = optee_rpmi_pool_destroy,
+};
+
+static struct tee_shm_pool *optee_rpmi_shm_pool_alloc(void)
+{
+ struct tee_shm_pool *pool;
+
+ pool = kzalloc_obj(*pool);
+ if (!pool)
+ return ERR_PTR(-ENOMEM);
+
+ pool->ops = &optee_rpmi_pool_ops;
+
+ return pool;
+}
+
/* Convert a memory reference to an OP-TEE RPMI parcel reference. */
static int to_msg_param_rpmi_mem(struct optee_msg_param *mp,
const struct tee_param *p)
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 6/9] tee: optee: add RPMI RPC handling
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
` (4 preceding siblings ...)
2026-10-13 5:41 ` [PATCH RFC v3 5/9] tee: optee: add RPMI dynamic shared-memory pool Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:59 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 7/9] tee: optee: execute yielding RPMI calls Amirreza Zarrabi
` (2 subsequent siblings)
8 siblings, 1 reply; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
OP-TEE can suspend a yielding call to request services from normal
world through RPC.
Add handlers for shared-memory allocation and free requests. Use the
existing kernel and supplicant allocation helpers, return RPMI parcel
references for allocated memory, and resolve those references when
freeing it.
Route other commands through the common OP-TEE RPC dispatcher. Store
RPC results in the shared arguments for OP-TEE to consume when the
yielding call resumes.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/rpmi_abi.c | 104 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 104 insertions(+)
diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
index bbb219ddab38..8b90c3653e80 100644
--- a/drivers/tee/optee/rpmi_abi.c
+++ b/drivers/tee/optee/rpmi_abi.c
@@ -12,6 +12,7 @@
#include <linux/unaligned.h>
#include "optee_private.h"
#include "optee_rpmi.h"
+#include "optee_rpc_cmd.h"
/* Keep transport errors separate from the control status in a response. */
static int optee_rpmi_call_with_status(struct optee *optee,
@@ -353,3 +354,106 @@ static int optee_rpmi_from_msg_param(struct optee *optee,
return 0;
}
+
+static void optee_rpmi_handle_rpc_shm_alloc(struct tee_context *ctx,
+ struct optee *optee,
+ struct optee_msg_arg *arg)
+{
+ struct tee_shm *shm;
+ u64 type, size;
+
+ if (arg->num_params != 1 ||
+ arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
+ goto err_bad_param;
+
+ type = arg->params[0].u.value.a;
+ size = arg->params[0].u.value.b;
+ if (!size || size > SIZE_MAX - PAGE_SIZE + 1)
+ goto err_bad_param;
+
+ switch (type) {
+ case OPTEE_RPC_SHM_TYPE_APPL:
+ shm = optee_rpc_cmd_alloc_suppl(ctx, size);
+ break;
+ case OPTEE_RPC_SHM_TYPE_KERNEL:
+ shm = tee_shm_alloc_priv_buf(optee->ctx, size);
+ break;
+ default:
+ goto err_bad_param;
+ }
+
+ if (IS_ERR(shm)) {
+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+ return;
+ }
+
+ arg->params[0] = (struct optee_msg_param) {
+ .attr = OPTEE_MSG_ATTR_TYPE_PMEM_OUTPUT,
+ .u.pmem.size = tee_shm_get_size(shm),
+ .u.pmem.global_id = shm->sec_world_id,
+ .u.pmem.offs = shm->offset,
+ };
+
+ arg->ret = TEEC_SUCCESS;
+ return;
+
+err_bad_param:
+ arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void optee_rpmi_handle_rpc_shm_free(struct tee_context *ctx,
+ struct optee *optee,
+ struct optee_msg_arg *arg)
+{
+ struct tee_shm *shm;
+ u64 type, global_id;
+
+ if (arg->num_params != 1 ||
+ arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
+ goto err_bad_param;
+
+ type = arg->params[0].u.value.a;
+ global_id = arg->params[0].u.value.b;
+ /* SHM_FREE requires a non-NULL identity. */
+ if (!optee_rpmi_nonce(global_id) || arg->params[0].u.value.c)
+ goto err_bad_param;
+
+ shm = optee_shm_handle_map_lookup(&optee->rpmi.shm_map, global_id);
+ if (!shm)
+ goto err_bad_param;
+
+ switch (type) {
+ case OPTEE_RPC_SHM_TYPE_APPL:
+ optee_rpc_cmd_free_suppl(ctx, shm);
+ break;
+ case OPTEE_RPC_SHM_TYPE_KERNEL:
+ tee_shm_free(shm);
+ break;
+ default:
+ goto err_bad_param;
+ }
+
+ arg->ret = TEEC_SUCCESS;
+ return;
+
+err_bad_param:
+ arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+/* OP-TEE leaves the shared arguments unchanged while Linux handles the RPC. */
+static void optee_rpmi_handle_rpc_cmd(struct tee_context *ctx,
+ struct optee *optee,
+ struct optee_msg_arg *arg)
+{
+ arg->ret_origin = TEEC_ORIGIN_COMMS;
+ switch (arg->cmd) {
+ case OPTEE_RPC_CMD_SHM_ALLOC:
+ optee_rpmi_handle_rpc_shm_alloc(ctx, optee, arg);
+ break;
+ case OPTEE_RPC_CMD_SHM_FREE:
+ optee_rpmi_handle_rpc_shm_free(ctx, optee, arg);
+ break;
+ default:
+ optee_rpc_cmd(ctx, optee, arg);
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 7/9] tee: optee: execute yielding RPMI calls
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
` (5 preceding siblings ...)
2026-10-13 5:41 ` [PATCH RFC v3 6/9] tee: optee: add RPMI RPC handling Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:56 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 8/9] tee: optee: bind RPMI services and negotiate backend capabilities Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 9/9] tee: optee: support RPMI asynchronous notification doorbells Amirreza Zarrabi
8 siblings, 1 reply; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
OP-TEE commands can span multiple exchanges with normal world. A call
may yield to request an RPC service or allow interrupt processing
before continuing execution in secure world.
Add the RPMI yielding-call path used by the common OP-TEE session
operations. Submit the command and RPC argument buffers as ranges
within a shared memory parcel.
Handle RPC requests while the call is suspended and resume execution
using the token returned by OP-TEE until the command completes.
Use the common OP-TEE call queue to wait when an initial request is
rejected with TEE_ERROR_BUSY, allowing another active call to complete
before retrying.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/rpmi_abi.c | 122 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
index 8b90c3653e80..9a6439e15a33 100644
--- a/drivers/tee/optee/rpmi_abi.c
+++ b/drivers/tee/optee/rpmi_abi.c
@@ -8,6 +8,7 @@
#include <linux/mailbox/riscv-rpmi-message.h>
#include <linux/overflow.h>
#include <linux/rpmi_tee.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/unaligned.h>
#include "optee_private.h"
@@ -457,3 +458,124 @@ static void optee_rpmi_handle_rpc_cmd(struct tee_context *ctx,
optee_rpc_cmd(ctx, optee, arg);
}
}
+
+/* Handle RPC command or interrupt returns from a yielding call. */
+static void optee_rpmi_handle_rpc(struct tee_context *ctx, struct optee *optee,
+ u32 result, struct optee_msg_arg *arg)
+{
+ switch (result) {
+ case OPTEE_RPMI_YIELDING_CALL_RETURN_RPC_CMD:
+ optee_rpmi_handle_rpc_cmd(ctx, optee, arg);
+ break;
+ case OPTEE_RPMI_YIELDING_CALL_RETURN_INTERRUPT:
+ break;
+ default:
+ pr_warn("Unknown RPC func 0x%x\n", result);
+ break;
+ }
+}
+
+/**
+ * optee_rpmi_yielding_call() - submit and resume a yielding RPMI command
+ * @ctx: calling context
+ * @req: initial command request
+ * @rpc_arg: shared RPC argument buffer
+ * @system_thread: caller requests TEE system thread support
+ *
+ * Only TEE_ERROR_BUSY rejection of the initial command permits retry.
+ *
+ * Return: zero on completion, or a negative error.
+ */
+static int optee_rpmi_yielding_call(struct tee_context *ctx,
+ const struct optee_rpmi_call_req *req,
+ struct optee_msg_arg *rpc_arg,
+ bool system_thread)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_rpmi_resume_req resume = {
+ .op = cpu_to_le32(OPTEE_RPMI_YIELDING_CALL_RESUME),
+ };
+ struct optee_rpmi_call_resp resp;
+ struct optee_call_waiter waiter;
+ bool resuming = false;
+ u32 result, status;
+ int rc;
+
+ optee_cq_wait_init(&optee->call_queue, &waiter, system_thread);
+ while (true) {
+ if (resuming)
+ rc = optee_rpmi_call_with_status(optee, &resume,
+ sizeof(resume),
+ &resp, sizeof(resp),
+ &status);
+ else
+ rc = optee_rpmi_call_with_status(optee, req,
+ sizeof(*req),
+ &resp, sizeof(resp),
+ &status);
+ if (rc)
+ goto done;
+
+ switch (status) {
+ case TEEC_SUCCESS:
+ break;
+ case TEEC_ERROR_BUSY:
+ if (!resuming) {
+ optee_cq_wait_for_completion(&optee->call_queue,
+ &waiter);
+ continue;
+ }
+
+ fallthrough;
+ default:
+ rc = -EIO;
+ goto done;
+ }
+
+ result = le32_to_cpu(resp.result);
+ if (result == OPTEE_RPMI_YIELDING_CALL_RETURN_DONE)
+ goto done;
+
+ cond_resched();
+ optee_rpmi_handle_rpc(ctx, optee, result, rpc_arg);
+
+ resume.resume_token = resp.resume_token;
+ resuming = true;
+ }
+done:
+ optee_cq_wait_final(&optee->call_queue, &waiter);
+
+ return rc;
+}
+
+/* The caller supplies SHM with room for command and RPC args. */
+static int optee_rpmi_do_call_with_arg(struct tee_context *ctx,
+ struct tee_shm *shm, u_int offs,
+ bool system_thread)
+{
+ struct optee *optee = tee_get_drvdata(ctx->teedev);
+ struct optee_msg_arg *arg, *rpc_arg;
+ struct optee_rpmi_call_req req = {
+ .op = cpu_to_le32(OPTEE_RPMI_YIELDING_CALL_WITH_ARG),
+ .global_id = cpu_to_le64(shm->sec_world_id),
+ };
+ size_t arg_size, rpc_size, rpc_offset;
+
+ arg = tee_shm_get_va(shm, offs);
+ if (IS_ERR(arg))
+ return PTR_ERR(arg);
+
+ arg_size = OPTEE_MSG_GET_ARG_SIZE(arg->num_params);
+ rpc_size = OPTEE_MSG_GET_ARG_SIZE(optee->rpc_param_count);
+ rpc_offset = offs + arg_size;
+ rpc_arg = tee_shm_get_va(shm, rpc_offset);
+ if (IS_ERR(rpc_arg))
+ return PTR_ERR(rpc_arg);
+
+ req.arg_offset = cpu_to_le64((u64)shm->offset + offs);
+ req.rpc_offset = cpu_to_le64((u64)shm->offset + rpc_offset);
+ req.arg_size = cpu_to_le32(arg_size);
+ req.rpc_size = cpu_to_le32(rpc_size);
+
+ return optee_rpmi_yielding_call(ctx, &req, rpc_arg, system_thread);
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 8/9] tee: optee: bind RPMI services and negotiate backend capabilities
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
` (6 preceding siblings ...)
2026-10-13 5:41 ` [PATCH RFC v3 7/9] tee: optee: execute yielding RPMI calls Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:56 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 9/9] tee: optee: support RPMI asynchronous notification doorbells Amirreza Zarrabi
8 siblings, 1 reply; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
Register an RPMI service driver matching the OP-TEE service UUID and
integrate it with OP-TEE module initialization and removal.
Check the service API version, query the trusted OS revision, and
obtain the RPC parameter and logical notification counts. Initialize
shared-memory tracking, the call queue, supplicant state and internal
context before publishing the client and supplicant TEE devices.
Connect the RPMI backend to the common OP-TEE operations and enumerate
trusted application devices. Enable in-kernel RPMB routing when the
RPMB subsystem is reachable.
Add removal and probe failure cleanup for the backend resources.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/core.c | 10 +-
drivers/tee/optee/optee_private.h | 18 ++-
drivers/tee/optee/rpmi_abi.c | 299 ++++++++++++++++++++++++++++++++++++++
3 files changed, 322 insertions(+), 5 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index a52c1f498b99..d9bb4d3a34b3 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -220,6 +220,7 @@ void optee_remove_common(struct optee *optee)
static int smc_abi_rc;
static int ffa_abi_rc;
+static int rpmi_abi_rc;
static bool intf_is_regged;
static int __init optee_core_init(void)
@@ -245,14 +246,15 @@ static int __init optee_core_init(void)
smc_abi_rc = optee_smc_abi_register();
ffa_abi_rc = optee_ffa_abi_register();
+ rpmi_abi_rc = optee_rpmi_abi_register();
- /* If both failed there's no point with this module */
- if (smc_abi_rc && ffa_abi_rc) {
+ /* Keep the module if any transport registered successfully. */
+ if (smc_abi_rc && ffa_abi_rc && rpmi_abi_rc) {
if (IS_REACHABLE(CONFIG_RPMB)) {
rpmb_interface_unregister(&rpmb_class_intf);
intf_is_regged = false;
}
- return smc_abi_rc;
+ return -EOPNOTSUPP;
}
return 0;
@@ -270,6 +272,8 @@ static void __exit optee_core_exit(void)
optee_smc_abi_unregister();
if (!ffa_abi_rc)
optee_ffa_abi_unregister();
+ if (!rpmi_abi_rc)
+ optee_rpmi_abi_unregister();
}
module_exit(optee_core_exit);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index efc1e37fe1bf..fcb291a43b7b 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -224,8 +224,8 @@ struct optee;
* @os_build_id: OP-TEE OS build identifier (0 if unspecified)
*
* Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
- * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
- * FF-A ABI version.
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI) or OPTEE_RPMI_GET_OS_VERSION (RPMI ABI).
+ * This is the trusted OS revision, not a transport ABI version.
*/
struct optee_revision {
u32 os_major;
@@ -503,4 +503,18 @@ static inline void optee_ffa_abi_unregister(void)
}
#endif
+#if IS_REACHABLE(CONFIG_RISCV_RPMI_TEE_TRANSPORT)
+int optee_rpmi_abi_register(void);
+void optee_rpmi_abi_unregister(void);
+#else
+static inline int optee_rpmi_abi_register(void)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void optee_rpmi_abi_unregister(void)
+{
+}
+#endif
+
#endif /*OPTEE_PRIVATE_H*/
diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
index 9a6439e15a33..0ed2de96758e 100644
--- a/drivers/tee/optee/rpmi_abi.c
+++ b/drivers/tee/optee/rpmi_abi.c
@@ -579,3 +579,302 @@ static int optee_rpmi_do_call_with_arg(struct tee_context *ctx,
return optee_rpmi_yielding_call(ctx, &req, rpc_arg, system_thread);
}
+
+/* Query and store the trusted OS revision. */
+static int optee_rpmi_get_os_version(struct optee *optee)
+{
+ struct optee_rpmi_probe_req req = {
+ .op = cpu_to_le32(OPTEE_RPMI_GET_OS_VERSION),
+ };
+ struct optee_rpmi_os_resp os;
+ int rc;
+
+ rc = optee_rpmi_call(optee, &req, sizeof(req), &os, sizeof(os));
+ if (rc)
+ return rc;
+
+ optee->revision.os_major = le32_to_cpu(os.major);
+ optee->revision.os_minor = le32_to_cpu(os.minor);
+ optee->revision.os_build_id = le64_to_cpu(os.build_id);
+
+ if (optee->revision.os_build_id)
+ pr_info("revision %u.%u (%016llx)\n",
+ optee->revision.os_major, optee->revision.os_minor,
+ optee->revision.os_build_id);
+ else
+ pr_info("revision %u.%u\n", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
+/* Query secure-world limits; version 1 defines no optional capability bits. */
+static int optee_rpmi_exchange_caps(struct optee *optee, u32 *rpc_param_count,
+ u32 *max_notif_key)
+{
+ struct optee_rpmi_probe_req req = {
+ .op = cpu_to_le32(OPTEE_RPMI_EXCHANGE_CAPABILITIES),
+ };
+ struct optee_rpmi_caps_resp caps;
+ u32 rpc_count, max_key;
+ int rc;
+
+ rc = optee_rpmi_call(optee, &req, sizeof(req), &caps, sizeof(caps));
+ if (rc)
+ return rc;
+
+ rpc_count = le32_to_cpu(caps.rpc_param_count);
+ if (!rpc_count)
+ return -EPROTO;
+
+ *rpc_param_count = rpc_count;
+
+ max_key = le32_to_cpu(caps.max_notification_key);
+ if (max_key)
+ *max_notif_key = max_key;
+ else
+ *max_notif_key = OPTEE_DEFAULT_MAX_NOTIF_VALUE;
+
+ return 0;
+}
+
+static int optee_rpmi_api_is_compatible(struct optee *optee)
+{
+ struct optee_rpmi_probe_req req = {
+ .op = cpu_to_le32(OPTEE_RPMI_GET_API_VERSION),
+ };
+ struct optee_rpmi_api_resp api;
+ int rc;
+
+ rc = optee_rpmi_call(optee, &req, sizeof(req), &api, sizeof(api));
+ if (rc)
+ return rc;
+
+ if (le32_to_cpu(api.major) != OPTEE_RPMI_VERSION_MAJOR)
+ return -EPROTONOSUPPORT;
+
+ /* Version 1.0 has no minimum minor revision beyond zero. */
+ return 0;
+}
+
+static void optee_rpmi_get_version(struct tee_device *teedev,
+ struct tee_ioctl_version_data *vers)
+{
+ *vers = (struct tee_ioctl_version_data) {
+ .impl_id = TEE_IMPL_ID_OPTEE,
+ .gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM |
+ TEE_GEN_CAP_MEMREF_NULL,
+ };
+}
+
+static int optee_rpmi_open(struct tee_context *ctx)
+{
+ return optee_open(ctx, true);
+}
+
+static const struct tee_driver_ops optee_rpmi_clnt_ops = {
+ .get_version = optee_rpmi_get_version,
+ .get_tee_revision = optee_get_revision,
+ .open = optee_rpmi_open,
+ .release = optee_release,
+ .open_session = optee_open_session,
+ .close_session = optee_close_session,
+ .invoke_func = optee_invoke_func,
+ .cancel_req = optee_cancel_req,
+ .shm_register = optee_rpmi_shm_register,
+ .shm_unregister = optee_rpmi_shm_unregister,
+};
+
+static const struct tee_driver_ops optee_rpmi_supp_ops = {
+ .get_version = optee_rpmi_get_version,
+ .get_tee_revision = optee_get_revision,
+ .open = optee_rpmi_open,
+ .release = optee_release_supp,
+ .supp_recv = optee_supp_recv,
+ .supp_send = optee_supp_send,
+ .shm_register = optee_rpmi_shm_register,
+ .shm_unregister = optee_rpmi_shm_unregister_supp,
+};
+
+static const struct tee_desc optee_rpmi_clnt_desc = {
+ .name = DRIVER_NAME "-rpmi-clnt",
+ .ops = &optee_rpmi_clnt_ops,
+ .owner = THIS_MODULE,
+};
+
+static const struct tee_desc optee_rpmi_supp_desc = {
+ .name = DRIVER_NAME "-rpmi-supp",
+ .ops = &optee_rpmi_supp_ops,
+ .owner = THIS_MODULE,
+ .flags = TEE_DESC_PRIVILEGED,
+};
+
+static const struct optee_ops optee_rpmi_ops = {
+ .do_call_with_arg = optee_rpmi_do_call_with_arg,
+ .to_msg_param = optee_rpmi_to_msg_param,
+ .from_msg_param = optee_rpmi_from_msg_param,
+};
+
+/* Keep callback state and memory tables alive until all TEE users release. */
+static void optee_rpmi_remove(struct rpmi_tee_device *rdev)
+{
+ struct optee *optee = rpmi_tee_dev_get_drvdata(rdev);
+
+ optee_remove_common(optee);
+ optee_shm_handle_map_uninit(&optee->rpmi.shm_map);
+ ida_destroy(&optee->rpmi.nonce_ida);
+ kfree(optee);
+}
+
+static int optee_rpmi_probe(struct rpmi_tee_device *rdev)
+{
+ struct tee_device *teedev;
+ struct tee_shm_pool *pool;
+ struct optee *optee;
+ struct tee_context *ctx;
+ u32 rpc_param_count, max_notif_key;
+ int rc;
+
+ optee = kzalloc_obj(*optee);
+ if (!optee)
+ return -ENOMEM;
+
+ optee->rpmi.rdev = rdev;
+ ida_init(&optee->rpmi.nonce_ida);
+ optee->ops = &optee_rpmi_ops;
+
+ rc = optee_rpmi_api_is_compatible(optee);
+ if (rc)
+ goto err_optee;
+
+ rc = optee_rpmi_get_os_version(optee);
+ if (rc)
+ goto err_optee;
+
+ rc = optee_rpmi_exchange_caps(optee, &rpc_param_count, &max_notif_key);
+ if (rc)
+ goto err_optee;
+
+ optee->rpc_param_count = rpc_param_count;
+ optee->in_kernel_rpmb_routing = IS_REACHABLE(CONFIG_RPMB);
+
+ pool = optee_rpmi_shm_pool_alloc();
+ if (IS_ERR(pool)) {
+ rc = PTR_ERR(pool);
+ goto err_optee;
+ }
+ optee->pool = pool;
+
+ rc = optee_shm_handle_map_init(&optee->rpmi.shm_map);
+ if (rc)
+ goto err_pool;
+
+ optee_cq_init(&optee->call_queue, 0);
+ optee_supp_init(&optee->supp);
+ optee_shm_arg_cache_init(optee, OPTEE_SHM_ARG_SHARED);
+ mutex_init(&optee->rpmb_dev_mutex);
+ INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
+ optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
+ rc = optee_notif_init(optee, max_notif_key);
+ if (rc)
+ goto err_common;
+
+ teedev = tee_device_alloc(&optee_rpmi_clnt_desc, &rdev->dev,
+ optee->pool, optee);
+ if (IS_ERR(teedev)) {
+ rc = PTR_ERR(teedev);
+ goto err_notif;
+ }
+ optee->teedev = teedev;
+
+ teedev = tee_device_alloc(&optee_rpmi_supp_desc, &rdev->dev,
+ optee->pool, optee);
+ if (IS_ERR(teedev)) {
+ rc = PTR_ERR(teedev);
+ goto err_devices;
+ }
+ optee->supp_teedev = teedev;
+
+ optee_set_dev_group(optee);
+
+ /* Internal RPC allocation must be ready before userspace can enter. */
+ ctx = teedev_open(optee->teedev);
+ if (IS_ERR(ctx)) {
+ rc = PTR_ERR(ctx);
+ goto err_devices;
+ }
+ optee->ctx = ctx;
+
+ rpmi_tee_dev_set_drvdata(rdev, optee);
+
+ if (optee->in_kernel_rpmb_routing)
+ blocking_notifier_chain_register(&optee_rpmb_intf_added,
+ &optee->rpmb_intf);
+
+ rc = tee_device_register(optee->teedev);
+ if (rc)
+ goto err_initialized;
+
+ rc = tee_device_register(optee->supp_teedev);
+ if (rc)
+ goto err_initialized;
+
+ rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
+ if (rc)
+ goto err_initialized;
+
+ dev_info(&rdev->dev, "OP-TEE RPMI %u.%u initialized\n",
+ optee->revision.os_major, optee->revision.os_minor);
+
+ return 0;
+
+err_initialized:
+ /* The remove path owns and frees the published backend state. */
+ optee_rpmi_remove(rdev);
+
+ return rc;
+
+err_devices:
+ tee_device_unregister(optee->supp_teedev);
+ tee_device_unregister(optee->teedev);
+ optee_shm_arg_cache_uninit(optee);
+err_notif:
+ optee_notif_uninit(optee);
+err_common:
+ optee_supp_uninit(&optee->supp);
+ mutex_destroy(&optee->call_queue.mutex);
+ rpmb_dev_put(optee->rpmb_dev);
+ mutex_destroy(&optee->rpmb_dev_mutex);
+ optee_shm_handle_map_uninit(&optee->rpmi.shm_map);
+err_pool:
+ tee_shm_pool_free(optee->pool);
+err_optee:
+ ida_destroy(&optee->rpmi.nonce_ida);
+ kfree(optee);
+
+ return rc;
+}
+
+static const struct rpmi_tee_device_id optee_rpmi_device_ids[] = {
+ { OPTEE_RPMI_SERVICE_UUID },
+ {}
+};
+
+static struct rpmi_tee_driver optee_rpmi_driver = {
+ .name = DRIVER_NAME "-rpmi",
+ .probe = optee_rpmi_probe,
+ .remove = optee_rpmi_remove,
+ .id_table = optee_rpmi_device_ids,
+};
+
+int optee_rpmi_abi_register(void)
+{
+ return rpmi_tee_register(&optee_rpmi_driver);
+}
+
+void optee_rpmi_abi_unregister(void)
+{
+ rpmi_tee_unregister(&optee_rpmi_driver);
+}
+
+MODULE_ALIAS("rpmi_tee:486178e0-e7f8-11e3-bc5e-0002a5d5c51b");
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH RFC v3 9/9] tee: optee: support RPMI asynchronous notification doorbells
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
` (7 preceding siblings ...)
2026-10-13 5:41 ` [PATCH RFC v3 8/9] tee: optee: bind RPMI services and negotiate backend capabilities Amirreza Zarrabi
@ 2026-10-13 5:41 ` Amirreza Zarrabi
2026-10-13 5:48 ` sashiko-bot
8 siblings, 1 reply; 17+ messages in thread
From: Amirreza Zarrabi @ 2026-10-13 5:41 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Rahul Pathak, Anup Patel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marouene Boubakri
Cc: linux-arm-msm, linux-kernel, op-tee, linux-riscv, devicetree,
Amirreza Zarrabi
OP-TEE uses asynchronous notifications to request bottom-half
processing in normal world without waiting for a client call.
Allocate a TEE-to-REE RPMI signal and pass its ID to OP-TEE when
enabling asynchronous notifications. Queue bottom-half processing on
a private workqueue.
Initialize notification delivery before publishing the TEE devices.
Allow probe to continue if setup fails, keeping synchronous RPC
notifications available.
On cleanup, request that OP-TEE stop asynchronous notifications,
relinquish the signal and drain the bottom-half workqueue. Transport
relinquishment does not synchronize an already-selected callback.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
---
drivers/tee/optee/optee_private.h | 6 +++
drivers/tee/optee/rpmi_abi.c | 92 +++++++++++++++++++++++++++++++++++++++
2 files changed, 98 insertions(+)
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index fcb291a43b7b..3bc65ffb6f99 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -207,11 +207,17 @@ struct optee_ffa {
* @rdev: owning RPMI service device
* @shm_map: lookup by the host-endian parcel ID and nonce pair
* @nonce_ida: nonzero SHM nonces, reusable after successful parcel reclamation
+ * @signal: allocated TEE-to-REE doorbell, independent of logical keys
+ * @notif_wq: private bottom-half workqueue, NULL when inactive
+ * @notif_work: processes bottom halves requested by the RPMI signal
*/
struct optee_rpmi {
struct rpmi_tee_device *rdev;
struct optee_shm_handle_map shm_map;
struct ida nonce_ida;
+ u32 signal;
+ struct workqueue_struct *notif_wq;
+ struct work_struct notif_work;
};
#endif
diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
index 0ed2de96758e..6434767a2249 100644
--- a/drivers/tee/optee/rpmi_abi.c
+++ b/drivers/tee/optee/rpmi_abi.c
@@ -580,6 +580,92 @@ static int optee_rpmi_do_call_with_arg(struct tee_context *ctx,
return optee_rpmi_yielding_call(ctx, &req, rpc_arg, system_thread);
}
+/* Yielding bottom halves run here, not in the transport retrieval worker. */
+static void optee_rpmi_notif_work(struct work_struct *work)
+{
+ struct optee_rpmi *rpmi = container_of(work, struct optee_rpmi,
+ notif_work);
+ struct optee *optee = container_of(rpmi, struct optee, rpmi);
+
+ optee_do_bottom_half(optee->ctx);
+}
+
+static void optee_rpmi_notif_callback(struct rpmi_tee_device *rdev, u32 signal,
+ void *cb_data)
+{
+ struct optee *optee = cb_data;
+
+ queue_work(optee->rpmi.notif_wq, &optee->rpmi.notif_work);
+}
+
+/* Relinquish is not a barrier for an already-selected transport callback. */
+static void optee_rpmi_async_notif_uninit(struct optee *optee)
+{
+ struct optee_rpmi *rpmi = &optee->rpmi;
+ struct rpmi_tee_device *rdev = rpmi->rdev;
+ int rc;
+
+ if (!rpmi->notif_wq)
+ return;
+
+ rc = optee_stop_async_notif(optee->ctx);
+ if (rc)
+ dev_warn(&rdev->dev, "stop notifications failed: %d\n", rc);
+
+ rc = rdev->ops->notifier_ops->notify_relinquish(rdev, rpmi->signal);
+ if (rc && rc != -EOPNOTSUPP)
+ dev_warn(&rdev->dev,
+ "relinquish notification failed: %d\n", rc);
+
+ destroy_workqueue(rpmi->notif_wq);
+
+ rpmi->notif_wq = NULL;
+}
+
+/* Enable OP-TEE's bottom-half doorbell using the reserved RPMI signal. */
+static int optee_rpmi_enable_async_notif(struct optee *optee)
+{
+ struct optee_rpmi_enable_notif_req req = {
+ .op = cpu_to_le32(OPTEE_RPMI_ENABLE_ASYNC_NOTIF),
+ .signal_id = cpu_to_le32(optee->rpmi.signal),
+ };
+ struct optee_rpmi_status_resp resp;
+
+ return optee_rpmi_call(optee, &req, sizeof(req), &resp, sizeof(resp));
+}
+
+static int optee_rpmi_async_notif_init(struct optee *optee)
+{
+ struct optee_rpmi *rpmi = &optee->rpmi;
+ struct rpmi_tee_device *rdev = rpmi->rdev;
+ struct workqueue_struct *wq;
+ int rc;
+
+ INIT_WORK(&rpmi->notif_work, optee_rpmi_notif_work);
+ wq = alloc_workqueue("optee_rpmi_notif", WQ_UNBOUND, 1);
+ if (!wq)
+ return -ENOMEM;
+
+ rpmi->notif_wq = wq;
+
+ rc = rdev->ops->notifier_ops->notify_request(rdev,
+ optee_rpmi_notif_callback,
+ optee, &rpmi->signal);
+ if (rc) {
+ destroy_workqueue(rpmi->notif_wq);
+ /* Checked in optee_rpmi_async_notif_uninit(). */
+ rpmi->notif_wq = NULL;
+
+ return rc;
+ }
+
+ rc = optee_rpmi_enable_async_notif(optee);
+ if (rc)
+ optee_rpmi_async_notif_uninit(optee);
+
+ return rc;
+}
+
/* Query and store the trusted OS revision. */
static int optee_rpmi_get_os_version(struct optee *optee)
{
@@ -720,6 +806,7 @@ static void optee_rpmi_remove(struct rpmi_tee_device *rdev)
{
struct optee *optee = rpmi_tee_dev_get_drvdata(rdev);
+ optee_rpmi_async_notif_uninit(optee);
optee_remove_common(optee);
optee_shm_handle_map_uninit(&optee->rpmi.shm_map);
ida_destroy(&optee->rpmi.nonce_ida);
@@ -807,6 +894,11 @@ static int optee_rpmi_probe(struct rpmi_tee_device *rdev)
rpmi_tee_dev_set_drvdata(rdev, optee);
+ rc = optee_rpmi_async_notif_init(optee);
+ if (rc)
+ dev_warn(&rdev->dev, "async notifications unavailable: %d\n",
+ rc);
+
if (optee->in_kernel_rpmb_routing)
blocking_notifier_chain_register(&optee_rpmb_intf_added,
&optee->rpmb_intf);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH RFC v3 9/9] tee: optee: support RPMI asynchronous notification doorbells
2026-10-13 5:41 ` [PATCH RFC v3 9/9] tee: optee: support RPMI asynchronous notification doorbells Amirreza Zarrabi
@ 2026-10-13 5:48 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-10-13 5:48 UTC (permalink / raw)
To: Amirreza Zarrabi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] tee: optee: race between async notif callback and teardown causes UAF/NULL deref
--
commit fbdb2da759a2fc5e6692fd4d211f0b6f63644339
Author: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
tee: optee: support RPMI asynchronous notification doorbells
This commit introduces support for RPMI asynchronous notification doorbells
in OP-TEE. It allocates a TEE-to-REE RPMI signal, passes it to OP-TEE, and
queues bottom-half processing on a private workqueue. It also adds teardown
logic to stop asynchronous notifications and drain the workqueue upon
cleanup.
> diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
> index 0ed2de96758e2..6434767a22497 100644
> --- a/drivers/tee/optee/rpmi_abi.c
> +++ b/drivers/tee/optee/rpmi_abi.c
[ ... ]
> +static void optee_rpmi_notif_callback(struct rpmi_tee_device *rdev, u32 signal,
> + void *cb_data)
> +{
> + struct optee *optee = cb_data;
> +
> + queue_work(optee->rpmi.notif_wq, &optee->rpmi.notif_work);
> +}
> +
> +/* Relinquish is not a barrier for an already-selected transport callback. */
> +static void optee_rpmi_async_notif_uninit(struct optee *optee)
> +{
> + struct optee_rpmi *rpmi = &optee->rpmi;
> + struct rpmi_tee_device *rdev = rpmi->rdev;
> + int rc;
> +
> + if (!rpmi->notif_wq)
> + return;
> +
> + rc = optee_stop_async_notif(optee->ctx);
> + if (rc)
> + dev_warn(&rdev->dev, "stop notifications failed: %d\n", rc);
> +
> + rc = rdev->ops->notifier_ops->notify_relinquish(rdev, rpmi->signal);
> + if (rc && rc != -EOPNOTSUPP)
> + dev_warn(&rdev->dev,
> + "relinquish notification failed: %d\n", rc);
> +
> + destroy_workqueue(rpmi->notif_wq);
> +
> + rpmi->notif_wq = NULL;
> +}
[Severity: High]
Can an in-flight RPMI notification invoke optee_rpmi_notif_callback()
concurrently with or after this teardown process?
As the comment mentions, notify_relinquish() is not a barrier for an
already-selected transport callback. If the callback runs concurrently,
queue_work() accesses the workqueue without locking. This can cause a
use-after-free if destroy_workqueue() is executing, or a NULL pointer
dereference if rpmi->notif_wq has already been set to NULL.
Furthermore, if the callback execution is delayed until after
optee_rpmi_remove() completes, the optee structure itself is freed
via kfree(optee), which would lead to a use-after-free when the callback
dereferences optee->rpmi.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012-rpmi-tee-service-grp-dev-v3-0-975b574667e2@oss.qualcomm.com?part=9
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH RFC v3 2/9] tee: optee: allow RPMI transport builds on RISC-V
2026-10-13 5:41 ` [PATCH RFC v3 2/9] tee: optee: allow RPMI transport builds on RISC-V Amirreza Zarrabi
@ 2026-10-13 5:55 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-10-13 5:55 UTC (permalink / raw)
To: Amirreza Zarrabi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] kconfig: missing RISCV_RPMI_TEE_TRANSPORT definition
--
commit 1268be5496f8b09029f843bfb4ecaffba27bd25e
Author: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
tee: optee: allow RPMI transport builds on RISC-V
This commit allows the TEE subsystem and OP-TEE driver to be built on RISC-V systems using the RPMI TEE transport. It also implements the memory type check for RISC-V to prevent unsupported architecture build errors.
> diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig
> index 98c3ad0839409..ad754b647a3c5 100644
> --- a/drivers/tee/Kconfig
> +++ b/drivers/tee/Kconfig
> @@ -2,7 +2,8 @@
> # Generic Trusted Execution Environment Configuration
> menuconfig TEE
> tristate "Trusted Execution Environment support"
> - depends on HAVE_ARM_SMCCC || COMPILE_TEST || CPU_SUP_AMD
> + depends on HAVE_ARM_SMCCC || COMPILE_TEST || CPU_SUP_AMD || \
> + RISCV_RPMI_TEE_TRANSPORT
[Severity: High]
Is the RISCV_RPMI_TEE_TRANSPORT config defined in another patch in this
series?
Since RISCV_RPMI_TEE_TRANSPORT is not defined in this patch or the current
tree, this dependency evaluates to false. This makes it impossible to
enable TEE on RISC-V configurations, leaving the drivers disabled when
users expect them to be built.
[ ... ]
> diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
> index 891dac63cab85..cc057c76de622 100644
> --- a/drivers/tee/optee/Kconfig
> +++ b/drivers/tee/optee/Kconfig
> @@ -2,10 +2,11 @@
> # OP-TEE Trusted Execution Environment Configuration
> config OPTEE
> tristate "OP-TEE"
> - depends on HAVE_ARM_SMCCC
> + depends on HAVE_ARM_SMCCC || RISCV_RPMI_TEE_TRANSPORT
[Severity: High]
Similar to the TEE config above, OPTEE depends on RISCV_RPMI_TEE_TRANSPORT
here.
Without a corresponding config RISCV_RPMI_TEE_TRANSPORT definition in the
tree, this condition can never be met for RISC-V systems. Does this rely
on a missing prerequisite patch?
> depends on MMU
> depends on RPMB || !RPMB
> depends on ARM_FFA_TRANSPORT || !ARM_FFA_TRANSPORT
> + depends on RISCV_RPMI_TEE_TRANSPORT || !RISCV_RPMI_TEE_TRANSPORT
> help
> This implements the OP-TEE Trusted Execution Environment (TEE)
> driver.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012-rpmi-tee-service-grp-dev-v3-0-975b574667e2@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH RFC v3 8/9] tee: optee: bind RPMI services and negotiate backend capabilities
2026-10-13 5:41 ` [PATCH RFC v3 8/9] tee: optee: bind RPMI services and negotiate backend capabilities Amirreza Zarrabi
@ 2026-10-13 5:56 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-10-13 5:56 UTC (permalink / raw)
To: Amirreza Zarrabi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] tee: optee: buffer overflow in optee_get_msg_arg() due to unbounded rpc_param_count
- [Medium] optee: missing optee_shm_arg_cache_uninit() on probe error paths
--
commit 8a4dd6e2611ab6b4ab7ba8d46b447719a90842db
Author: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
tee: optee: bind RPMI services and negotiate backend capabilities
Register an RPMI service driver matching the OP-TEE service UUID and
integrate it with OP-TEE module initialization and removal. Check the service
API version, query the trusted OS revision, and obtain the RPC parameter and
logical notification counts.
> diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
> index 9a6439e15a33f..0ed2de96758e2 100644
> --- a/drivers/tee/optee/rpmi_abi.c
> +++ b/drivers/tee/optee/rpmi_abi.c
[ ... ]
> +static int optee_rpmi_exchange_caps(struct optee *optee, u32 *rpc_param_count,
> + u32 *max_notif_key)
> +{
[ ... ]
> + rc = optee_rpmi_call(optee, &req, sizeof(req), &caps, sizeof(caps));
> + if (rc)
> + return rc;
> +
> + rpc_count = le32_to_cpu(caps.rpc_param_count);
> + if (!rpc_count)
> + return -EPROTO;
> +
> + *rpc_param_count = rpc_count;
[Severity: High]
Does this code overflow the shared memory buffer in optee_get_msg_arg()?
Here rpc_count is read directly from the TEE response and assigned without
any upper bounds check.
Later, in optee_get_msg_arg(), sz = optee_msg_arg_size(optee->rpc_param_count)
is calculated. If sz exceeds SHM_ENTRY_SIZE (which is typically 4096),
the code falls back to allocating a new shared memory buffer using
tee_shm_alloc_kernel_buf(ctx, SHM_ENTRY_SIZE), which strictly allocates 4096
bytes regardless of the calculated sz.
Finally, memset(ma, 0, sz) is called, which writes sz bytes into the newly
allocated 4096-byte buffer.
Since rpc_param_count is unbounded, a large value from a compromised or
buggy secure world could lead to an out-of-bounds write.
[ ... ]
> +static int optee_rpmi_probe(struct rpmi_tee_device *rdev)
> +{
[ ... ]
> + optee_cq_init(&optee->call_queue, 0);
> + optee_supp_init(&optee->supp);
> + optee_shm_arg_cache_init(optee, OPTEE_SHM_ARG_SHARED);
> + mutex_init(&optee->rpmb_dev_mutex);
> + INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
> + optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
> + rc = optee_notif_init(optee, max_notif_key);
> + if (rc)
> + goto err_common;
> +
> + teedev = tee_device_alloc(&optee_rpmi_clnt_desc, &rdev->dev,
> + optee->pool, optee);
> + if (IS_ERR(teedev)) {
> + rc = PTR_ERR(teedev);
> + goto err_notif;
> + }
[ ... ]
> +err_devices:
> + tee_device_unregister(optee->supp_teedev);
> + tee_device_unregister(optee->teedev);
> + optee_shm_arg_cache_uninit(optee);
> +err_notif:
> + optee_notif_uninit(optee);
> +err_common:
> + optee_supp_uninit(&optee->supp);
[Severity: Medium]
Does this error path leak the argument cache resources?
optee_shm_arg_cache_init() is called unconditionally earlier in this function,
which allocates resources and initializes a mutex (optee->shm_arg_cache.mutex).
If optee_notif_init() fails, execution jumps to the err_common label. If
tee_device_alloc() fails, execution jumps to the err_notif label.
Both of these labels bypass the required cleanup call
optee_shm_arg_cache_uninit() which is located under the err_devices label
above them, leading to a leaked mutex and other argument cache resources.
> + mutex_destroy(&optee->call_queue.mutex);
> + rpmb_dev_put(optee->rpmb_dev);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012-rpmi-tee-service-grp-dev-v3-0-975b574667e2@oss.qualcomm.com?part=8
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH RFC v3 1/9] tee: optee: share the global shared-memory ID mapping
2026-10-13 5:41 ` [PATCH RFC v3 1/9] tee: optee: share the global shared-memory ID mapping Amirreza Zarrabi
@ 2026-10-13 5:56 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-10-13 5:56 UTC (permalink / raw)
To: Amirreza Zarrabi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] tee: optee: UAF race in SHM free via unreferenced lookup
--
commit 39c6675fd3cd9df49f5b14024b5f0c43a9129fe8
Author: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
tee: optee: share the global shared-memory ID mapping
This commit moves the FF-A backend's hash table mapping secure-world memory
handles to TEE shared-memory objects into common helpers. It also converts
the FF-A backend to use this new shared mapping API.
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -488,7 +404,8 @@ static void handle_ffa_rpc_func_cmd_shm_free(struct tee_context *ctx,
> arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
> goto err_bad_param;
>
> - shm = optee_shm_from_ffa_handle(optee, arg->params[0].u.value.b);
> + shm = optee_shm_handle_map_lookup(&optee->ffa.shm_map,
> + arg->params[0].u.value.b);
> if (!shm)
> goto err_bad_param;
[Severity: High]
Could this lookup introduce a Use-After-Free race if multiple RPCs are
processed concurrently?
If a compromised Secure World sends concurrent OPTEE_RPC_CMD_SHM_FREE RPCs
for the same shared memory global_id, both threads can look up the pointer
before either has a chance to free it.
Since optee_shm_handle_map_lookup() returns the tee_shm pointer without
incrementing its reference count, both threads will then proceed down to
tee_shm_free() or optee_rpc_cmd_free_suppl(). The first thread will drop
the reference to 0, freeing the object, and the second thread will
subsequently access shm->refcount on the freed pointer.
> diff --git a/drivers/tee/optee/shm_handle_map.c b/drivers/tee/optee/shm_handle_map.c
> --- /dev/null
> +++ b/drivers/tee/optee/shm_handle_map.c
[ ... ]
> +struct tee_shm *
> +optee_shm_handle_map_lookup(struct optee_shm_handle_map *map, u64 global_id)
> +{
> + struct optee_shm_handle_map_entry *entry;
> + struct tee_shm *shm = NULL;
> +
> + mutex_lock(&map->mutex);
> + entry = rhashtable_lookup_fast(&map->rht, &global_id,
> + shm_handle_map_params);
> + if (entry)
> + shm = entry->shm;
> + mutex_unlock(&map->mutex);
> +
> + return shm;
> +}
[Severity: High]
Since this new API helper retrieves the shared memory pointer without acquiring
a reference, does it allow the object to be freed concurrently by another
thread once the mutex is released?
The kerneldoc above this function mentions "The caller must keep the SHM
object alive; no reference is acquired here", but as seen in
handle_ffa_rpc_func_cmd_shm_free(), the caller isn't guaranteed to hold a
reference when handling incoming RPCs from the secure world.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012-rpmi-tee-service-grp-dev-v3-0-975b574667e2@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH RFC v3 7/9] tee: optee: execute yielding RPMI calls
2026-10-13 5:41 ` [PATCH RFC v3 7/9] tee: optee: execute yielding RPMI calls Amirreza Zarrabi
@ 2026-10-13 5:56 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-10-13 5:56 UTC (permalink / raw)
To: Amirreza Zarrabi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] optee: missing bounds check on rpc_arg->num_params allows OOB access
--
commit 5f5afbcfcd90452c0729b00f504945e1e4f52c26
Author: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
tee: optee: execute yielding RPMI calls
This patch adds the RPMI yielding-call path used by the common OP-TEE
session operations. It submits the command and RPC argument buffers as
ranges within a shared memory parcel and handles RPC requests while the
call is suspended.
> diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
> index 8b90c3653e80d..9a6439e15a33f 100644
> --- a/drivers/tee/optee/rpmi_abi.c
> +++ b/drivers/tee/optee/rpmi_abi.c
[ ... ]
> +static int optee_rpmi_yielding_call(struct tee_context *ctx,
> + const struct optee_rpmi_call_req *req,
> + struct optee_msg_arg *rpc_arg,
> + bool system_thread)
> +{
[ ... ]
> + result = le32_to_cpu(resp.result);
> + if (result == OPTEE_RPMI_YIELDING_CALL_RETURN_DONE)
> + goto done;
> +
> + cond_resched();
> + optee_rpmi_handle_rpc(ctx, optee, result, rpc_arg);
[Severity: High]
Does this code allow an out-of-bounds access if the firmware returns a
maliciously large num_params?
In optee_rpmi_do_call_with_arg() below, the RPC buffer is bounded in shared
memory by optee->rpc_param_count.
However, when the firmware populates rpc_arg and optee_rpmi_handle_rpc()
is called here, the num_params field isn't validated. The call chain
optee_rpmi_handle_rpc()->optee_rpmi_handle_rpc_cmd()->optee_rpc_cmd() reaches
handle_rpc_supp_cmd() in drivers/tee/optee/rpc.c, which uses the unvalidated
num_params:
params = kmalloc_objs(struct tee_param, arg->num_params);
if (!params) {
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
return;
}
if (optee->ops->from_msg_param(optee, params, arg->num_params,
arg->params)) {
This processes the RPC arguments using the firmware-supplied arg->num_params,
which can lead to out-of-bounds reads and writes on the arg->params shared
memory array if arg->num_params > optee->rpc_param_count.
[ ... ]
> +static int optee_rpmi_do_call_with_arg(struct tee_context *ctx,
> + struct tee_shm *shm, u_int offs,
> + bool system_thread)
> +{
> + struct optee *optee = tee_get_drvdata(ctx->teedev);
> + struct optee_msg_arg *arg, *rpc_arg;
[ ... ]
> + arg_size = OPTEE_MSG_GET_ARG_SIZE(arg->num_params);
> + rpc_size = OPTEE_MSG_GET_ARG_SIZE(optee->rpc_param_count);
> + rpc_offset = offs + arg_size;
> + rpc_arg = tee_shm_get_va(shm, rpc_offset);
> + if (IS_ERR(rpc_arg))
> + return PTR_ERR(rpc_arg);
> +
> + req.arg_offset = cpu_to_le64((u64)shm->offset + offs);
> + req.rpc_offset = cpu_to_le64((u64)shm->offset + rpc_offset);
> + req.arg_size = cpu_to_le32(arg_size);
> + req.rpc_size = cpu_to_le32(rpc_size);
> +
> + return optee_rpmi_yielding_call(ctx, &req, rpc_arg, system_thread);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012-rpmi-tee-service-grp-dev-v3-0-975b574667e2@oss.qualcomm.com?part=7
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH RFC v3 4/9] tee: optee: add RPMI shared-memory and parameter support
2026-10-13 5:41 ` [PATCH RFC v3 4/9] tee: optee: add RPMI shared-memory and parameter support Amirreza Zarrabi
@ 2026-10-13 5:57 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-10-13 5:57 UTC (permalink / raw)
To: Amirreza Zarrabi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] tee: optee: OOB access in RPC handlers due to unvalidated memref size
- [High] tee: optee: NULL pointer dereference in RPC handlers from empty memref
- [Medium] tee: optee: IDA nonce leak on memory_share failure allows DoS
--
commit 75de307b7e8cbec8782ab18004851de96c3a4c73
Author: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
tee: optee: add RPMI shared-memory and parameter support
OP-TEE commands and RPCs need shared memory that secure world can
identify through RPMI parcels.
Register normal-world pages as read-write parcels shared with the
OP-TEE endpoint. Track each parcel ID and nonce with the common
shared-memory map. Store the combined identity in tee_shm.sec_world_id
and allocate nonzero nonces to distinguish registered memory from NULL
references. Reuse nonces after successful parcel reclamation.
Add conversions between TEE parameters and parcel memory references.
On client memory unregistration, ask OP-TEE to release its mapping
before reclaiming the parcel. Supplicant memory has already been
released by OP-TEE through its SHM_FREE RPC and only needs reclaiming.
> diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
> new file mode 100644
> index 0000000000000..940676794280d
> --- /dev/null
> +++ b/drivers/tee/optee/rpmi_abi.c
[ ... ]
> +static int optee_rpmi_shm_register(struct tee_context *ctx, struct tee_shm *shm,
> + struct page **pages, size_t num_pages,
> + unsigned long start)
> +{
[ ... ]
> + rc = ida_alloc_min(&optee->rpmi.nonce_ida, 1, GFP_KERNEL);
> + if (rc < 0) {
> + sg_free_table(&sgt);
> + return rc;
> + }
> +
> + args.nonce = rc;
> + args.sg = sgt.sgl;
> + rc = rdev->ops->mem_ops->memory_share(rdev, &args);
> + sg_free_table(&sgt);
> + /* A failed exchange may leave a parcel using this nonce. */
> + if (rc)
> + return rc;
[Severity: Medium]
Does this error path leak the allocated IDA nonce?
If memory_share() fails (for instance, when firmware rejects the request),
it appears the nonce allocated via ida_alloc_min() is returned without being
freed. Since TEE_IOC_SHM_REGISTER is accessible to unprivileged users, could
this be repeatedly triggered to exhaust the IDA space and cause a denial of
service for subsequent shared memory registrations?
> +
> + global_id = optee_rpmi_global_id(args.parcel_id, args.nonce);
[ ... ]
> +/* Convert an RPMI parcel reference to a memref; callers own SHM lifetime. */
> +static int from_msg_param_rpmi_mem(struct optee *optee, struct tee_param *p,
> + u32 attr, const struct optee_msg_param *mp)
> +{
> + struct tee_shm *shm;
> + u64 offset;
> +
> + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + attr -
> + OPTEE_MSG_ATTR_TYPE_PMEM_INPUT;
> +
> + if (mp->u.pmem.size > SIZE_MAX)
> + return -EOVERFLOW;
> + p->u.memref.size = mp->u.pmem.size;
> +
> + if (!optee_rpmi_nonce(mp->u.pmem.global_id)) {
> + /* Return NULL shm. */
> + if (mp->u.pmem.offs)
> + return -EINVAL;
> + p->u.memref.shm = NULL;
> + p->u.memref.shm_offs = 0;
> + return 0;
[Severity: High]
Can a malformed RPC request with an empty PMEM argument cause a NULL pointer
dereference in the common RPC handlers?
If OP-TEE sends a request with a NULL reference (global_id == 0), this
function successfully sets shm to NULL and returns 0. When the common RPC
handlers in drivers/tee/optee/rpc.c (such as handle_rpc_func_rpmb_probe_next()
or handle_rpc_func_cmd_i2c_transfer()) process the parameters, they appear to
dereference the shm pointer or pass it to tee_shm_get_va() without checking if
it is valid.
> + }
> +
> + shm = optee_shm_handle_map_lookup(&optee->rpmi.shm_map,
> + mp->u.pmem.global_id);
> + if (!shm || mp->u.pmem.offs < shm->offset)
> + return -EINVAL;
> +
> + offset = mp->u.pmem.offs - shm->offset;
> + if (offset > SIZE_MAX)
> + return -EOVERFLOW;
> +
> + p->u.memref.shm = shm;
> + p->u.memref.shm_offs = offset;
[Severity: High]
Are we missing bounds checking for the memory reference size and offset?
The size and offset from the OP-TEE message are assigned to the tee_param
without bounds validation. While this might be necessary to return required
buffer sizes in TEEC_ERROR_SHORT_BUFFER, downstream RPC handlers in
drivers/tee/optee/rpc.c (like handle_rpc_func_rpmb_probe_next() and
handle_rpc_func_rpmb_frames()) appear to trust these parameters blindly.
Could a compromised OP-TEE use this to trigger out-of-bounds reads and writes
in the kernel by causing the handlers to calculate out-of-bounds pointers via
tee_shm_get_va() and perform operations like memcpy()?
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012-rpmi-tee-service-grp-dev-v3-0-975b574667e2@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH RFC v3 6/9] tee: optee: add RPMI RPC handling
2026-10-13 5:41 ` [PATCH RFC v3 6/9] tee: optee: add RPMI RPC handling Amirreza Zarrabi
@ 2026-10-13 5:59 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-10-13 5:59 UTC (permalink / raw)
To: Amirreza Zarrabi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] tee/optee: Use-After-Free race in SHM_FREE RPC due to missing refcount
- [Medium] tee/optee: Memory leak via type confusion in SHM_FREE RPC
--
commit 3440aa8b7411752d7f5d5849d15211a269875fce
Author: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
tee: optee: add RPMI RPC handling
OP-TEE can suspend a yielding call to request services from normal world
through RPC. This patch adds handlers for shared-memory allocation and
free requests, returning RPMI parcel references. It routes other
commands through the common OP-TEE RPC dispatcher.
> diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
> index bbb219ddab381..8b90c3653e80d 100644
> --- a/drivers/tee/optee/rpmi_abi.c
> +++ b/drivers/tee/optee/rpmi_abi.c
[ ... ]
> +static void optee_rpmi_handle_rpc_shm_free(struct tee_context *ctx,
> + struct optee *optee,
> + struct optee_msg_arg *arg)
> +{
> + struct tee_shm *shm;
> + u64 type, global_id;
> +
> + if (arg->num_params != 1 ||
> + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
> + goto err_bad_param;
> +
> + type = arg->params[0].u.value.a;
> + global_id = arg->params[0].u.value.b;
> + /* SHM_FREE requires a non-NULL identity. */
> + if (!optee_rpmi_nonce(global_id) || arg->params[0].u.value.c)
> + goto err_bad_param;
> +
> + shm = optee_shm_handle_map_lookup(&optee->rpmi.shm_map, global_id);
[Severity: High]
Does this code allow a use-after-free if concurrent free requests are made?
If a compromised or buggy TEE issues concurrent OPTEE_RPC_CMD_SHM_FREE RPC
requests for the same global_id, multiple kernel threads could obtain the same
unrefcounted shm pointer from optee_shm_handle_map_lookup() before it is
removed from the map.
Both threads would then concurrently call tee_shm_free() (or
optee_rpc_cmd_free_suppl()), causing a use-after-free when the second thread
attempts to drop the reference count.
> + if (!shm)
> + goto err_bad_param;
> +
> + switch (type) {
[Severity: Medium]
Could this code cause a memory leak by trusting the type from the message?
The type is read directly from the untrusted secure world message without
verifying that the shm object actually corresponds to that allocation type.
If the secure world requests to free a supplicant-allocated buffer but spoofs
the type as OPTEE_RPC_SHM_TYPE_KERNEL, the kernel will execute
tee_shm_free(shm).
This bypasses optee_rpc_cmd_free_suppl(), preventing the free message from
reaching the user-space supplicant, which will then permanently leak the
memory.
> + case OPTEE_RPC_SHM_TYPE_APPL:
> + optee_rpc_cmd_free_suppl(ctx, shm);
> + break;
> + case OPTEE_RPC_SHM_TYPE_KERNEL:
> + tee_shm_free(shm);
> + break;
> + default:
> + goto err_bad_param;
> + }
> +
> + arg->ret = TEEC_SUCCESS;
> + return;
> +
> +err_bad_param:
> + arg->ret = TEEC_ERROR_BAD_PARAMETERS;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012-rpmi-tee-service-grp-dev-v3-0-975b574667e2@oss.qualcomm.com?part=6
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-10-13 5:59 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 5:41 [PATCH RFC v3 0/9] tee: optee: add RPMI backend support on RISC-V Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 1/9] tee: optee: share the global shared-memory ID mapping Amirreza Zarrabi
2026-10-13 5:56 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 2/9] tee: optee: allow RPMI transport builds on RISC-V Amirreza Zarrabi
2026-10-13 5:55 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 3/9] tee: optee: define the RPMI control and parcel-reference ABI Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 4/9] tee: optee: add RPMI shared-memory and parameter support Amirreza Zarrabi
2026-10-13 5:57 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 5/9] tee: optee: add RPMI dynamic shared-memory pool Amirreza Zarrabi
2026-10-13 5:41 ` [PATCH RFC v3 6/9] tee: optee: add RPMI RPC handling Amirreza Zarrabi
2026-10-13 5:59 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 7/9] tee: optee: execute yielding RPMI calls Amirreza Zarrabi
2026-10-13 5:56 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 8/9] tee: optee: bind RPMI services and negotiate backend capabilities Amirreza Zarrabi
2026-10-13 5:56 ` sashiko-bot
2026-10-13 5:41 ` [PATCH RFC v3 9/9] tee: optee: support RPMI asynchronous notification doorbells Amirreza Zarrabi
2026-10-13 5:48 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox