[PATCH RFC v3 7/9] tee: optee: execute yielding RPMI calls
Amirreza Zarrabi
amirreza.zarrabi at oss.qualcomm.com
Mon Oct 12 22:41:34 PDT 2026
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 at 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
More information about the linux-riscv
mailing list