Core: implement TZ context handling if used by NS RTOS

Change-Id: Iaecd473d90d4b51b0174aa5d7695d59fa13fec51
Signed-off-by: Miklos Balint <miklos.balint@arm.com>
diff --git a/secure_fw/core/CMakeLists.inc b/secure_fw/core/CMakeLists.inc
index 91ba969..f625cd0 100644
--- a/secure_fw/core/CMakeLists.inc
+++ b/secure_fw/core/CMakeLists.inc
@@ -34,6 +34,7 @@
 		"${SS_CORE_DIR}/tfm_handler.c"
 		"${SS_CORE_DIR}/tfm_secure_api.c"
 		"${SS_CORE_DIR}/tfm_unpriv_api.c"
+		"${SS_CORE_DIR}/tfm_nspm.c"
 	)
 
 #Append all our source files to global lists.
diff --git a/secure_fw/core/tfm_nspm.c b/secure_fw/core/tfm_nspm.c
new file mode 100644
index 0000000..76ba608
--- /dev/null
+++ b/secure_fw/core/tfm_nspm.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include "secure_utilities.h"
+
+#ifndef TFM_MAX_NS_THREAD_COUNT
+#define TFM_MAX_NS_THREAD_COUNT 8
+#endif
+#define INVALID_CLIENT_ID 0
+
+typedef uint32_t TZ_ModuleId_t;
+typedef uint32_t TZ_MemoryId_t;
+
+static struct ns_client_list_t {
+    int32_t ns_client_id;
+    int32_t next_free_index;
+} NsClientIdList[TFM_MAX_NS_THREAD_COUNT];
+
+static int32_t next_ns_client_id = -1;
+static int32_t free_index = 0U;
+static int32_t active_ns_client = INVALID_CLIENT_ID;
+
+void tfm_nspm_configure_clients(void)
+{
+    int32_t i;
+
+    /* Default to one NS client */
+    free_index = 1;
+    NsClientIdList[0].ns_client_id = next_ns_client_id--;
+    for (i = 1; i < TFM_MAX_NS_THREAD_COUNT; ++i) {
+        NsClientIdList[i].ns_client_id = INVALID_CLIENT_ID;
+    }
+}
+
+/* TF-M implementation of the CMSIS TZ RTOS thread context management API */
+
+/// Initialize secure context memory system
+/// \return execution status (1: success, 0: error)
+/* This veneer is TF-M internal, not a secure service */
+__attribute__((cmse_nonsecure_entry))
+uint32_t TZ_InitContextSystem_S(void)
+{
+    int32_t i;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return 0U;
+    }
+
+    /* NS RTOS supports TZ context management, override defaults */
+    LOG_MSG("NS RTOS initialized TZ RTOS context management");
+    for (i = 0; i < TFM_MAX_NS_THREAD_COUNT; ++i) {
+        NsClientIdList[i].ns_client_id = INVALID_CLIENT_ID;
+        NsClientIdList[i].next_free_index = i + 1;
+    }
+
+    /* Terminate list */
+    NsClientIdList[i - 1].next_free_index = -1;
+    free_index = 0;
+    /* Success */
+    return 1U;
+}
+
+
+/// Allocate context memory for calling secure software modules in TrustZone
+/// \param[in]  module   identifies software modules called from non-secure mode
+/// \return value != 0 id TrustZone memory slot identifier
+/// \return value 0    no memory available or internal error
+/* This veneer is TF-M internal, not a secure service */
+__attribute__((cmse_nonsecure_entry))
+TZ_MemoryId_t TZ_AllocModuleContext_S (TZ_ModuleId_t module)
+{
+    TZ_MemoryId_t tz_id;
+    (void) module; /* Currently unused */
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return 0U;
+    }
+
+    if (free_index < 0) {
+        /* No more free slots */
+        return 0U;
+    }
+
+    /* TZ_MemoryId_t must be a positive integer */
+    tz_id = free_index + 1;
+    NsClientIdList[free_index].ns_client_id = next_ns_client_id--;
+    printf("TZ_AllocModuleContext_S called, returning id %d\r\n",
+        NsClientIdList[free_index].ns_client_id);
+    free_index = NsClientIdList[free_index].next_free_index;
+
+    return tz_id;
+}
+
+
+/// Free context memory that was previously allocated with \ref TZ_AllocModuleContext_S
+/// \param[in]  id  TrustZone memory slot identifier
+/// \return execution status (1: success, 0: error)
+/* This veneer is TF-M internal, not a secure service */
+__attribute__((cmse_nonsecure_entry))
+uint32_t TZ_FreeModuleContext_S (TZ_MemoryId_t id)
+{
+    uint32_t index;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return 0U;
+    }
+
+    if ((id == 0U) || (id > TFM_MAX_NS_THREAD_COUNT)) {
+        /* Invalid TZ_MemoryId_t */
+        return 0U;
+    }
+
+    index = id - 1;
+
+    if (NsClientIdList[index].ns_client_id == INVALID_CLIENT_ID) {
+        /* Non-existent client */
+        return 0U;
+    }
+
+    printf("TZ_FreeModuleContext_S called for id %d\r\n",
+        NsClientIdList[index].ns_client_id);
+    if (active_ns_client == NsClientIdList[index].ns_client_id) {
+        printf("Freeing active NS client, NS inactive\r\n");
+        active_ns_client = INVALID_CLIENT_ID;
+    }
+    NsClientIdList[index].ns_client_id = INVALID_CLIENT_ID;
+    NsClientIdList[index].next_free_index = free_index;
+
+    free_index = index;
+
+    return 1U;    // Success
+}
+
+
+/// Load secure context (called on RTOS thread context switch)
+/// \param[in]  id  TrustZone memory slot identifier
+/// \return execution status (1: success, 0: error)
+/* This veneer is TF-M internal, not a secure service */
+__attribute__((cmse_nonsecure_entry))
+uint32_t TZ_LoadContext_S (TZ_MemoryId_t id)
+{
+    uint32_t index;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return 0U;
+    }
+
+    LOG_MSG("TZ_LoadContext_S called");
+    if ((id == 0U) || (id > TFM_MAX_NS_THREAD_COUNT)) {
+        /* Invalid TZ_MemoryId_t */
+        return 0U;
+    }
+
+    index = id - 1;
+
+    if (NsClientIdList[index].ns_client_id == INVALID_CLIENT_ID) {
+        /* Non-existent client */
+        return 0U;
+    }
+
+    active_ns_client = NsClientIdList[index].ns_client_id;
+    printf("TZ_LoadContext_S called for id %d\r\n",
+        NsClientIdList[index].ns_client_id);
+
+    return 1U;    // Success
+}
+
+
+/// Store secure context (called on RTOS thread context switch)
+/// \param[in]  id  TrustZone memory slot identifier
+/// \return execution status (1: success, 0: error)
+/* This veneer is TF-M internal, not a secure service */
+__attribute__((cmse_nonsecure_entry))
+uint32_t TZ_StoreContext_S (TZ_MemoryId_t id)
+{
+    uint32_t index;
+
+    if (__get_active_exc_num() == EXC_NUM_THREAD_MODE) {
+        /* This veneer should only be called by NS RTOS in handler mode */
+        return 0U;
+    }
+
+    LOG_MSG("TZ_StoreContext_S called");
+    /* id corresponds to context being swapped out on NS side */
+    if ((id == 0U) || (id > TFM_MAX_NS_THREAD_COUNT)) {
+        /* Invalid TZ_MemoryId_t */
+        return 0U;
+    }
+
+    index = id - 1;
+
+    if (NsClientIdList[index].ns_client_id == INVALID_CLIENT_ID) {
+        /* Non-existent client */
+        return 0U;
+    }
+
+    if (active_ns_client != NsClientIdList[index].ns_client_id) {
+        printf("TZ_StoreContext_S called for id %d, active id: %d\r\n",
+            NsClientIdList[index].ns_client_id, active_ns_client);
+        return 0U;
+    }
+
+    printf("TZ_StoreContext_S called for id %d\r\n",
+        NsClientIdList[index].ns_client_id);
+    active_ns_client = INVALID_CLIENT_ID;
+
+    return 1U;    // Success
+}
diff --git a/secure_fw/spm/spm_api.c b/secure_fw/spm/spm_api.c
index ee08e8c..7b96b11 100644
--- a/secure_fw/spm/spm_api.c
+++ b/secure_fw/spm/spm_api.c
@@ -66,6 +66,9 @@
     return SPM_INVALID_PARTITION_IDX;
 }
 
+/* FixMe: this should be in a header */
+extern void tfm_nspm_configure_clients(void);
+
 enum spm_err_t tfm_spm_db_init(void)
 {
     struct spm_partition_desc_t *part_ptr;
@@ -103,6 +106,7 @@
 #endif
 
     part_ptr->runtime_data.partition_state = SPM_PARTITION_STATE_UNINIT;
+    tfm_nspm_configure_clients();
     ++g_spm_partition_db.partition_count;
 
     /* For the TF-M core environment itself */