SPM: Involve SPM backends

Compare to the existing IPC runtime model and the upcoming SFN
runtime model, they:
- Have different component runtime initialization methods:
  thread vs thread-less.
- Have different messaging mechanisms: scheduling vs function call.
- Have different system kick-off mechanisms: scheduling vs calling
  into partition initialization routines one by one.

Create backends for addressing these differentiate. And IPC backend
is the first instance. Also fine-tunes backend related items, move
them into backend sources, such as partition list, and change the
way for binding service signals with partitions.

Change-Id: I954af30d65514a13ac9f5f86e5e718cf1c9a697c
Signed-off-by: Ken Liu <ken.liu@arm.com>
Co-authored-by: Mingyang Sun <mingyang.sun@arm.com>
diff --git a/secure_fw/spm/CMakeLists.txt b/secure_fw/spm/CMakeLists.txt
index 443279f..f118fd0 100755
--- a/secure_fw/spm/CMakeLists.txt
+++ b/secure_fw/spm/CMakeLists.txt
@@ -52,6 +52,7 @@
         $<$<BOOL:${TFM_PSA_API}>:cmsis_psa/spm_ipc.c>
         $<$<BOOL:${TFM_PSA_API}>:cmsis_psa/static_load.c>
         $<$<BOOL:${TFM_PSA_API}>:ffm/psa_api.c>
+        $<$<BOOL:${TFM_PSA_API}>:ffm/backend_ipc.c>
         $<$<BOOL:${TFM_PSA_API}>:cmsis_psa/tfm_core_svcalls_ipc.c>
         $<$<AND:$<BOOL:${TFM_PSA_API}>,$<NOT:$<BOOL:${TFM_MULTI_CORE_TOPOLOGY}>>>:cmsis_psa/tfm_nspm_ipc.c>
         $<$<BOOL:${TFM_PSA_API}>:cmsis_psa/tfm_pools.c>
diff --git a/secure_fw/spm/cmsis_psa/spm_ipc.c b/secure_fw/spm/cmsis_psa/spm_ipc.c
index 4e8960f..054ee11 100644
--- a/secure_fw/spm/cmsis_psa/spm_ipc.c
+++ b/secure_fw/spm/cmsis_psa/spm_ipc.c
@@ -30,13 +30,13 @@
 #include "tfm_pools.h"
 #include "region.h"
 #include "psa_manifest/pid.h"
+#include "ffm/backend.h"
 #include "load/partition_defs.h"
 #include "load/service_defs.h"
 #include "load/asset_defs.h"
 #include "load/spm_load_api.h"
 
 /* Partition and service runtime data list head/runtime data table */
-static struct partition_head_t partitions_listhead;
 static struct service_head_t services_listhead;
 struct service_t *stateless_services_ref_tbl[STATIC_HANDLE_NUM_LIMIT];
 
@@ -294,7 +294,7 @@
 {
     struct partition_t *p_part;
 
-    UNI_LIST_FOR_EACH(p_part, &partitions_listhead) {
+    UNI_LIST_FOR_EACH(p_part, PARTITION_LIST_ADDR) {
         if (p_part->p_ldinf->pid == partition_id) {
             return p_part;
         }
@@ -472,41 +472,6 @@
     }
 }
 
-void tfm_spm_send_event(struct service_t *service,
-                        struct tfm_msg_body_t *msg)
-{
-    struct partition_t *partition = NULL;
-    psa_signal_t signal = 0;
-
-    if (!msg || !service || !service->p_ldinf || !service->partition) {
-        tfm_core_panic();
-    }
-
-    partition = service->partition;
-    signal = service->p_ldinf->signal;
-
-    /* Add message to partition message list tail */
-    BI_LIST_INSERT_BEFORE(&partition->msg_list, &msg->msg_node);
-
-    /* Messages put. Update signals */
-    partition->signals_asserted |= signal;
-
-    if (partition->signals_waiting & signal) {
-        thrd_wake_up(
-                    &partition->waitobj,
-                    (partition->signals_asserted & partition->signals_waiting));
-        partition->signals_waiting &= ~signal;
-    }
-
-    /*
-     * If it is a NS request via RPC, it is unnecessary to block current
-     * thread.
-     */
-    if (!is_tfm_rpc_msg(msg)) {
-        thrd_wait_on(&msg->ack_evnt, CURRENT_THREAD);
-    }
-}
-
 int32_t tfm_spm_partition_get_running_partition_id(void)
 {
     struct partition_t *partition;
@@ -622,8 +587,8 @@
 uint32_t tfm_spm_init(void)
 {
     struct partition_t *partition;
-    const struct partition_load_info_t *p_ldinf;
-    void *p_param, *p_boundaries = NULL;
+    const struct partition_load_info_t *p_pldi;
+    uint32_t service_setting = 0;
 
 #ifdef TFM_FIH_PROFILE_ON
     fih_int fih_rc = FIH_FAILURE;
@@ -634,62 +599,49 @@
                   sizeof(struct tfm_conn_handle_t),
                   TFM_CONN_HANDLE_MAX_NUM);
 
-    UNI_LISI_INIT_HEAD(&partitions_listhead);
+    UNI_LISI_INIT_HEAD(PARTITION_LIST_ADDR);
     UNI_LISI_INIT_HEAD(&services_listhead);
 
     while (1) {
-        partition = load_a_partition_assuredly(&partitions_listhead);
+        partition = load_a_partition_assuredly(PARTITION_LIST_ADDR);
         if (partition == NO_MORE_PARTITION) {
             break;
         }
 
-        p_ldinf = partition->p_ldinf;
+        p_pldi = partition->p_ldinf;
 
-        if (p_ldinf->nservices) {
-            load_services_assuredly(partition, &services_listhead,
-                                    stateless_services_ref_tbl,
-                                    sizeof(stateless_services_ref_tbl));
+        if (p_pldi->nservices) {
+            service_setting = load_services_assuredly(
+                                partition,
+                                &services_listhead,
+                                stateless_services_ref_tbl,
+                                sizeof(stateless_services_ref_tbl));
         }
 
-        if (p_ldinf->nirqs) {
+        if (p_pldi->nirqs) {
             load_irqs_assuredly(partition);
         }
 
-        /* Bind the partition with plaform. */
+        /* Bind the partition with platform. */
 #if TFM_FIH_PROFILE_ON
         FIH_CALL(tfm_hal_bind_boundaries, fih_rc, partition->p_ldinf,
-                 &p_boundaries);
+                 &partition->p_boundaries);
         if (fih_not_eq(fih_rc, fih_int_encode(TFM_HAL_SUCCESS))) {
             tfm_core_panic();
         }
 #else /* TFM_FIH_PROFILE_ON */
         if (tfm_hal_bind_boundaries(partition->p_ldinf,
-                                    &p_boundaries) != TFM_HAL_SUCCESS) {
+                                    &partition->p_boundaries)
+                != TFM_HAL_SUCCESS) {
             tfm_core_panic();
         }
 #endif /* TFM_FIH_PROFILE_ON */
 
-        partition->p_boundaries = p_boundaries;
-        partition->signals_allowed |= PSA_DOORBELL;
-
-        THRD_SYNC_INIT(&partition->waitobj);
-        BI_LIST_INIT_NODE(&partition->msg_list);
-
-        THRD_INIT(&partition->thrd, &partition->ctx_ctrl,
-                  TO_THREAD_PRIORITY(PARTITION_PRIORITY(p_ldinf->flags)));
-
-        p_param = NULL;
-        if (p_ldinf->pid == TFM_SP_NON_SECURE_ID) {
-            p_param = (void *)tfm_spm_hal_get_ns_entry_point();
-        }
-
-        thrd_start(&partition->thrd,
-                   POSITION_TO_ENTRY(p_ldinf->entry, thrd_fn_t), p_param,
-                   LOAD_ALLOCED_STACK_ADDR(p_ldinf),
-                   LOAD_ALLOCED_STACK_ADDR(p_ldinf) + p_ldinf->stack_size);
+        /* TODO: Replace this 'BACKEND_IPC' after SFN get involved. */
+        backend_instance.comp_init_assuredly(partition, service_setting);
     }
 
-    return thrd_start_scheduler(&CURRENT_THREAD);
+    return backend_instance.system_run();
 }
 
 /*
diff --git a/secure_fw/spm/cmsis_psa/spm_ipc.h b/secure_fw/spm/cmsis_psa/spm_ipc.h
index fd92f3e..2de04a9 100644
--- a/secure_fw/spm/cmsis_psa/spm_ipc.h
+++ b/secure_fw/spm/cmsis_psa/spm_ipc.h
@@ -287,19 +287,6 @@
                       psa_outvec *caller_outvec);
 
 /**
- * \brief                   Send message and wake up the SP who is waiting on
- *                          message queue, block the current thread and
- *                          scheduler triggered
- *
- * \param[in] service       Target service context pointer, which can be
- *                          obtained by partition management functions
- * \param[in] msg           message created by tfm_spm_create_msg()
- *                          \ref tfm_msg_body_t structures
- */
-void tfm_spm_send_event(struct service_t *service,
-                        struct tfm_msg_body_t *msg);
-
-/**
  * \brief                   Check the client version according to
  *                          version policy
  *
diff --git a/secure_fw/spm/cmsis_psa/static_load.c b/secure_fw/spm/cmsis_psa/static_load.c
index acd20c5..c319c74 100644
--- a/secure_fw/spm/cmsis_psa/static_load.c
+++ b/secure_fw/spm/cmsis_psa/static_load.c
@@ -98,10 +98,6 @@
         tfm_core_panic();
     }
 
-    if (!(p_ptldinf->flags & PARTITION_MODEL_IPC)) {
-        tfm_core_panic();
-    }
-
     if (p_ptldinf->pid < 0) {
         /* 0 is the internal NS Agent, besides the normal positive PIDs */
         tfm_core_panic();
@@ -117,12 +113,12 @@
     return partition;
 }
 
-void load_services_assuredly(struct partition_t *p_partition,
-                             struct service_head_t *services_listhead,
-                             struct service_t **stateless_services_ref_tbl,
-                             size_t ref_tbl_size)
+uint32_t load_services_assuredly(struct partition_t *p_partition,
+                                 struct service_head_t *services_listhead,
+                                 struct service_t **stateless_services_ref_tbl,
+                                 size_t ref_tbl_size)
 {
-    uint32_t i, serv_ldflags, hidx;
+    uint32_t i, serv_ldflags, hidx, service_setting = 0;
     struct service_t *services;
     const struct partition_load_info_t *p_ptldinf;
     const struct service_load_info_t *p_servldinf;
@@ -140,11 +136,14 @@
      */
     services = tfm_allocate_service_assuredly(p_ptldinf->nservices);
     for (i = 0; i < p_ptldinf->nservices && services; i++) {
-        p_partition->signals_allowed |= p_servldinf[i].signal;
         services[i].p_ldinf = &p_servldinf[i];
         services[i].partition = p_partition;
         services[i].next = NULL;
 
+        if (p_ptldinf->flags & PARTITION_MODEL_IPC) {
+            service_setting |= p_servldinf[i].signal;
+        }
+
         BI_LIST_INIT_NODE(&services[i].handle_list);
 
         /* Populate the stateless service reference table */
@@ -168,6 +167,8 @@
 
         UNI_LIST_INSERT_AFTER(services_listhead, &services[i]);
     }
+
+    return service_setting;
 }
 
 void load_irqs_assuredly(struct partition_t *p_partition)
diff --git a/secure_fw/spm/ffm/backend_ipc.c b/secure_fw/spm/ffm/backend_ipc.c
new file mode 100644
index 0000000..4b2f8a2
--- /dev/null
+++ b/secure_fw/spm/ffm/backend_ipc.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdint.h>
+#include "spm_ipc.h"
+#include "tfm_rpc.h"
+#include "tfm_spm_hal.h" /* To be checked */
+#include "ffm/backend.h"
+#include "load/partition_defs.h"
+#include "load/service_defs.h"
+#include "load/spm_load_api.h"
+#include "psa/error.h"
+
+/* Declare the global component list */
+struct partition_head_t partition_listhead;
+
+/*
+ * Send message and wake up the SP who is waiting on message queue, block the
+ * current thread and triggere scheduler.
+ */
+static psa_status_t ipc_messaging(struct service_t *service,
+                                  struct tfm_msg_body_t *msg)
+{
+    struct partition_t *p_owner = NULL;
+    psa_signal_t signal = 0;
+
+    if (!msg || !service || !service->p_ldinf || !service->partition) {
+        tfm_core_panic();
+    }
+
+    p_owner = service->partition;
+    signal = service->p_ldinf->signal;
+
+    /* Add message to partition message list tail */
+    BI_LIST_INSERT_BEFORE(&p_owner->msg_list, &msg->msg_node);
+
+    /* Messages put. Update signals */
+    p_owner->signals_asserted |= signal;
+
+    if (p_owner->signals_waiting & signal) {
+        thrd_wake_up(&p_owner->waitobj,
+                     (p_owner->signals_asserted & p_owner->signals_waiting));
+        p_owner->signals_waiting &= ~signal;
+    }
+
+    /*
+     * If it is a NS request via RPC, it is unnecessary to block current
+     * thread.
+     */
+
+    if (!is_tfm_rpc_msg(msg)) {
+        thrd_wait_on(&msg->ack_evnt, CURRENT_THREAD);
+    }
+
+    return PSA_SUCCESS;
+}
+
+/* Parameters are treated as assuredly */
+static void ipc_comp_init_assuredly(struct partition_t *p_pt,
+                                    uint32_t service_setting)
+{
+    const struct partition_load_info_t *p_pldi = p_pt->p_ldinf;
+    void *p_param = NULL;
+
+    p_pt->signals_allowed |= PSA_DOORBELL | service_setting;
+
+    THRD_SYNC_INIT(&p_pt->waitobj);
+    BI_LIST_INIT_NODE(&p_pt->msg_list);
+
+    THRD_INIT(&p_pt->thrd, &p_pt->ctx_ctrl,
+              TO_THREAD_PRIORITY(PARTITION_PRIORITY(p_pldi->flags)));
+
+    if (p_pldi->pid == TFM_SP_NON_SECURE_ID) {
+        p_param = (void *)tfm_spm_hal_get_ns_entry_point();
+    }
+
+    thrd_start(&p_pt->thrd,
+               POSITION_TO_ENTRY(p_pldi->entry, thrd_fn_t), p_param,
+               LOAD_ALLOCED_STACK_ADDR(p_pldi),
+               LOAD_ALLOCED_STACK_ADDR(p_pldi) + p_pldi->stack_size);
+}
+
+static uint32_t ipc_system_run(void)
+{
+    return thrd_start_scheduler(&CURRENT_THREAD);
+}
+
+const struct backend_ops_t backend_instance = {
+    .comp_init_assuredly = ipc_comp_init_assuredly,
+    .system_run          = ipc_system_run,
+    .messaging           = ipc_messaging,
+};
diff --git a/secure_fw/spm/ffm/psa_api.c b/secure_fw/spm/ffm/psa_api.c
index 6e35c57..b38f85c 100644
--- a/secure_fw/spm/ffm/psa_api.c
+++ b/secure_fw/spm/ffm/psa_api.c
@@ -17,6 +17,7 @@
 #include "load/interrupt_defs.h"
 #include "psa_api.h"
 #include "utilities.h"
+#include "ffm/backend.h"
 #include "ffm/spm_error_base.h"
 #include "tfm_rpc.h"
 #include "tfm_spm_hal.h"
@@ -27,7 +28,6 @@
 #define GET_STATELESS_SERVICE(index)    (stateless_services_ref_tbl[index])
 extern struct service_t *stateless_services_ref_tbl[];
 
-
 uint32_t tfm_spm_get_lifecycle_state(void)
 {
     /*
@@ -130,13 +130,8 @@
     tfm_spm_fill_msg(msg, service, handle, PSA_IPC_CONNECT,
                      client_id, NULL, 0, NULL, 0, NULL);
 
-    /*
-     * Send message and wake up the SP who is waiting on message queue,
-     * and scheduler triggered
-     */
-    tfm_spm_send_event(service, msg);
 
-    return PSA_SUCCESS;
+    return backend_instance.messaging(service, msg);
 }
 
 psa_status_t tfm_spm_client_psa_call(psa_handle_t handle,
@@ -323,13 +318,7 @@
     tfm_spm_fill_msg(msg, service, handle, type, client_id,
                      invecs, in_num, outvecs, out_num, outptr);
 
-    /*
-     * Send message and wake up the SP who is waiting on message queue,
-     * and scheduler triggered
-     */
-    tfm_spm_send_event(service, msg);
-
-    return PSA_SUCCESS;
+    return backend_instance.messaging(service, msg);
 }
 
 void tfm_spm_client_psa_close(psa_handle_t handle)
@@ -385,11 +374,7 @@
     tfm_spm_fill_msg(msg, service, handle, PSA_IPC_DISCONNECT, client_id,
                      NULL, 0, NULL, 0, NULL);
 
-    /*
-     * Send message and wake up the SP who is waiting on message queue,
-     * and scheduler triggered
-     */
-    tfm_spm_send_event(service, msg);
+    (void)backend_instance.messaging(service, msg);
 }
 
 /* PSA Partition API function body */
diff --git a/secure_fw/spm/include/ffm/backend.h b/secure_fw/spm/include/ffm/backend.h
new file mode 100644
index 0000000..c53c35c
--- /dev/null
+++ b/secure_fw/spm/include/ffm/backend.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __BACKEND_H__
+#define __BACKEND_H__
+
+#include <stdint.h>
+#include "psa/error.h"
+#include "spm_ipc.h"
+#include "load/spm_load_api.h"
+
+/* BASIC TYPE DEFINITIONS */
+
+struct backend_ops_t {
+    /*
+     * Runtime model-specific component initialization routine. This
+     * is an `assuredly` function, would panic if any error occurred.
+     */
+    void (*comp_init_assuredly)(struct partition_t *p_pt,
+                                uint32_t service_setting);
+
+    /*
+     * Runtime model-specific kick-off method for the whole system.
+     * Returns a hardware-specific control value, which is transparent
+     * to SPM common logic.
+     */
+    uint32_t (*system_run)(void);
+
+    /* Runtime model-specific message handling mechanism. */
+    psa_status_t (*messaging)(struct service_t *p_serv,
+                              struct tfm_msg_body_t *p_msg);
+
+};
+
+/* RUNTIME MODEL BACKENDS DECLARATION */
+
+/* IPC backend */
+extern const struct backend_ops_t backend_instance;
+
+/* The component list, and a MACRO indicate this is not a common global. */
+extern struct partition_head_t partition_listhead;
+#define PARTITION_LIST_ADDR (&partition_listhead)
+
+#endif /* __BACKEND_H__ */
diff --git a/secure_fw/spm/include/load/spm_load_api.h b/secure_fw/spm/include/load/spm_load_api.h
index d8fbc16..a402d9a 100644
--- a/secure_fw/spm/include/load/spm_load_api.h
+++ b/secure_fw/spm/include/load/spm_load_api.h
@@ -68,11 +68,13 @@
  * contains.
  * As an 'assuredly' function, errors simply panic the system and never
  * return.
+ * This function returns the service signal set in a 32 bit number. Return
+ * ZERO if services are not represented by signals.
  */
-void load_services_assuredly(struct partition_t *p_partition,
-                             struct service_head_t *services_listhead,
-                             struct service_t **stateless_services_ref_tbl,
-                             size_t ref_tbl_size);
+uint32_t load_services_assuredly(struct partition_t *p_partition,
+                                 struct service_head_t *services_listhead,
+                                 struct service_t **stateless_services_ref_tbl,
+                                 size_t ref_tbl_size);
 
 /*
  * Append IRQ signals to Partition signals.