[PATCH RFC v2 5/8] tee: optee: add RPMI RPC handling

From: Amirreza Zarrabi

Date: Mon Oct 05 2026 - 20:42:27 EST


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@xxxxxxxxxxxxxxxx>
---
drivers/tee/optee/rpmi_abi.c | 109 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 109 insertions(+)

diff --git a/drivers/tee/optee/rpmi_abi.c b/drivers/tee/optee/rpmi_abi.c
index c29b7ac4552a..58d82678be98 100644
--- a/drivers/tee/optee/rpmi_abi.c
+++ b/drivers/tee/optee/rpmi_abi.c
@@ -13,6 +13,7 @@
#include <linux/unaligned.h>
#include "optee_private.h"
#include "optee_rpmi.h"
+#include "optee_rpc_cmd.h"

/* Nonzero nonce keeps parcel ID zero distinct from a null reference. */
#define OPTEE_RPMI_SHM_NONCE 1
@@ -451,3 +452,111 @@ 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)
+{
+ u32 parcel_id, nonce;
+ 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;
+ }
+
+ optee_rpmi_shm_get_identity(shm, &parcel_id, &nonce);
+ arg->params[0] = (struct optee_msg_param) {
+ .attr = OPTEE_MSG_ATTR_TYPE_PMEM_OUTPUT,
+ .u.pmem = {
+ .offs = shm->offset,
+ .size = tee_shm_get_size(shm),
+ .parcel_id = parcel_id,
+ .nonce = nonce,
+ },
+ };
+
+ 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, parcel_id, nonce;
+
+ 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;
+ parcel_id = arg->params[0].u.value.b;
+ nonce = arg->params[0].u.value.c;
+ if (parcel_id > U32_MAX || nonce > U32_MAX)
+ goto err_bad_param;
+
+ shm = optee_rpmi_get_shm_for_parcel(optee, parcel_id, nonce);
+ 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