SPM: Add Non-secure Client extension support

The Non-secure client extension is the interface to the non-secure side
for managing the non-secure client context in TF-M side.
For now, the context is used for the non-secure client ID only.

Signed-off-by: David Wang <david.wang@arm.com>
Change-Id: I374ebb2ee148165a3e1d77665cf39a6941087bb6
diff --git a/interface/include/tfm_ns_client_ext.h b/interface/include/tfm_ns_client_ext.h
new file mode 100644
index 0000000..b6e944c
--- /dev/null
+++ b/interface/include/tfm_ns_client_ext.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_NS_CLIENT_EXT_H__
+#define __TFM_NS_CLIENT_EXT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define TFM_NS_CLIENT_INVALID_TOKEN         0xFFFFFFFF
+
+/* TF-M NSID Error code */
+#define TFM_NS_CLIENT_ERR_SUCCESS           0x0
+#define TFM_NS_CLIENT_ERR_INVALID_TOKEN     0x1
+#define TFM_NS_CLIENT_ERR_INVALID_NSID      0x2
+#define TFM_NS_CLIENT_ERR_INVALID_ACCESS    0x3
+
+/**
+ * \brief Initialize the non-secure client extension
+ *
+ * \details This function should be called before any other non-secure client
+ *          APIs. It gives NSPE the opportunity to initialize the non-secure
+ *          client extension in TF-M. Also, NSPE can get the number of allocated
+ *          non-secure client context slots in the return value. That is useful
+ *          if NSPE wants to decide the group (context) assignment at runtime.
+ *
+ * \param[in] ctx_requested The number of non-secure context requested from the
+ *            NS entity. If request maximum available context, then set it to 0.
+ *
+ * \return Returns the number of non-secure context allocated to the NS entity.
+ *         The allocated context number <= maximum supported context number.
+ *         If the initialization is failed, then 0 is returned.
+ */
+uint32_t tfm_nsce_init(uint32_t ctx_requested);
+
+/**
+ * \brief Acquire the context for a non-secure client
+ *
+ * \details This function should be called before a non-secure client calling
+ *          the PSA API into TF-M. It is to request the allocation of the
+ *          context for the upcoming service call from that non-secure client.
+ *          The non-secure clients in one group share the same context.
+ *          The thread ID is used to identify the different non-secure clients.
+ *
+ * \param[in] group_id The group ID of the non-secure client
+ * \param[in] thread_id The thread ID of the non-secure client
+ *
+ * \return Returns the token of the allocated context. 0xFFFFFFFF means the
+ *         allocation failed and the token is invalid.
+ */
+uint32_t tfm_nsce_acquire_ctx(uint8_t group_id, uint8_t thread_id);
+
+/**
+ * \brief Release the context for the non-secure client
+ *
+ * \details This function should be called when a non-secure client is going to
+ *          be terminated or will not call TF-M secure services in the future.
+ *          It is to release the context allocated for the calling non-secure
+ *          client. If the calling non-secure client is the only thread in the
+ *          group, then the context will be deallocated. Otherwise, the context
+ *          will still be taken for the other threads in the group.
+ *
+ * \param[in] token The token returned by tfm_nsce_acquire_ctx
+ *
+ * \return Returns the error code.
+ */
+uint32_t tfm_nsce_release_ctx(uint32_t token);
+
+/**
+ * \brief Load the context for the non-secure client
+ *
+ * \details This function should be called when a non-secure client is going to
+ *          be scheduled in at the non-secure side.
+ *          The caller is usually the scheduler of the RTOS.
+ *          The non-secure client ID is managed by the non-secure world and
+ *          passed to TF-M as the input parameter of TF-M.
+ *
+ * \param[in] token The token returned by tfm_nsce_acquire_ctx
+ * \param[in] nsid The non-secure client ID for this client
+ *
+ * \return Returns the error code.
+ */
+uint32_t tfm_nsce_load_ctx(uint32_t token, int32_t nsid);
+
+/**
+ * \brief Save the context for the non-secure client
+ *
+ * \details This function should be called when a non-secure client is going to
+ *          be scheduled out at the non-secure side.
+ *          The caller is usually the scheduler of the RTOS.
+ *
+ * \param[in] token The token returned by tfm_nsce_acquire_ctx
+ *
+ * \return Returns the error code.
+ */
+uint32_t tfm_nsce_save_ctx(uint32_t token);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_NS_CLIENT_EXT_H__ */
diff --git a/secure_fw/spm/ns_client_ext/tfm_ns_client_ext.c b/secure_fw/spm/ns_client_ext/tfm_ns_client_ext.c
new file mode 100644
index 0000000..07c067d
--- /dev/null
+++ b/secure_fw/spm/ns_client_ext/tfm_ns_client_ext.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "tfm_arch.h"
+#include "tfm_nspm.h"
+#include "tfm_ns_client_ext.h"
+#include "tfm_ns_ctx.h"
+
+/*
+ * Definiton of NS Client Token
+ * bit[0:7] - Thread ID
+ * bit[8:15] - Group ID
+ * bit[16:23] - Context identifier
+ * bit[24:31] - Reserved, must be 0
+ */
+#define MAKE_NS_CLIENT_TOKEN(tid, gid, idx)                         \
+                             (uint32_t)((((uint32_t)tid & 0xff)     \
+                             | (((uint32_t)gid & 0xff) << 8)        \
+                             | (((uint32_t)idx & 0xff) << 16))      \
+                             & 0x00ffffff)
+#define IS_INVALID_TOKEN(token)             ((token) & 0xff000000)
+#define NS_CLIENT_TOKEN_TO_CTX_IDX(token)   (((token) >> 16) & 0xff)
+#define NS_CLIENT_TOKEN_TO_GID(token)       (((token) >> 8) & 0xff)
+#define NS_CLIENT_TOKEN_TO_TID(token)       ((token) & 0xff)
+
+__tfm_nspm_secure_gateway_attributes__
+uint32_t tfm_nsce_init(uint32_t ctx_requested)
+{
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return 0; /* Return 0 if the caller is from thread mode */
+    }
+
+    /* Nothing to initialize for now */
+    if (ctx_requested == 0) {
+        /* 0 means requesting maximum available context */
+        return TFM_NS_CONTEXT_MAX;
+    } else {
+        return ((ctx_requested <= TFM_NS_CONTEXT_MAX) ?
+                ctx_requested : TFM_NS_CONTEXT_MAX);
+    }
+}
+
+__tfm_nspm_secure_gateway_attributes__
+uint32_t tfm_nsce_acquire_ctx(uint8_t group_id, uint8_t thread_id)
+{
+    uint8_t ns_ctx_idx;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return TFM_NS_CLIENT_ERR_INVALID_ACCESS;
+    }
+
+    /* Try to allocate the non-secure context */
+    if (!acquire_ns_ctx(group_id, &ns_ctx_idx)) {
+        return TFM_NS_CLIENT_INVALID_TOKEN;
+    }
+
+    /* Return the token */
+    return MAKE_NS_CLIENT_TOKEN(thread_id, group_id, ns_ctx_idx);
+}
+
+__tfm_nspm_secure_gateway_attributes__
+uint32_t tfm_nsce_release_ctx(uint32_t token)
+{
+    /* Free the context slot assigned */
+    uint8_t ctx_idx;
+    uint8_t gid;
+    uint8_t tid;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return TFM_NS_CLIENT_ERR_INVALID_ACCESS;
+    }
+
+    if (IS_INVALID_TOKEN(token)) {
+        return TFM_NS_CLIENT_ERR_INVALID_TOKEN;
+    } else {
+        /* Extract the context index, group ID and thread ID from the token */
+        ctx_idx = NS_CLIENT_TOKEN_TO_CTX_IDX(token);
+        gid = NS_CLIENT_TOKEN_TO_GID(token);
+        tid = NS_CLIENT_TOKEN_TO_TID(token);
+    }
+
+    if (!release_ns_ctx(gid, tid, ctx_idx)) {
+        return TFM_NS_CLIENT_ERR_INVALID_TOKEN;
+    } else {
+        return TFM_NS_CLIENT_ERR_SUCCESS;
+    }
+}
+
+__tfm_nspm_secure_gateway_attributes__
+uint32_t tfm_nsce_load_ctx(uint32_t token, int32_t nsid)
+{
+    uint8_t ctx_idx;
+    uint8_t gid;
+    uint8_t tid;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return TFM_NS_CLIENT_ERR_INVALID_ACCESS;
+    }
+
+    /* Check if NSID is valid */
+    if (nsid >= 0) {
+        return TFM_NS_CLIENT_ERR_INVALID_NSID;
+    }
+
+    if (IS_INVALID_TOKEN(token)) {
+        return TFM_NS_CLIENT_ERR_INVALID_TOKEN;
+    } else {
+        /* Extract the context index, group ID and thread ID from the token */
+        ctx_idx = NS_CLIENT_TOKEN_TO_CTX_IDX(token);
+        gid = NS_CLIENT_TOKEN_TO_GID(token);
+        tid = NS_CLIENT_TOKEN_TO_TID(token);
+    }
+
+    if (!load_ns_ctx(gid, tid, nsid, ctx_idx)) {
+        return TFM_NS_CLIENT_ERR_INVALID_TOKEN;
+    } else {
+        return TFM_NS_CLIENT_ERR_SUCCESS;
+    }
+}
+
+__tfm_nspm_secure_gateway_attributes__
+uint32_t tfm_nsce_save_ctx(uint32_t token)
+{
+    uint8_t ctx_idx;
+    uint8_t gid;
+    uint8_t tid;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return TFM_NS_CLIENT_ERR_INVALID_ACCESS;
+    }
+
+    if (IS_INVALID_TOKEN(token)) {
+        return TFM_NS_CLIENT_ERR_INVALID_TOKEN;
+    } else {
+        /* Extract the context index, group ID and thread ID from the token */
+        ctx_idx = NS_CLIENT_TOKEN_TO_CTX_IDX(token);
+        gid = NS_CLIENT_TOKEN_TO_GID(token);
+        tid = NS_CLIENT_TOKEN_TO_TID(token);
+    }
+
+    if (!save_ns_ctx(gid, tid, ctx_idx)) {
+        return TFM_NS_CLIENT_ERR_INVALID_TOKEN;
+    } else {
+        return TFM_NS_CLIENT_ERR_SUCCESS;
+    }
+}