This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 707cdaf42e6eb454b8761f7af6219aa77c748ecc
Author: Theodore Karatapanis <tkaratapa...@census-labs.com>
AuthorDate: Wed Jul 16 13:36:09 2025 +0300

    drivers/misc/optee: Expanded RPC support.
    
    This commit expands RPC support for the OP-TEE driver using 2 files:
    1) drivers/misc/optee_rpc.c
       * Add support for RPCs that can be handled directly by the kernel.
       * Can delegate RPC handling to optee_supplicant.c for RPCs that
         need userspace interaction.
    2) drivers/misc/optee_supplicant.c
       * Enable communication between the userspace TEE supplicant and the
         kernel driver.
    
    Additional changes were needed to the following files:
    1) drivers/misc/optee.c
       * Add ioctls used SOLELY by the userspace TEE supplicant.
       * Register /dev/teepriv0 if the supplicant is enabled in Kconfig
       * Add OPTEE_ROLE_CA and OPTEE_ROLE_SUPPLICANT conditionals to
         differentiate paths, between a normal Client Application (CA)
         and the TEE supplicant.
       * Change some functions from static to "public" to reuse them
         in other C files.
       * Adjust optee_to/from_msg_param() to work with RPCs.
    
    2) drivers/misc/optee_smc.c
       * Call the RPC handler from optee_rpc.c
    
    3) drivers/misc/optee_msg.h
       * Add definition needed for RPCs
    
    4) drivers/misc/tee.h
       * Add ioctl definitions
       * Add TEE_SHM_SUPP flag, checked when unregistering supplicant
         memory.
    
    5) Documentation/guides/optee.rs
       * Add documentation for RPCs and the supplicant.
    
    6) drivers/misc/{CMakeLists.txt, Make.defs}
       * Account for the new files.
    
    7) drivers/misc/Kconfig
       * Add DEV_OPTEE_SUPPLICANT option to enable/disable the supplicant
         driver.
    
    Signed-off-by: Theodore Karatapanis <tkaratapa...@census-labs.com>
---
 Documentation/guides/optee.rst  |  83 +++++-
 drivers/misc/CMakeLists.txt     |   4 +
 drivers/misc/Kconfig            |   5 +
 drivers/misc/Make.defs          |   4 +
 drivers/misc/optee.c            | 640 ++++++++++++++++++++++++++++------------
 drivers/misc/optee.h            |  37 ++-
 drivers/misc/optee_msg.h        | 134 +++++++++
 drivers/misc/optee_rpc.c        | 359 ++++++++++++++++++++++
 drivers/misc/optee_rpc.h        |  53 ++++
 drivers/misc/optee_smc.c        |  14 +-
 drivers/misc/optee_supplicant.c | 611 ++++++++++++++++++++++++++++++++++++++
 drivers/misc/optee_supplicant.h |  69 +++++
 include/nuttx/tee.h             |   1 +
 13 files changed, 1819 insertions(+), 195 deletions(-)

diff --git a/Documentation/guides/optee.rst b/Documentation/guides/optee.rst
index ab1adac9b3d..4260506e0fb 100644
--- a/Documentation/guides/optee.rst
+++ b/Documentation/guides/optee.rst
@@ -14,8 +14,16 @@ not officially supported by NuttX, and is out of the scope 
of this guide.
 
 The driver supports opening and closing sessions, allocating and registering
 shared memory, and invoking functions on OP-TEE Trusted Applications (TAs).
-It does not (yet) support reverse direction commands (TA -> Normal World)
-to something like a TEE supplicant.
+The driver also supports, reverse direction commands called RPCs
+(TA -> Normal World). Some of the RPCs are handled completely by the kernel
+driver while others require the TEE supplicant userspace process to be running
+by having opened (``/dev/teepriv#``). Similarly to libteec, the supplicant
+is not officially supported.
+
+.. note::
+   ``/dev/teepriv#`` is reserved solely for the supplicant and shouldn't be
+   used by any other NuttX application.
+
 
 Enabling the OP-TEE Driver
 ==========================
@@ -27,16 +35,14 @@ The driver is enabled using one of:
 - ``CONFIG_DEV_OPTEE_SMC``
 
 All of the above require also ``CONFIG_ALLOW_BSD_COMPONENTS`` and
-``CONFIG_LIBC_MEMFD_SHMFS``, which in turn requires ``CONFIG_FS_SHMFS``. So,
-at a bare minimum, to enable the driver one would need something like the
-following:
+``CONFIG_FS_ANONMAP``. So, at a bare minimum, to enable the driver
+one would need something like the following:
 
 .. code-block::
 
   CONFIG_ALLOW_BSD_COMPONENTS=y
   CONFIG_DEV_OPTEE_SMC=y
-  CONFIG_FS_SHMFS=y
-  CONFIG_LIBC_MEMFD_SHMFS=y
+  CONFIG_FS_ANONMAP=y
 
 Each implementation (local, RPMsg, or SMC) may have further dependencies
 (e.g. RPMsg requires ``CONFIG_NET_RPMSG`` and more) and may have further
@@ -49,10 +55,13 @@ parameters to configure (e.g. RPMsg remote CPU name through
   encounter issues with shared memory depending on the state of the data
   cache in Secure World.
 
+If ``CONFIG_DEV_OPTEE_SMC`` is enabled we can also enable the kernel driver
+for the TEE supplicant by using ``CONFIG_DEV_OPTEE_SUPPLICANT``.
+
 Successful registration of the driver can be verified by looking into
-``/dev/tee0``. For instance, incompatibility with the TEE OS running in the
-system, will prevent the ``/dev/tee0`` character device from being
-registered.
+``/dev/tee0`` and ``/dev/teepriv0`` (for the supplicant). For instance,
+incompatibility with the TEE OS running in the system, will prevent the
+``/dev/tee0`` character device from being registered.
 
 IOCTLs supported
 ================
@@ -130,6 +139,26 @@ on success unless otherwise specified (see 
``TEE_IOC_SHM_ALLOC``).
     returned ``.id`` field when specifying shared memory references
     (``tee_ioctl_param.c`` field).
 
+- ``TEE_IOC_SUPPL_RECV`` : Receive an RPC request from the OP-TEE that needs 
userspace interaction from the supplicant.
+
+  - Expects a pointer to a ``struct tee_ioctl_buf_data`` instance where the
+    ``.buf_ptr`` field points to a user allocated buffer that must hold a
+    ``struct tee_iocl_supp_send/recv_arg`` followed by a number of
+    ``struct tee_ioctl_param`` parameters.  The ``.buf_len`` field communicates
+    to the kernel the length of that buffer.  If the user passes a bigger 
number
+    of parameters than ``OPTEE_MAX_PARAM_NUM`` or smaller number of parameters 
than
+    the number sent by OP-TEE, the ioctl will fail. The TEE supplicant by 
default
+    uses 5 ``struct tee_ioctl_param`` parameters.
+
+- ``TEE_IOC_SUPPL_SEND`` : Respond to an RPC request from the OP-TEE that 
needed userspace interaction from the supplicant.
+
+  - Expects a pointer to a ``struct tee_ioctl_buf_data`` instance where the
+    ``.buf_ptr`` field points to a user allocated buffer that must hold a
+    ``struct tee_iocl_supp_send/recv_arg`` followed by a number of
+    ``struct tee_ioctl_param`` parameters.  The ``.buf_len`` field communicates
+    to the kernel the length of that buffer. The number of parameters depends 
on
+    the size of expected RPC response by the OP-TEE.
+
 Typical usage
 =============
 
@@ -307,3 +336,37 @@ Typical usage
        }
 
      /* use result (if any) in ioc_args->params */
+
+#. OP-TEE secure storage support through TEE supplicant
+
+   .. code-block:: shell
+
+     optee_supplicant -f /data/tee > /dev/null &
+
+This runs the OP-TEE supplicant in the background, using ``/data/tee`` as the
+directory for the TEE file system. Output is redirected to ``/dev/null`` to
+suppress standard output. Make sure that the userspace support for the
+supplicant is enabled and that ``/data`` is mounted as read/write.
+
+With the supplicant running, secure storage objects can be created, retrieved,
+and managed by Trusted Applications (TAs). In a typical workflow, a Client
+Application (CA) running on NuttX invokes a command in a TA that may need to
+read from or create persistent objects. In such cases, certain RPCs generated
+by OP-TEE are routed from the CA to the TEE supplicant for handling (provided
+the supplicant is running in the background). Once the supplicant has processed
+the request, it responds using ``TEE_IOC_SUPPL_SEND``, and the kernel driver
+delivers this response back to the CA in its context.
+
+#. OP-TEE REE time request
+
+In this scenario, the userspace supplicant isn't needed, as the response can be
+handled directly by the kernel driver.
+
+An OP-TEE application can request the current time from the NuttX clock using:
+
+   .. code-block:: c
+
+     TEE_GetREETime(&t);
+
+The NuttX kernel driver will respond to the TA with the ``CLOCK_REALTIME``
+which represents the machine's best-guess as to the current wall-clock.
diff --git a/drivers/misc/CMakeLists.txt b/drivers/misc/CMakeLists.txt
index 3c3a3b28d9c..fc7508b7c48 100644
--- a/drivers/misc/CMakeLists.txt
+++ b/drivers/misc/CMakeLists.txt
@@ -82,6 +82,10 @@ if(NOT CONFIG_DEV_OPTEE_NONE)
   list(APPEND SRCS optee.c)
   if(CONFIG_DEV_OPTEE_SMC)
     list(APPEND SRCS optee_smc.c)
+    list(APPEND SRCS optee_rpc.c)
+    if(CONFIG_DEV_OPTEE_SUPPLICANT)
+      list(APPEND SRCS optee_supplicant.c)
+    endif()
   else()
     list(APPEND SRCS optee_socket.c)
   endif()
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index a9fb7092598..b89d6bdb80d 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -73,6 +73,11 @@ config DEV_OPTEE_NONE
 
 endchoice
 
+config DEV_OPTEE_SUPPLICANT
+       bool "Enables kernel driver for TEE supplicant"
+       default n
+       depends on DEV_OPTEE_SMC
+
 if DEV_OPTEE_RPMSG
 
 config OPTEE_REMOTE_CPU_NAME
diff --git a/drivers/misc/Make.defs b/drivers/misc/Make.defs
index 3d2b45a95cf..722e192ec28 100644
--- a/drivers/misc/Make.defs
+++ b/drivers/misc/Make.defs
@@ -79,6 +79,10 @@ ifneq ($(CONFIG_DEV_OPTEE_NONE),y)
   CSRCS += optee.c
   ifeq ($(CONFIG_DEV_OPTEE_SMC),y)
     CSRCS += optee_smc.c
+    CSRCS += optee_rpc.c
+    ifeq ($(CONFIG_DEV_OPTEE_SUPPLICANT),y)
+      CSRCS += optee_supplicant.c
+    endif
   else
     CSRCS += optee_socket.c
   endif
diff --git a/drivers/misc/optee.c b/drivers/misc/optee.c
index ef883abc421..4426758d8ab 100644
--- a/drivers/misc/optee.c
+++ b/drivers/misc/optee.c
@@ -43,6 +43,10 @@
 
 #include "optee.h"
 
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+#  include "optee_supplicant.h"
+#endif
+
 /****************************************************************************
  * The driver's main purpose is to support the porting of the open source
  * component optee_client (https://github.com/OP-TEE/optee_client) to NuttX.
@@ -55,23 +59,8 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
-/* Some GlobalPlatform error codes used in this driver */
-
-#define TEE_SUCCESS                    0x00000000
-#define TEE_ERROR_ACCESS_DENIED        0xFFFF0001
-#define TEE_ERROR_BAD_FORMAT           0xFFFF0005
-#define TEE_ERROR_BAD_PARAMETERS       0xFFFF0006
-#define TEE_ERROR_NOT_SUPPORTED        0xFFFF000A
-#define TEE_ERROR_OUT_OF_MEMORY        0xFFFF000C
-#define TEE_ERROR_BUSY                 0xFFFF000D
-#define TEE_ERROR_COMMUNICATION        0xFFFF000E
-#define TEE_ERROR_SECURITY             0xFFFF000F
-#define TEE_ERROR_SHORT_BUFFER         0xFFFF0010
-#define TEE_ERROR_TIMEOUT              0xFFFF3001
-
-#define TEE_ORIGIN_COMMS               0x00000002
-
 #define OPTEE_DEV_PATH                 "/dev/tee0"
+#define OPTEE_SUPPLICANT_DEV_PATH      "/dev/teepriv0"
 
 /* According to optee_msg.h#OPTEE_MSG_ATTR_NONCONTIG */
 
@@ -187,35 +176,6 @@ static struct inode g_optee_shm_inode =
  * Private Functions
  ****************************************************************************/
 
-static int optee_convert_error(uint32_t oterr)
-{
-  switch (oterr)
-    {
-      case TEE_SUCCESS:
-        return 0;
-      case TEE_ERROR_ACCESS_DENIED:
-      case TEE_ERROR_SECURITY:
-        return -EACCES;
-      case TEE_ERROR_BAD_FORMAT:
-      case TEE_ERROR_BAD_PARAMETERS:
-        return -EINVAL;
-      case TEE_ERROR_NOT_SUPPORTED:
-        return -EOPNOTSUPP;
-      case TEE_ERROR_OUT_OF_MEMORY:
-        return -ENOMEM;
-      case TEE_ERROR_BUSY:
-        return -EBUSY;
-      case TEE_ERROR_COMMUNICATION:
-        return -ECOMM;
-      case TEE_ERROR_SHORT_BUFFER:
-        return -ENOBUFS;
-      case TEE_ERROR_TIMEOUT:
-        return -ETIMEDOUT;
-      default:
-        return -EIO;
-    }
-}
-
 /****************************************************************************
  * Name: optee_is_valid_range
  *
@@ -401,7 +361,7 @@ static int optee_shm_register(FAR struct optee_priv_data 
*priv,
 
   if (msg->ret)
     {
-      ret = optee_convert_error(msg->ret);
+      ret = optee_convert_to_errno(msg->ret);
     }
 
 errout_with_list:
@@ -452,7 +412,7 @@ static int optee_shm_unregister(FAR struct optee_priv_data 
*priv,
 
   if (msg->ret)
     {
-      ret = optee_convert_error(msg->ret);
+      ret = optee_convert_to_errno(msg->ret);
     }
 
 errout_with_msg:
@@ -481,7 +441,6 @@ static int optee_shm_close(FAR struct file *filep)
   if (shm != NULL && shm->id > -1)
     {
       filep->f_priv = NULL;
-      shm->fd = -1;
       optee_shm_free(shm);
     }
 
@@ -559,6 +518,7 @@ static int optee_shm_mmap(FAR struct file *filep,
 static int optee_open(FAR struct file *filep)
 {
   FAR struct optee_priv_data *priv;
+  enum optee_role_e role = (uintptr_t)filep->f_inode->i_private;
   int ret;
 
   ret = optee_transport_open(&priv);
@@ -567,7 +527,30 @@ static int optee_open(FAR struct file *filep)
       return ret;
     }
 
-  priv->shms = idr_init();
+  priv->role = role;
+
+  if (role == OPTEE_ROLE_CA)
+    {
+      priv->shms = idr_init();
+    }
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+  else if (role == OPTEE_ROLE_SUPPLICANT)
+    {
+      /* Allow only one process to open the device. */
+
+      if (filep->f_inode->i_crefs > 2)
+        {
+          return -EBUSY;
+        }
+
+      optee_supplicant_init(&priv->shms);
+    }
+#endif
+  else
+    {
+      return -EOPNOTSUPP;
+    }
+
   filep->f_priv = priv;
   return 0;
 }
@@ -590,26 +573,29 @@ static int optee_close(FAR struct file *filep)
 {
   FAR struct optee_priv_data *priv = filep->f_priv;
   FAR struct optee_shm *shm;
-  FAR struct file *shm_filep;
   int id = 0;
 
   idr_for_each_entry(priv->shms, shm, id)
     {
-      if (shm->fd > -1 && file_get(shm->fd, &shm_filep) >= 0)
-        {
-          /* The user did not call close(), prevent vfs auto-close from
-           * double-freeing our SHM
-           */
+      /* Here, we only free, unfreed kernel allocations, the rest will be
+       * done by optee_shm_close().
+       */
 
-          shm_filep->f_priv = NULL;
-          file_put(shm_filep);
+      if (shm->fd == -1)
+        {
+          optee_shm_free(shm);
         }
-
-      optee_shm_free(shm);
     }
 
   idr_destroy(priv->shms);
   optee_transport_close(priv);
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+  if (priv->role == OPTEE_ROLE_SUPPLICANT)
+    {
+      optee_supplicant_uninit();
+    }
+#endif
+
   return 0;
 }
 
@@ -619,6 +605,7 @@ static int optee_memref_to_msg_param(FAR struct 
optee_priv_data *priv,
 {
   FAR struct optee_shm *shm;
   uintptr_t page_list_pa;
+  bool external_vm_context = false;
 
   if (p->c == TEE_MEMREF_NULL)
     {
@@ -633,7 +620,17 @@ static int optee_memref_to_msg_param(FAR struct 
optee_priv_data *priv,
   shm = idr_find(priv->shms, p->c);
   if (shm == NULL)
     {
-      return -EINVAL;
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+      /* Search also the shared memory registered by the supplicant. */
+
+      shm = optee_supplicant_search_shm(p->c);
+      external_vm_context = true;
+
+      if (shm == NULL)
+#endif
+        {
+          return -EINVAL;
+        }
     }
 
   if (shm->flags & TEE_SHM_REGISTER)
@@ -654,6 +651,15 @@ static int optee_memref_to_msg_param(FAR struct 
optee_priv_data *priv,
                  TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
       mp->attr |= OPTEE_MSG_ATTR_NONCONTIG;
 
+      /* This shouldn't happen, we can't translate vmas from
+       * another vm context.
+       */
+
+      if (external_vm_context)
+        {
+          return -EPROTO;
+        }
+
       shm->page_list = optee_shm_to_page_list(shm, &page_list_pa);
       if (shm->page_list == NULL)
         {
@@ -662,7 +668,7 @@ static int optee_memref_to_msg_param(FAR struct 
optee_priv_data *priv,
 
       mp->u.tmem.buf_ptr = page_list_pa;
       mp->u.tmem.shm_ref = (uintptr_t)shm;
-      mp->u.tmem.size = shm->length;
+      mp->u.tmem.size = p->b;
     }
 
 #ifndef CONFIG_ARCH_USE_MMU
@@ -672,121 +678,6 @@ static int optee_memref_to_msg_param(FAR struct 
optee_priv_data *priv,
   return 0;
 }
 
-static int optee_to_msg_param(FAR struct optee_priv_data *priv,
-                              FAR struct optee_msg_param *mparams,
-                              size_t num_params,
-                              FAR const struct tee_ioctl_param *params)
-{
-  size_t n;
-  int ret;
-
-  for (n = 0; n < num_params; n++)
-    {
-      FAR const struct tee_ioctl_param *p = params + n;
-      FAR struct optee_msg_param *mp = mparams + n;
-
-      if (p->attr & ~TEE_IOCTL_PARAM_ATTR_MASK)
-        {
-          return -EINVAL;
-        }
-
-      switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK)
-        {
-          case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
-            mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
-            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:
-            mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
-                       TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
-            mp->u.value.a = p->a;
-            mp->u.value.b = p->b;
-            mp->u.value.c = p->c;
-            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:
-            ret = optee_memref_to_msg_param(priv, mp, p);
-            if (ret < 0)
-              {
-                return ret;
-              }
-            break;
-          default:
-            return -EINVAL;
-        }
-    }
-
-  return 0;
-}
-
-static int optee_from_msg_param(FAR struct tee_ioctl_param *params,
-                                size_t num_params,
-                                FAR const struct optee_msg_param *mparams)
-{
-  size_t n;
-
-  for (n = 0; n < num_params; n++)
-    {
-      FAR const struct optee_msg_param *mp = mparams + n;
-      FAR struct tee_ioctl_param *p = params + n;
-      FAR struct optee_shm *shm = NULL;
-
-      switch (mp->attr & OPTEE_MSG_ATTR_TYPE_MASK)
-        {
-          case OPTEE_MSG_ATTR_TYPE_NONE:
-            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
-            p->a = 0;
-            p->b = 0;
-            p->c = 0;
-            break;
-          case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
-          case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
-          case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
-            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
-                      mp->attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
-            p->a = mp->u.value.a;
-            p->b = mp->u.value.b;
-            p->c = mp->u.value.c;
-            break;
-          case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
-          case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
-          case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
-            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
-                      mp->attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
-            p->b = mp->u.tmem.size;
-
-            shm = (FAR struct optee_shm *)(uintptr_t)mp->u.tmem.shm_ref;
-            if (shm && shm->page_list)
-              {
-                kmm_free(shm->page_list);
-                shm->page_list = NULL;
-              }
-            break;
-          case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
-          case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
-          case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
-            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
-                      mp->attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
-            p->b = mp->u.rmem.size;
-            shm = (FAR struct optee_shm *)(uintptr_t)mp->u.tmem.shm_ref;
-            break;
-          default:
-            return -EINVAL;
-        }
-
-#ifndef CONFIG_ARCH_USE_MMU
-      if (shm)
-        {
-          up_invalidate_dcache(shm->vaddr, shm->vaddr + shm->length);
-        }
-#endif
-    }
-
-  return 0;
-}
-
 static int optee_close_session(FAR struct optee_priv_data *priv,
                                uint32_t session)
 {
@@ -1047,8 +938,6 @@ optee_ioctl_shm_alloc(FAR struct optee_priv_data *priv,
   ret = file_allocate_from_inode(&g_optee_shm_inode,
                                  O_CLOEXEC | O_RDOK, 0, shm, 0);
 
-  /* Will free automatically the shm once the descriptor is closed. */
-
   if (ret < 0)
     {
       optee_shm_free(shm);
@@ -1061,6 +950,43 @@ optee_ioctl_shm_alloc(FAR struct optee_priv_data *priv,
   return shm->fd;
 }
 
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+static int
+optee_shm_register_supplicant(FAR struct optee_priv_data *priv,
+                              uintptr_t addr, uint64_t length,
+                              FAR struct optee_shm **shmp)
+{
+  FAR struct optee_shm *shm;
+  uintptr_t page_list_pa;
+  int ret = 0;
+
+  shm = kmm_zalloc(sizeof(struct optee_shm));
+  *shmp = shm;
+  if (shm == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  shm->fd = -1;
+  shm->priv = priv;
+  shm->vaddr = addr;
+  shm->length = length;
+  shm->flags = TEE_SHM_REGISTER | TEE_SHM_SUPP;
+  shm->page_list = optee_shm_to_page_list(shm, &page_list_pa);
+  shm->paddr = page_list_pa;
+  shm->id = idr_alloc(priv->shms, shm, 0, 0);
+
+  if (shm->id < 0)
+    {
+      kmm_free(shm->page_list);
+      kmm_free(shm);
+      return -ENOMEM;
+    }
+
+  return ret;
+}
+#endif
+
 static int
 optee_ioctl_shm_register(FAR struct optee_priv_data *priv,
                          FAR struct tee_ioctl_shm_register_data *rdata)
@@ -1084,8 +1010,25 @@ optee_ioctl_shm_register(FAR struct optee_priv_data 
*priv,
       return -EINVAL;
     }
 
-  ret = optee_shm_alloc(priv, (FAR void *)(uintptr_t)rdata->addr,
-                        rdata->length, TEE_SHM_REGISTER, &shm);
+  if (priv->role == OPTEE_ROLE_CA)
+    {
+      ret = optee_shm_alloc(priv, (FAR void *)(uintptr_t)rdata->addr,
+                            rdata->length, TEE_SHM_REGISTER, &shm);
+    }
+
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+  else if (priv->role == OPTEE_ROLE_SUPPLICANT)
+    {
+      ret = optee_shm_register_supplicant(priv, (uintptr_t)rdata->addr,
+                                          rdata->length, &shm);
+      rdata->flags = shm->flags;
+    }
+#endif
+  else
+    {
+      return -ENODEV;
+    }
+
   if (ret < 0)
     {
       return ret;
@@ -1103,6 +1046,114 @@ optee_ioctl_shm_register(FAR struct optee_priv_data 
*priv,
   return ret;
 }
 
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+static int
+optee_ioctl_supplicant_recv(FAR struct optee_priv_data *priv,
+                            FAR struct tee_ioctl_buf_data *data)
+{
+  int n;
+  int ret;
+  FAR struct tee_iocl_supp_recv_arg *arg;
+
+  if (!optee_is_valid_range(data, sizeof(*data)))
+    {
+      return -EFAULT;
+    }
+
+  if (!optee_is_valid_range((FAR void *)data->buf_ptr, data->buf_len))
+    {
+      return -EFAULT;
+    }
+
+  if (data->buf_len > TEE_MAX_ARG_SIZE ||
+      data->buf_len < sizeof(struct tee_iocl_supp_recv_arg))
+    {
+      return -EINVAL;
+    }
+
+  arg = (FAR struct tee_iocl_supp_recv_arg *)(uintptr_t)data->buf_ptr;
+
+  if (sizeof(*arg) + TEE_IOCTL_PARAM_SIZE(arg->num_params) !=
+      data->buf_len)
+    {
+      return -EINVAL;
+    }
+
+  if (arg->num_params > OPTEE_MAX_PARAM_NUM)
+    {
+      return -EINVAL;
+    }
+
+  ret = optee_supplicant_recv(&arg->func, &arg->num_params, arg->params);
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  for (n = 0; n < arg->num_params; n++)
+    {
+      FAR struct tee_ioctl_param *p = arg->params + n;
+
+      switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK)
+        {
+          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 (!p->b)
+              {
+                p->a = 0;
+                p->c = (uint64_t)-1; /* Invalid shm id. */
+                break;
+              }
+          break;
+        default:
+          break;
+        }
+    }
+
+  return 0;
+}
+
+static int
+optee_ioctl_supplicant_send(FAR struct optee_priv_data *priv,
+                            FAR struct tee_ioctl_buf_data *data)
+{
+  FAR struct tee_iocl_supp_send_arg *arg;
+
+  if (!optee_is_valid_range(data, sizeof(*data)))
+    {
+      return -EFAULT;
+    }
+
+  if (!optee_is_valid_range((FAR void *)data->buf_ptr, data->buf_len))
+    {
+      return -EFAULT;
+    }
+
+  if (data->buf_len > TEE_MAX_ARG_SIZE ||
+      data->buf_len < sizeof(struct tee_iocl_supp_send_arg))
+    {
+      return -EINVAL;
+    }
+
+  arg = (FAR struct tee_iocl_supp_send_arg *)(uintptr_t)data->buf_ptr;
+
+  if (sizeof(*arg) + TEE_IOCTL_PARAM_SIZE(arg->num_params) !=
+      data->buf_len)
+    {
+      return -EINVAL;
+    }
+
+  if (arg->num_params > OPTEE_MAX_PARAM_NUM)
+    {
+      return -EINVAL;
+    }
+
+  return optee_supplicant_send(arg->ret, arg->num_params, arg->params);
+}
+#endif
+
 /****************************************************************************
  * Name: optee_ioctl
  *
@@ -1140,6 +1191,12 @@ static int optee_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
         return optee_ioctl_shm_alloc(priv, parg);
       case TEE_IOC_SHM_REGISTER:
         return optee_ioctl_shm_register(priv, parg);
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+      case TEE_IOC_SUPPL_RECV:
+        return optee_ioctl_supplicant_recv(priv, parg);
+      case TEE_IOC_SUPPL_SEND:
+        return optee_ioctl_supplicant_send(priv, parg);
+#endif
       default:
         return -ENOTTY;
     }
@@ -1151,6 +1208,49 @@ static int optee_ioctl(FAR struct file *filep, int cmd, 
unsigned long arg)
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: optee_convert_to_errno
+ *
+ * Description:
+ *   Convert TEE errors to errno values
+ *
+ * Parameters:
+ *   oterr - TEE error code.
+ *
+ * Returned Values:
+ *   The converted errno value.
+ *
+ ****************************************************************************/
+
+int optee_convert_to_errno(uint32_t oterr)
+{
+  switch (oterr)
+    {
+      case TEE_SUCCESS:
+        return 0;
+      case TEE_ERROR_ACCESS_DENIED:
+      case TEE_ERROR_SECURITY:
+        return -EACCES;
+      case TEE_ERROR_BAD_FORMAT:
+      case TEE_ERROR_BAD_PARAMETERS:
+        return -EINVAL;
+      case TEE_ERROR_NOT_SUPPORTED:
+        return -EOPNOTSUPP;
+      case TEE_ERROR_OUT_OF_MEMORY:
+        return -ENOMEM;
+      case TEE_ERROR_BUSY:
+        return -EBUSY;
+      case TEE_ERROR_COMMUNICATION:
+        return -ECOMM;
+      case TEE_ERROR_SHORT_BUFFER:
+        return -ENOBUFS;
+      case TEE_ERROR_TIMEOUT:
+        return -ETIMEDOUT;
+      default:
+        return -EIO;
+    }
+}
+
 /****************************************************************************
  * Name: optee_va_to_pa
  *
@@ -1318,7 +1418,7 @@ void optee_shm_free(FAR struct optee_shm *shm)
       return;
     }
 
-  if (shm->flags & TEE_SHM_REGISTER)
+  if (!(shm->flags & TEE_SHM_SUPP) && (shm->flags & TEE_SHM_REGISTER))
     {
       optee_shm_unregister(shm->priv, shm);
     }
@@ -1354,5 +1454,181 @@ int optee_register(void)
       return ret;
     }
 
-  return register_driver(OPTEE_DEV_PATH, &g_optee_ops, 0666, NULL);
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+  ret = register_driver(OPTEE_SUPPLICANT_DEV_PATH, &g_optee_ops, 0666,
+                        (FAR void *)OPTEE_ROLE_SUPPLICANT);
+  if (ret < 0)
+    {
+      return ret;
+    }
+#endif
+
+  return register_driver(OPTEE_DEV_PATH, &g_optee_ops, 0666,
+                         (FAR void *)OPTEE_ROLE_CA);
+}
+
+/****************************************************************************
+ * Name: optee_from_msg_param
+ *
+ * Description:
+ *   Converts and copies the message parameters received by OP-TEE to buffer
+ *   for processing by nuttx.
+ *
+ * Parameters:
+ *   params - Pointer, to copy the received parameters after some processing.
+ *   num_params - Number of these parameters.
+ *   mparams - Pointer to the message parameters received by OP-TEE.
+ *
+ * Returned Values:
+ *   OK on success; A negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+int optee_from_msg_param(FAR struct tee_ioctl_param *params,
+                         size_t num_params,
+                         FAR const struct optee_msg_param *mparams)
+{
+  size_t n;
+
+  for (n = 0; n < num_params; n++)
+    {
+      FAR const struct optee_msg_param *mp = mparams + n;
+      FAR struct tee_ioctl_param *p = params + n;
+      FAR struct optee_shm *shm = NULL;
+
+      switch (mp->attr & OPTEE_MSG_ATTR_TYPE_MASK)
+        {
+          case OPTEE_MSG_ATTR_TYPE_NONE:
+            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+            p->a = 0;
+            p->b = 0;
+            p->c = 0;
+            break;
+          case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
+          case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
+          case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
+            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
+                      mp->attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
+            p->a = mp->u.value.a;
+            p->b = mp->u.value.b;
+            p->c = mp->u.value.c;
+            break;
+          case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
+          case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
+          case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
+            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+                      mp->attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
+            p->b = mp->u.tmem.size;
+
+            shm = (FAR struct optee_shm *)(uintptr_t)mp->u.tmem.shm_ref;
+            if (shm && shm->page_list)
+              {
+                kmm_free(shm->page_list);
+                shm->page_list = NULL;
+                p->c = shm->id;
+              }
+            else
+              {
+                p->c = TEE_MEMREF_NULL;
+              }
+            break;
+          case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
+          case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
+          case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
+            p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+                      mp->attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
+            p->b = mp->u.rmem.size;
+            p->a = mp->u.rmem.offs;
+            shm = (FAR struct optee_shm *)(uintptr_t)mp->u.tmem.shm_ref;
+            if (shm)
+              {
+                p->c = shm->id;
+              }
+            else
+              {
+                p->c = TEE_MEMREF_NULL;
+              }
+            break;
+          default:
+            return -EINVAL;
+        }
+
+#ifndef CONFIG_ARCH_USE_MMU
+          if (shm)
+            {
+              up_invalidate_dcache(shm->vaddr, shm->vaddr + shm->length);
+            }
+#endif
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: optee_to_msg_param
+ *
+ * Description:
+ *   Converts and copies the processed by nuttx parameters to the shared
+ *   memory area containing the message to/from the OP-TEE.
+ *
+ * Parameters:
+ *   priv - pointer to the driver's optee_priv_data struct
+ *   mparams - Pointer to the message parameters provided by OP-TEE.
+ *   params - Pointer, of the processed by nuttx parameters containing the
+ *            response.
+ *   num_params - Number of these parameters.
+ *
+ * Returned Values:
+ *   OK on success; A negated errno value is returned on any failure.
+ *
+ ****************************************************************************/
+
+int optee_to_msg_param(FAR struct optee_priv_data *priv,
+                       FAR struct optee_msg_param *mparams,
+                       size_t num_params,
+                       FAR const struct tee_ioctl_param *params)
+{
+  size_t n;
+  int ret;
+
+  for (n = 0; n < num_params; n++)
+    {
+      FAR const struct tee_ioctl_param *p = params + n;
+      FAR struct optee_msg_param *mp = mparams + n;
+
+      if (p->attr & ~TEE_IOCTL_PARAM_ATTR_MASK)
+        {
+          return -EINVAL;
+        }
+
+      switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK)
+        {
+          case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
+            mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
+            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:
+            mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
+                       TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+            mp->u.value.a = p->a;
+            mp->u.value.b = p->b;
+            mp->u.value.c = p->c;
+            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:
+            ret = optee_memref_to_msg_param(priv, mp, p);
+            if (ret < 0)
+              {
+                return ret;
+              }
+            break;
+          default:
+            return -EINVAL;
+        }
+    }
+
+  return 0;
 }
+
diff --git a/drivers/misc/optee.h b/drivers/misc/optee.h
index db09b4993f5..c2fd32a8001 100644
--- a/drivers/misc/optee.h
+++ b/drivers/misc/optee.h
@@ -38,6 +38,23 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+/* Some GlobalPlatform error codes used in this driver */
+
+#define TEE_SUCCESS                    0x00000000
+#define TEE_ERROR_ACCESS_DENIED        0xFFFF0001
+#define TEE_ERROR_BAD_FORMAT           0xFFFF0005
+#define TEE_ERROR_BAD_PARAMETERS       0xFFFF0006
+#define TEE_ERROR_GENERIC              0xFFFF0000
+#define TEE_ERROR_NOT_SUPPORTED        0xFFFF000A
+#define TEE_ERROR_OUT_OF_MEMORY        0xFFFF000C
+#define TEE_ERROR_BUSY                 0xFFFF000D
+#define TEE_ERROR_COMMUNICATION        0xFFFF000E
+#define TEE_ERROR_SECURITY             0xFFFF000F
+#define TEE_ERROR_SHORT_BUFFER         0xFFFF0010
+#define TEE_ERROR_TIMEOUT              0xFFFF3001
+
+#define TEE_ORIGIN_COMMS               0x00000002
+
 #define OPTEE_SERVER_PATH              "optee"
 #define OPTEE_MAX_PARAM_NUM            6
 
@@ -45,10 +62,17 @@
  * Public Types
  ****************************************************************************/
 
+enum optee_role_e
+{
+  OPTEE_ROLE_CA,              /* /dev/tee0   */
+  OPTEE_ROLE_SUPPLICANT,      /* /dev/tee-supp0 */
+};
+
 struct optee_priv_data
 {
   uintptr_t alignment;        /* Transport-specified message alignment */
-  FAR struct idr_s *shms;     /* An RB tree of all shm entries */
+  FAR struct idr_s *shms;     /* An RB tree of process local shm entries */
+  enum optee_role_e role;
 };
 
 struct optee_shm
@@ -88,9 +112,20 @@ void optee_shm_free(FAR struct optee_shm *shm);
 int optee_transport_init(void);
 int optee_transport_open(FAR struct optee_priv_data **priv);
 void optee_transport_close(FAR struct optee_priv_data *priv);
+
 int optee_transport_call(FAR struct optee_priv_data *priv,
                          FAR struct optee_msg_arg *arg);
 
+int optee_from_msg_param(FAR struct tee_ioctl_param *params,
+                         size_t num_params,
+                         FAR const struct optee_msg_param *mparams);
+
+int optee_to_msg_param(FAR struct optee_priv_data *priv,
+                       FAR struct optee_msg_param *mparams,
+                       size_t num_params,
+                       FAR const struct tee_ioctl_param *params);
+
+int optee_convert_to_errno(uint32_t oterr);
 #undef EXTERN
 #if defined(__cplusplus)
 }
diff --git a/drivers/misc/optee_msg.h b/drivers/misc/optee_msg.h
index 31cf0b5146f..0dfba8b41ea 100644
--- a/drivers/misc/optee_msg.h
+++ b/drivers/misc/optee_msg.h
@@ -22,6 +22,8 @@
  * This file is divided into two sections.
  * 1. Formatting of messages.
  * 2. Requests from normal world
+ * 3. Requests from secure world, Remote Procedure Call (RPC), handled by
+ *    tee-supplicant
  */
 
 /****************************************************************************
@@ -352,4 +354,136 @@ struct optee_msg_arg
 #define OPTEE_MSG_CMD_STOP_ASYNC_NOTIF  7
 #define OPTEE_MSG_FUNCID_CALL_WITH_ARG  0x0004
 
+/****************************************************************************
+ * Part 3 - Requests from secure world, RPC
+ ****************************************************************************/
+
+/* All RPC is done with a struct optee_msg_arg as bearer of information,
+ * struct optee_msg_arg::arg holds values defined by OPTEE_MSG_RPC_CMD_*
+ * below
+ *
+ * RPC communication with tee-supplicant is reversed compared to normal
+ * client communication described above. The supplicant receives requests
+ * and sends responses.
+ *
+ * Load a TA into memory, defined in tee-supplicant
+ */
+
+#define OPTEE_MSG_RPC_CMD_LOAD_TA       0
+
+/* Reserved */
+
+#define OPTEE_MSG_RPC_CMD_RPMB          1
+
+/* File system access, defined in tee-supplicant */
+
+#define OPTEE_MSG_RPC_CMD_FS            2
+
+/* Get time
+ *
+ * Returns number of seconds and nano seconds since the Epoch,
+ * 1970-01-01 00:00:00 +0000 (UTC).
+ *
+ * [out] param[0].u.value.a Number of seconds
+ * [out] param[0].u.value.b Number of nano seconds.
+ */
+
+#define OPTEE_MSG_RPC_CMD_GET_TIME      3
+
+/* Wait queue primitive, helper for secure world to implement a wait queue.
+ *
+ * If secure world need to wait for a secure world mutex it issues a sleep
+ * request instead of spinning in secure world. Conversely is a wakeup
+ * request issued when a secure world mutex with a thread waiting thread is
+ * unlocked.
+ *
+ * Waiting on a key
+ * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP
+ * [in] param[0].u.value.b wait key
+ *
+ * Waking up a key
+ * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP
+ * [in] param[0].u.value.b wakeup key
+ */
+
+#define OPTEE_MSG_RPC_CMD_WAIT_QUEUE    4
+#define OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP  0
+#define OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP 1
+
+/* Suspend execution
+ *
+ * [in] param[0].value  .a number of milliseconds to suspend
+ */
+
+#define OPTEE_MSG_RPC_CMD_SUSPEND       5
+
+/* Allocate a piece of shared memory
+ *
+ * Shared memory can optionally be fragmented, to support that additional
+ * spare param entries are allocated to make room for eventual fragments.
+ * The spare param entries has .attr = OPTEE_MSG_ATTR_TYPE_NONE when
+ * unused. All returned temp memrefs except the last should have the
+ * OPTEE_MSG_ATTR_FRAGMENT bit set in the attr field.
+ *
+ * [in]  param[0].u.value.a   type of memory one of
+ *          OPTEE_MSG_RPC_SHM_TYPE_* below
+ * [in]  param[0].u.value.b   requested size
+ * [in]  param[0].u.value.c   required alignment
+ *
+ * [out] param[0].u.tmem.buf_ptr  physical address (of first fragment)
+ * [out] param[0].u.tmem.size   size (of first fragment)
+ * [out] param[0].u.tmem.shm_ref  shared memory reference
+ * ...
+ * [out] param[n].u.tmem.buf_ptr  physical address
+ * [out] param[n].u.tmem.size   size
+ * [out] param[n].u.tmem.shm_ref  shared memory reference (same value
+ *          as in param[n-1].u.tmem.shm_ref)
+ */
+
+#define OPTEE_MSG_RPC_CMD_SHM_ALLOC     6
+
+/* Memory that can be shared with a non-secure user space application */
+
+#define OPTEE_MSG_RPC_SHM_TYPE_APPL     0
+
+/* Memory only shared with non-secure kernel */
+
+#define OPTEE_MSG_RPC_SHM_TYPE_KERNEL   1
+
+/* Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC
+ *
+ * [in]  param[0].u.value.a   type of memory one of
+ *          OPTEE_MSG_RPC_SHM_TYPE_* above
+ * [in]  param[0].u.value.b   value of shared memory reference
+ *          returned in param[0].u.tmem.shm_ref
+ *          above
+ */
+
+#define OPTEE_MSG_RPC_CMD_SHM_FREE      7
+
+/* Access a device on an i2c bus
+ *
+ * [in]  param[0].u.value.a   mode: RD(0), WR(1)
+ * [in]  param[0].u.value.b   i2c adapter
+ * [in]  param[0].u.value.c   i2c chip
+ *
+ * [in]  param[1].u.value.a   i2c control flags
+ *
+ * [in/out] memref[2]     buffer to exchange the transfer data
+ *          with the secure world
+ *
+ * [out]  param[3].u.value.a    bytes transferred by the driver
+ */
+
+#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER  21
+
+/* I2C master transfer modes */
+
+#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER_RD 0
+#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER_WR 1
+
+/* I2C master control flags */
+
+#define OPTEE_MSG_RPC_CMD_I2C_FLAGS_TEN_BIT  BIT(0)
+
 #endif /* __DRIVERS_MISC_OPTEE_MSG_H */
diff --git a/drivers/misc/optee_rpc.c b/drivers/misc/optee_rpc.c
new file mode 100644
index 00000000000..5a83fa803ad
--- /dev/null
+++ b/drivers/misc/optee_rpc.c
@@ -0,0 +1,359 @@
+/****************************************************************************
+ * drivers/misc/optee_rpc.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/signal.h>
+#include <nuttx/kmalloc.h>
+#include <stdint.h>
+
+#include "optee.h"
+#include "optee_msg.h"
+#include "optee_rpc.h"
+
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+#  include "optee_supplicant.h"
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: optee_rpc_cmd_get_time
+ *
+ * Description:
+ *   Return REE wall-clock time (seconds + nanoseconds) to secure world.
+ *
+ * Parameters:
+ *   arg - Pointer to the RPC message argument located in shared memory by
+ *         the secure world. The answer will be placed in the same argument.
+ *
+ * Returned Value:
+ *   The time is written to:
+ *     arg->params[0].u.value.a   containing seconds since epoch
+ *     arg->params[0].u.value.b   containing nanoseconds
+ *   Result code is written to arg->ret.
+ *
+ ****************************************************************************/
+
+static void optee_rpc_cmd_get_time(FAR struct optee_msg_arg *arg)
+{
+  struct timespec ts;
+
+  /* OP-TEE parameter validation. */
+
+  if (arg->num_params != 1 ||
+      (arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK)
+      != OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT)
+    {
+      arg->ret = TEE_ERROR_BAD_PARAMETERS;
+      return;
+    }
+
+  if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
+    {
+      /* Should not happen unless the RTC driver is missing */
+
+      arg->ret = TEE_ERROR_GENERIC;
+      return;
+    }
+
+  arg->params[0].u.value.a = (uint32_t)ts.tv_sec;   /* Seconds since epoch. */
+  arg->params[0].u.value.b = (uint32_t)ts.tv_nsec;  /* Nanoseconds.         */
+
+  arg->ret = TEE_SUCCESS;
+}
+
+/****************************************************************************
+ * Name: optee_rpc_cmd_suspend
+ *
+ * Description:
+ *   Request from OP-TEE to suspend the current nuttx process.
+ *
+ * Parameters:
+ *   arg - Pointer to the RPC message argument, located in a shared page, by
+ *         the secure world, containing the time in msec to sleep.
+ *
+ * Returned Value:
+ *   None.  Result codes are written into arg->ret.
+ *
+ ****************************************************************************/
+
+static void optee_rpc_cmd_suspend(FAR struct optee_msg_arg *arg)
+{
+  useconds_t usec_to_wait;
+
+  /* OP-TEE parameter validation. */
+
+  if (arg->num_params != 1 ||
+      (arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) !=
+      OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
+    {
+      arg->ret = TEE_ERROR_BAD_PARAMETERS;
+      return;
+    }
+
+  usec_to_wait = arg->params[0].u.value.a * 1000;
+
+  if (usec_to_wait)
+    {
+      int ret = nxsig_usleep(usec_to_wait);
+
+      if (ret < 0 && ret != -EINTR)
+        {
+            arg->ret = TEE_ERROR_GENERIC;
+            return;
+        }
+    }
+
+  arg->ret = TEE_SUCCESS;
+}
+
+/****************************************************************************
+ * Name: optee_rpc_cmd_shm_alloc
+ *
+ * Description:
+ *   Handle OP-TEE's RPC to allocate shared memory.
+ *
+ * Parameters:
+ *   priv - Pointer to the driver's optee_priv_data struct.
+ *   arg - Pointer to the RPC message argument, located in a shared page
+ *         by the secure world. A copy of this message might be sent to the
+ *         supplicant process that runs in userspace for further processing.
+ *   last_page_list - Passes by reference a pointer that will be updated with
+ *                    the virtual address of the page list.
+ *
+ * Returned Value:
+ *   None.  Result codes are written into arg->ret.
+ *   Information about the shared memory is passed through arg->params
+ *
+ ****************************************************************************/
+
+static void optee_rpc_cmd_shm_alloc(FAR struct optee_priv_data *priv,
+                                    FAR struct optee_msg_arg *arg,
+                                    FAR void **last_page_list)
+{
+  FAR struct optee_shm *shm;
+  size_t n;
+  size_t size;
+  int32_t ret = -EINVAL;
+
+  arg->ret_origin = TEE_ORIGIN_COMMS;
+
+  /* OP-TEE parameter validation. */
+
+  if (arg->num_params == 0 ||
+      arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
+    {
+      arg->ret = TEE_ERROR_BAD_PARAMETERS;
+      return;
+    }
+
+  for (n = 1; n < arg->num_params; n++)
+    {
+      if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE)
+        {
+          arg->ret = TEE_ERROR_BAD_PARAMETERS;
+          return;
+        }
+    }
+
+  size = arg->params[0].u.value.b;
+  switch (arg->params[0].u.value.a)
+    {
+      case OPTEE_MSG_RPC_SHM_TYPE_APPL:
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+        ret = optee_supplicant_alloc(priv, size, &shm);
+        break;
+#else
+        arg->ret = TEE_ERROR_NOT_SUPPORTED;
+        return;
+#endif
+      case OPTEE_MSG_RPC_SHM_TYPE_KERNEL:
+        ret = optee_shm_alloc(priv, NULL, size, TEE_SHM_ALLOC, &shm);
+        break;
+    }
+
+  if (ret == -ENOMEM)
+    {
+      arg->ret = TEE_ERROR_OUT_OF_MEMORY;
+      return;
+    }
+  else if (ret == -ECOMM)
+    {
+      arg->ret = TEE_ERROR_COMMUNICATION;
+      return;
+    }
+  else if (ret == -EINVAL)
+    {
+      arg->ret = TEE_ERROR_BAD_PARAMETERS;
+      return;
+    }
+  else if (ret != OK)
+    {
+      arg->ret = TEE_ERROR_GENERIC;
+      return;
+    }
+
+  if (shm->flags & TEE_SHM_REGISTER)
+    {
+      arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
+                            OPTEE_MSG_ATTR_NONCONTIG;
+      arg->params[0].u.tmem.buf_ptr = shm->paddr;
+      arg->params[0].u.tmem.size = shm->length;
+      arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+      *last_page_list = shm->page_list;
+    }
+  else
+    {
+      arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
+      arg->params[0].u.tmem.buf_ptr = shm->paddr;
+      arg->params[0].u.tmem.size = size;
+      arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+    }
+
+  arg->ret = TEE_SUCCESS;
+}
+
+/****************************************************************************
+ * Name: optee_rpc_cmd_shm_free
+ *
+ * Description:
+ *   RPC Request from OP-TEE to free shared memory allocated by nuttx.
+ *
+ * Parameters:
+ *   priv - Pointer to the driver's optee_priv_data struct.
+ *   arg  - Pointer to the RPC message argument, located in a shared page
+ *          by the secure world. A copy of this message might be sent to the
+ *          supplicant process that runs in userspace for further processing.
+ *
+ * Returned Value:
+ *   None.  Result codes are written into arg->ret.
+ *
+ ****************************************************************************/
+
+static void optee_rpc_cmd_shm_free(FAR struct optee_priv_data *priv,
+                                   FAR struct optee_msg_arg *arg)
+{
+  FAR struct optee_shm *shm;
+
+  arg->ret_origin = TEE_ORIGIN_COMMS;
+
+  /* OP-TEE parameter validation. */
+
+  if (arg->num_params != 1 ||
+      arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
+    {
+      arg->ret = TEE_ERROR_BAD_PARAMETERS;
+      return;
+    }
+
+  shm = (FAR struct optee_shm *)(unsigned long)arg->params[0].u.value.b;
+
+  switch (arg->params[0].u.value.a)
+    {
+      case OPTEE_MSG_RPC_SHM_TYPE_APPL:
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+        arg->ret = optee_supplicant_free(shm->id);
+        if (arg->ret)
+            {
+              /* The supplicant either failed or isn't running. */
+
+              return;
+            }
+#else
+        arg->ret = TEE_ERROR_NOT_SUPPORTED;
+        return;
+#endif
+        break;
+      case OPTEE_MSG_RPC_SHM_TYPE_KERNEL:
+        optee_shm_free(shm);
+        arg->ret = TEE_SUCCESS;
+        break;
+      default:
+        arg->ret = TEE_ERROR_BAD_PARAMETERS;
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: optee_rpc_handle_cmd
+ *
+ * Description:
+ *   Handler of RPC requests.
+ *
+ * Parameters:
+ *   priv - Pointer to the driver's optee_priv_data struct.
+ *   shm  - Contains a pointer to the RPC message argument, located in a
+ *          shared page, by the secure world. A copy of this message
+ *          might be sent to the supplicant process that runs in userspace
+ *          for further processing.
+ *   last_page_list - Passes by reference a pointer to the virtual address
+ *                    of a page list. The page list can be freed by a
+ *                    caller or by this function, depending on the response
+ *                    of the OP-TEE to the next SMC.
+ *
+ * Returned Value:
+ *   None. The response to OP-TEE will passed through the shared memory.
+ *
+ ****************************************************************************/
+
+void optee_rpc_handle_cmd(FAR struct optee_priv_data *priv,
+                          FAR struct optee_shm *shm,
+                          FAR void **last_page_list)
+{
+  FAR struct optee_msg_arg *arg;
+
+  DEBUGASSERT(shm != NULL);
+
+  arg = (FAR struct optee_msg_arg *)shm->vaddr;
+
+  switch (arg->cmd)
+    {
+      case OPTEE_MSG_RPC_CMD_GET_TIME:
+        optee_rpc_cmd_get_time(arg);
+        break;
+      case OPTEE_MSG_RPC_CMD_SUSPEND:
+        optee_rpc_cmd_suspend(arg);
+        break;
+      case OPTEE_MSG_RPC_CMD_SHM_ALLOC:
+        kmm_free(*last_page_list);
+        *last_page_list = NULL;
+        optee_rpc_cmd_shm_alloc(priv, arg, last_page_list);
+        break;
+      case OPTEE_MSG_RPC_CMD_SHM_FREE:
+        optee_rpc_cmd_shm_free(priv, arg);
+        break;
+      default:
+#ifdef CONFIG_DEV_OPTEE_SUPPLICANT
+        optee_supplicant_cmd(priv, arg);
+#else
+        arg->ret = TEE_ERROR_NOT_SUPPORTED;
+#endif
+    }
+}
diff --git a/drivers/misc/optee_rpc.h b/drivers/misc/optee_rpc.h
new file mode 100644
index 00000000000..0907fd1b768
--- /dev/null
+++ b/drivers/misc/optee_rpc.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+ * drivers/misc/optee_rpc.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_MISC_OPTEE_RPC_H
+#define __DRIVERS_MISC_OPTEE_RPC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "optee.h"
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+void optee_rpc_handle_cmd(FAR struct optee_priv_data *priv,
+                          FAR struct optee_shm *shm,
+                          FAR void **last_page_list);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+#endif /* __DRIVERS_MISC_OPTEE_RPC_H */
diff --git a/drivers/misc/optee_smc.c b/drivers/misc/optee_smc.c
index 740ef0f9309..bff5a041cd7 100644
--- a/drivers/misc/optee_smc.c
+++ b/drivers/misc/optee_smc.c
@@ -33,6 +33,7 @@
 
 #include "optee.h"
 #include "optee_smc.h"
+#include "optee_rpc.h"
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -177,7 +178,8 @@ static bool optee_smc_is_compatible(optee_smc_fn smc_fn)
 }
 
 static void optee_smc_handle_rpc(FAR struct optee_priv_data *priv_,
-                                 FAR smccc_res_t *par)
+                                 FAR smccc_res_t *par,
+                                 FAR void **last_page_list)
 {
   FAR struct optee_shm *shm;
   uint32_t rpc_func;
@@ -211,6 +213,10 @@ static void optee_smc_handle_rpc(FAR struct 
optee_priv_data *priv_,
 
         sched_yield();
         break;
+      case OPTEE_SMC_RPC_FUNC_CMD:
+        shm = reg_pair_to_ptr(par->a1, par->a2);
+        optee_rpc_handle_cmd(priv_, shm, last_page_list);
+        break;
 
       default:
         syslog(LOG_ERR, "OP-TEE: RPC 0x%04x not implemented\n", rpc_func);
@@ -323,6 +329,7 @@ int optee_transport_call(FAR struct optee_priv_data *priv_,
 
   up_clean_dcache((uintptr_t)arg, (uintptr_t)arg + arg_size);
 #endif
+  FAR void *last_page_list = NULL;
 
   memset(&par, 0, sizeof(smccc_res_t));
 
@@ -341,13 +348,16 @@ int optee_transport_call(FAR struct optee_priv_data 
*priv_,
       if (OPTEE_SMC_RETURN_IS_RPC(res.a0))
         {
           memcpy(&par, &res, 4 * sizeof(unsigned long));
-          optee_smc_handle_rpc(priv_, &par);
+          optee_smc_handle_rpc(priv_, &par, &last_page_list);
         }
       else
         {
 #ifndef CONFIG_ARCH_USE_MMU
           up_invalidate_dcache((uintptr_t)arg, (uintptr_t)arg + arg_size);
 #endif
+
+          kmm_free(last_page_list);
+          last_page_list = NULL;
           return (int)res.a0;
         }
     }
diff --git a/drivers/misc/optee_supplicant.c b/drivers/misc/optee_supplicant.c
new file mode 100644
index 00000000000..c600bf7f364
--- /dev/null
+++ b/drivers/misc/optee_supplicant.c
@@ -0,0 +1,611 @@
+/****************************************************************************
+ * drivers/misc/optee_supplicant.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/mutex.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/queue.h>
+#include <nuttx/idr.h>
+#include <string.h>
+
+#include "optee.h"
+#include "optee_supplicant.h"
+#include "optee_msg.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Request structure for RPCs serviced by the supplicant. */
+
+struct optee_supplicant_req
+{
+  sq_entry_t                  link;
+  uint32_t                    func;
+  uint32_t                    ret;
+  uint32_t                    num_params;
+  FAR struct tee_ioctl_param *params;
+  sem_t                       c;
+};
+
+struct optee_supplicant
+{
+  mutex_t           mutex;
+  int               req_id;
+  struct sq_queue_s reqs;
+  FAR struct idr_s *idr;
+  FAR struct idr_s *shm_idr;
+  sem_t             reqs_c;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct optee_supplicant g_optee_supp;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pop_entry
+ *
+ * Description:
+ *   Pop the first request from the request queue, and create unique id.
+ *
+ * Parameters:
+ *   num_params - Number of parameters passed.
+ *   id         - Pointer to the unique request id.
+ *
+ * Returned Value:
+ *   A pointer to the request on success or NULL.
+ *
+ ****************************************************************************/
+
+static FAR struct optee_supplicant_req *pop_entry(uint32_t num_params,
+                                                  FAR int *id)
+{
+  FAR struct optee_supplicant_req *req;
+
+  if (g_optee_supp.req_id != -1)
+    {
+      /* Mixing sync/async not supported */
+
+      return NULL;
+    }
+
+  if (sq_empty(&g_optee_supp.reqs))
+    {
+      return NULL;
+    }
+
+  req = (FAR struct optee_supplicant_req *)sq_remfirst(&g_optee_supp.reqs);
+
+  /* The request can't fit in the supplicant's supplied parameter buffer. */
+
+  if (num_params < req->num_params)
+    {
+      return NULL;
+    }
+
+  *id = idr_alloc(g_optee_supp.idr, req, 0, INT32_MAX);
+  if (*id < 0)
+    {
+      return NULL;
+    }
+
+  return req;
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_request
+ *
+ * Description:
+ *   Create a request with the received parameters from the OP-TEE, add it to
+ *   the supplicant's request queue and then wait on the request's semaphore
+ *   until it is serviced.
+ *
+ * Parameters:
+ *   func - Requested function for the supplicant to perform
+ *   params - Pointer pointer to parameter data.
+ *   num_params - Number of parameters passed.
+ *
+ * Returned Value:
+ *   TEE_SUCCESS on success or a global platform api error code on failure.
+ *
+ ****************************************************************************/
+
+static uint32_t optee_supplicant_request(uint32_t func, uint32_t num_params,
+                                         FAR struct tee_ioctl_param *params)
+{
+  FAR struct optee_supplicant_req req;
+  uint32_t ret;
+
+  /* The supplicant isn't running if the shm_idr == NULL. */
+
+  if (NULL == g_optee_supp.shm_idr)
+    {
+      return TEE_ERROR_COMMUNICATION;
+    }
+
+  sem_init(&req.c, 0, 0);
+  req.func = func;
+  req.num_params = num_params;
+  req.params = params;
+
+  nxmutex_lock(&g_optee_supp.mutex);
+  sq_addlast(&req.link, &g_optee_supp.reqs);
+  nxmutex_unlock(&g_optee_supp.mutex);
+
+  /* Wake supplicant receiver */
+
+  sem_post(&g_optee_supp.reqs_c);
+
+  /* Wait for completion if supplicant is running. */
+
+  while (sem_wait(&req.c) < 0)
+    {
+    }
+
+  ret = req.ret;
+  sem_destroy(&req.c);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: optee_supplicant_init
+ *
+ * Description:
+ *   Initialize supplicant data.
+ *
+ * Parameters:
+ *   shm_idr - A pointer, passed by reference, to the optee driver's shm_idr.
+ *             The destruction of the shm_idr will be handled by
+ *             optee_close(), so we only need to initialize it in this
+ *             context.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void optee_supplicant_init(FAR struct idr_s **shm_idr)
+{
+  memset(&g_optee_supp, 0, sizeof(g_optee_supp));
+  nxmutex_init(&g_optee_supp.mutex);
+  nxsem_init(&g_optee_supp.reqs_c, 0, 0);
+  sq_init(&g_optee_supp.reqs);
+  g_optee_supp.idr = idr_init();
+  g_optee_supp.shm_idr = idr_init();
+  g_optee_supp.req_id = -1;
+
+  /* Pass .shm_idr to the caller. */
+
+  *shm_idr = g_optee_supp.shm_idr;
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_uninit
+ *
+ * Description:
+ *   Uninitialize supplicant data.
+ *
+ * Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void optee_supplicant_uninit(void)
+{
+  /* The destruction of the idr_s is performed by optee_close(). */
+
+  g_optee_supp.shm_idr = NULL;
+  nxmutex_destroy(&g_optee_supp.mutex);
+  nxsem_destroy(&g_optee_supp.reqs_c);
+  idr_destroy(g_optee_supp.idr);
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_recv
+ *
+ * Description:
+ *   This function is invoked by an ioctl, used only by the supplicant. It
+ *   will obtain the function to be performed and the parameters passed by
+ *   OP-TEE.
+ *
+ * Parameters:
+ *   func - Pointer to obtain the function to be performed by the supplicant.
+ *   params - Pointer to the parameter data passed to supplicant.
+ *   num_params - Pointer with the number of parameters the supplicant can
+ *                process, and later updated with the number of parameters
+ *                of the OP-TEE's RPC request.
+ *
+ * Returned Value:
+ *   0 on success, a negated errno on failure.
+ *
+ ****************************************************************************/
+
+int optee_supplicant_recv(FAR uint32_t *func, FAR uint32_t *num_params,
+                          FAR struct tee_ioctl_param *params)
+{
+  FAR struct optee_supplicant_req *req = NULL;
+  int id;
+  int n;
+  uint8_t num_meta = (params->attr == TEE_IOCTL_PARAM_ATTR_META);
+
+  if (0 == num_params)
+    {
+      return -EINVAL;
+    }
+
+  for (n = 0; n < *num_params; n++)
+    {
+      if (params[n].attr &&
+          params[n].attr != TEE_IOCTL_PARAM_ATTR_META)
+        {
+          return -EINVAL;
+        }
+    }
+
+  for (; ; )
+    {
+      nxmutex_lock(&g_optee_supp.mutex);
+      req = pop_entry(*num_params - num_meta, &id);
+      nxmutex_unlock(&g_optee_supp.mutex);
+
+      if (req)
+        {
+          break;
+        }
+
+      if (sem_wait(&g_optee_supp.reqs_c) < 0)
+        {
+          return -EINTR;
+        }
+    }
+
+  if (num_meta)
+    {
+      params->attr |= TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
+      params->a = id;
+      params->b = 0;
+      params->c = 0;
+    }
+  else
+    {
+      nxmutex_lock(&g_optee_supp.mutex);
+      g_optee_supp.req_id = id;
+      nxmutex_unlock(&g_optee_supp.mutex);
+    }
+
+  /* Setup parameters */
+
+  *func = req->func;
+  *num_params = req->num_params + num_meta;
+
+  memcpy(params + num_meta, req->params,
+         req->num_params * sizeof(params[0]));
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_send
+ *
+ * Description:
+ *   This function is invoked by an ioctl, used only by the supplicant. It
+ *   will update the parameters of the OP-TEE request with the response from
+ *   the supplicant.
+ *
+ * Parameters:
+ *   ret - The return value to send to OP-TEE.
+ *   params - Contains the response parameters from nuttx.
+ *   num_params - Number of parameters.
+ *
+ * Returned Value:
+ *   0 on success, a negated errno on failure.
+ *
+ ****************************************************************************/
+
+int optee_supplicant_send(uint32_t ret, uint32_t num_params,
+                          FAR struct tee_ioctl_param *params)
+{
+  FAR struct optee_supplicant_req *req;
+  int id;
+  uint8_t num_meta = 0;
+  const uint32_t async_attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
+                              TEE_IOCTL_PARAM_ATTR_META;
+
+  nxmutex_lock(&g_optee_supp.mutex);
+
+  /* Check the parameters and obtain the request from the idr. */
+
+  if (!num_params)
+    {
+      nxmutex_unlock(&g_optee_supp.mutex);
+      return -EINVAL;
+    }
+
+  /* Async. */
+
+  if (g_optee_supp.req_id == -1)
+    {
+      if (params->attr != async_attr)
+        {
+          nxmutex_unlock(&g_optee_supp.mutex);
+          return -EINVAL;
+        }
+
+      id = params->a;
+      num_meta = 1;
+    }
+  else
+    {
+      /* Sync. */
+
+      id = g_optee_supp.req_id;
+      num_meta = 0;
+    }
+
+  req = idr_find(g_optee_supp.idr, id);
+  if (!req)
+    {
+      nxmutex_unlock(&g_optee_supp.mutex);
+      return -ENOENT;
+    }
+
+  if ((num_params - num_meta) != req->num_params)
+    {
+      nxmutex_unlock(&g_optee_supp.mutex);
+      return -EINVAL;
+    }
+
+  idr_remove(g_optee_supp.idr, id);
+  g_optee_supp.req_id = -1;
+
+  nxmutex_unlock(&g_optee_supp.mutex);
+
+  if (!req)
+    {
+      return -EINVAL;
+    }
+
+  /* Update output and in/out parameters. */
+
+  for (size_t n = 0; n < req->num_params; n++)
+    {
+      FAR struct tee_ioctl_param *p = &req->params[n];
+      FAR struct tee_ioctl_param *r = &params[n + num_meta];
+
+      switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK)
+        {
+          case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+          case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+            p->a = r->a;
+            p->b = r->b;
+            p->c = r->c;
+            break;
+
+          case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+          case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+            p->b = r->b;
+            break;
+
+          default:
+            break;
+        }
+    }
+
+  req->ret = ret;
+  sem_post(&req->c);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_alloc
+ *
+ * Description:
+ *   Prepares and creates a request for userspace memory allocation that was
+ *   requested by the OP-TEE through an RPC.
+ *
+ * Parameters:
+ *   priv - Pointer to the driver's optee_priv_data struct.
+ *   size - Size to allocate.
+ *   shm - Passed by reference pointer to shared memory. On success it will
+ *         be updated with the shared memory the supplicant allocated.
+ *
+ * Returned Value:
+ *   0 on success, a negated errno on failure.
+ *
+ ****************************************************************************/
+
+int optee_supplicant_alloc(FAR struct optee_priv_data *priv,
+                           size_t size, FAR struct optee_shm **shm)
+{
+  uint32_t ret;
+  struct tee_ioctl_param param;
+
+  param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
+  param.a = OPTEE_MSG_RPC_SHM_TYPE_APPL;
+  param.b = size;
+  param.c = 0;
+
+  ret = optee_supplicant_request(OPTEE_MSG_RPC_CMD_SHM_ALLOC, 1, &param);
+  if (ret)
+    {
+      return optee_convert_to_errno(ret);
+    }
+
+  if (NULL == g_optee_supp.shm_idr)
+    {
+      return -ECOMM;
+    }
+
+  *shm = idr_find(g_optee_supp.shm_idr, param.c);
+
+  if (NULL == *shm)
+    {
+      return -ENOENT;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_free
+ *
+ * Description:
+ *   Handles freeing of shared memory allocated by the TEE supplicant.
+ *
+ * Parameters:
+ *   shm_id - The id of the shared memory to be freed.
+ *
+ * Returned Value:
+ *   TEE_SUCCESS on success or a global platform api error code on failure.
+ *
+ ****************************************************************************/
+
+uint32_t optee_supplicant_free(int32_t shm_id)
+{
+  struct tee_ioctl_param param;
+  uint32_t ret;
+
+  param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
+  param.a = OPTEE_MSG_RPC_SHM_TYPE_APPL;
+  param.b = shm_id;
+  param.c = 0;
+
+  ret = optee_supplicant_request(OPTEE_MSG_RPC_CMD_SHM_FREE, 1, &param);
+
+  if (ret != TEE_SUCCESS)
+    {
+      return ret;
+    }
+
+  if (NULL == g_optee_supp.shm_idr)
+    {
+      ret = TEE_ERROR_COMMUNICATION;
+      return ret;
+    }
+
+  idr_remove(g_optee_supp.shm_idr, shm_id);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_cmd
+ *
+ * Description:
+ *   Copy the parameters from OP-TEE's message and then create a request to
+ *   supplicant to service this RPC request.
+ *
+ * Parameters:
+ *   priv - Pointer to the driver's optee_priv_data struct.
+ *   arg  - Pointer to the RPC message argument, located in a shared page, by
+ *          by the secure world. A copy of this message will be sent to the
+ *          supplicant process that runs in userspace for further processing.
+ *
+ * Returned Value:
+ *   None.  Result codes are written into arg->ret.
+ *
+ ****************************************************************************/
+
+void optee_supplicant_cmd(FAR struct optee_priv_data *priv,
+                          FAR struct optee_msg_arg *arg)
+{
+  FAR struct tee_ioctl_param *params;
+  int ret = 0;
+
+  arg->ret_origin = TEE_ORIGIN_COMMS;
+
+  params = kmm_zalloc(TEE_IOCTL_PARAM_SIZE(arg->num_params));
+  if (!params)
+    {
+      arg->ret = TEE_ERROR_OUT_OF_MEMORY;
+      return;
+    }
+
+  if (optee_from_msg_param(params, arg->num_params, arg->params))
+    {
+      arg->ret = TEE_ERROR_BAD_PARAMETERS;
+      goto out;
+    }
+
+  arg->ret = optee_supplicant_request(arg->cmd, arg->num_params, params);
+
+  if ((ret = optee_to_msg_param(priv, arg->params, arg->num_params, params)))
+    {
+      if (ret == -ENOMEM)
+        {
+          arg->ret = TEE_ERROR_OUT_OF_MEMORY;
+        }
+      else if (ret == -EPROTO)
+        {
+          arg->ret = TEE_ERROR_COMMUNICATION;
+        }
+      else if (ret == -EINVAL)
+        {
+          arg->ret = TEE_ERROR_BAD_PARAMETERS;
+        }
+      else
+        {
+          arg->ret = TEE_ERROR_GENERIC;
+        }
+    }
+
+out:
+  kmm_free(params);
+}
+
+/****************************************************************************
+ * Name: optee_supplicant_search_shm
+ *
+ * Description:
+ *   Search the supplicant's idr_s for a specific id and return a pointer to
+ *   that shared memory if found.
+ *
+ * Parameters:
+ *   id - Shared memory id to search for, in the supplicant's idr_s.
+ *
+ * Returned Value:
+ *   Pointer to the shm with the specified id or NULL.
+ *
+ ****************************************************************************/
+
+FAR struct optee_shm *optee_supplicant_search_shm(uint32_t id)
+{
+  return idr_find(g_optee_supp.shm_idr, id);
+}
diff --git a/drivers/misc/optee_supplicant.h b/drivers/misc/optee_supplicant.h
new file mode 100644
index 00000000000..65dc3d4c60d
--- /dev/null
+++ b/drivers/misc/optee_supplicant.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+ * drivers/misc/optee_supplicant.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_MISC_OPTEE_SUPPLICANT_H
+#define __DRIVERS_MISC_OPTEE_SUPPLICANT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/types.h>
+
+#include "optee.h"
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+void optee_supplicant_init(FAR struct idr_s **shm_idr);
+void optee_supplicant_uninit(void);
+
+FAR struct optee_shm *optee_supplicant_search_shm(uint32_t id);
+
+uint32_t optee_supplicant_free(int32_t shm_id);
+int optee_supplicant_alloc(FAR struct optee_priv_data *priv,
+                           size_t sz, FAR struct optee_shm **shm);
+
+void optee_supplicant_cmd(FAR struct optee_priv_data *priv,
+                          FAR struct optee_msg_arg *arg);
+
+int optee_supplicant_send(uint32_t ret, uint32_t num_params,
+                          FAR struct tee_ioctl_param *param);
+
+int optee_supplicant_recv(FAR uint32_t *func, FAR uint32_t *num_params,
+                          FAR struct tee_ioctl_param *params);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+#endif /* __DRIVERS_MISC_OPTEE_SUPPLICANT_H */
diff --git a/include/nuttx/tee.h b/include/nuttx/tee.h
index 8534d7e5b12..870e45b54ff 100644
--- a/include/nuttx/tee.h
+++ b/include/nuttx/tee.h
@@ -383,6 +383,7 @@ struct tee_iocl_supp_send_arg
 #define TEE_SHM_ALLOC    (1 << 0) /* Kernel-malloced and must freed */
 #define TEE_SHM_REGISTER (1 << 1) /* Registered with TEE OS */
 #define TEE_SHM_USER_MAP (1 << 2) /* Will be used by userspace after mmap */
+#define TEE_SHM_SUPP     (1 << 4) /* Registered by supplicant */
 
 /* struct tee_ioctl_shm_register_data - Shared memory register argument
  * addr:      [in] Start address of shared memory to register

Reply via email to