SPM: Turn bi-directional list into uni-directional

Uni-list works well enough for linking items and easier to apply
synchronization mechanisms for it as it has only one member to
be maintained when inserting/removing.

The existing usage defines a a fixed header node to make the general
uni-directional list work easier. This fixed header node does not
need to be the same type as the list item, just need to have the
same for link indicator. With this fixed header, there is no need
to keep checking if the head is going to be updated when updating
the members of the list.

For example, a 'partition_t' as the head node and it contains the
link indicator 'p_messages' to link all pending messages. So does
the list usage in handle pool logic.

And it is still applicable to reference these uni-directional list
functions for the general list usages without a fixed head member,
just choose proper functions to match the purpose.

Change-Id: I38a04f59e5fbf799a449a38fa09ea103b554c6bd
Signed-off-by: Ken Liu <Ken.Liu@arm.com>
diff --git a/secure_fw/spm/cmsis_psa/psa_interface_sfn.c b/secure_fw/spm/cmsis_psa/psa_interface_sfn.c
index 07d5d80..0b214f1 100644
--- a/secure_fw/spm/cmsis_psa/psa_interface_sfn.c
+++ b/secure_fw/spm/cmsis_psa/psa_interface_sfn.c
@@ -34,7 +34,8 @@
 
     p_target = GET_CURRENT_COMPONENT();
     if (p_client != p_target) {
-        stat = tfm_spm_partition_psa_reply(p_target->p_msg->msg.handle, stat);
+        stat = tfm_spm_partition_psa_reply(p_target->p_messages->msg.handle,
+                                           stat);
     }
 
     spm_handle_programmer_errors(stat);
@@ -84,7 +85,8 @@
 
     p_target = GET_CURRENT_COMPONENT();
     if (p_client != p_target) {
-        stat = tfm_spm_partition_psa_reply(p_target->p_msg->msg.handle, stat);
+        stat = tfm_spm_partition_psa_reply(p_target->p_messages->msg.handle,
+                                           stat);
     }
 
     spm_handle_programmer_errors(stat);
@@ -102,7 +104,7 @@
 
     p_target = GET_CURRENT_COMPONENT();
     if (p_client != p_target) {
-        stat = tfm_spm_partition_psa_reply(p_target->p_msg->msg.handle,
+        stat = tfm_spm_partition_psa_reply(p_target->p_messages->msg.handle,
                                            PSA_SUCCESS);
     }
 
diff --git a/secure_fw/spm/cmsis_psa/spm_ipc.c b/secure_fw/spm/cmsis_psa/spm_ipc.c
index 6df852b..0d8b087 100755
--- a/secure_fw/spm/cmsis_psa/spm_ipc.c
+++ b/secure_fw/spm/cmsis_psa/spm_ipc.c
@@ -234,50 +234,46 @@
 
 /* Partition management functions */
 
-struct tfm_msg_body_t *tfm_spm_get_msg_by_signal(struct partition_t *partition,
-                                                 psa_signal_t signal)
+struct tfm_msg_body_t *spm_get_msg_with_signal(struct partition_t *p_ptn,
+                                               psa_signal_t signal)
 {
-    struct bi_list_node_t *node, *head;
-    struct tfm_msg_body_t *tmp_msg, *msg = NULL;
+    struct tfm_msg_body_t *p_msg_iter;
+    struct tfm_msg_body_t **pr_msg_iter, **last_found_msg_holder = NULL;
     struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
+    uint32_t nr_found_msgs = 0;
 
-    TFM_CORE_ASSERT(partition);
-
-    head = &partition->msg_list;
-
-    if (BI_LIST_IS_EMPTY(head)) {
-        return NULL;
-    }
-
-    /*
-     * There may be multiple messages for this RoT Service signal, do not clear
-     * partition mask until no remaining message. Search may be optimized.
-     */
     CRITICAL_SECTION_ENTER(cs_assert);
-    BI_LIST_FOR_EACH(node, head) {
-        tmp_msg = TO_CONTAINER(node, struct tfm_msg_body_t, msg_node);
-        if (tmp_msg->service->p_ldinf->signal == signal && msg) {
-            CRITICAL_SECTION_LEAVE(cs_assert);
-            return msg;
-        } else if (tmp_msg->service->p_ldinf->signal == signal) {
-            msg = tmp_msg;
-            BI_LIST_REMOVE_NODE(node);
+
+    /* Return the last found message which applies a FIFO mechanism. */
+    UNI_LIST_FOREACH_NODE_PNODE(pr_msg_iter, p_msg_iter, p_ptn, p_messages) {
+        if (p_msg_iter->service->p_ldinf->signal == signal) {
+            last_found_msg_holder = pr_msg_iter;
+            nr_found_msgs++;
         }
     }
 
-    partition->signals_asserted &= ~signal;
+    if (last_found_msg_holder) {
+        p_msg_iter = *last_found_msg_holder;
+        UNI_LIST_REMOVE_NODE_BY_PNODE(last_found_msg_holder, p_messages);
+
+        /* Clear the signal bit since the only message is removed. */
+        if (nr_found_msgs == 1) {
+            p_ptn->signals_asserted &= ~signal;
+        }
+    }
+
     CRITICAL_SECTION_LEAVE(cs_assert);
 
-    return msg;
+    return p_msg_iter;
 }
 
 struct service_t *tfm_spm_get_service_by_sid(uint32_t sid)
 {
     struct service_t *p_prev, *p_curr;
 
-    UNI_LIST_FOR_EACH_PREV(p_prev, p_curr, &services_listhead) {
+    UNI_LIST_FOREACH_NODE_PREV(p_prev, p_curr, &services_listhead, next) {
         if (p_curr->p_ldinf->sid == sid) {
-            UNI_LIST_MOVE_AFTER(&services_listhead, p_prev, p_curr);
+            UNI_LIST_MOVE_AFTER(&services_listhead, p_prev, p_curr, next);
             return p_curr;
         }
     }
@@ -298,7 +294,7 @@
 {
     struct partition_t *p_part;
 
-    UNI_LIST_FOR_EACH(p_part, PARTITION_LIST_ADDR) {
+    UNI_LIST_FOREACH(p_part, PARTITION_LIST_ADDR, next) {
         if (p_part->p_ldinf->pid == partition_id) {
             return p_part;
         }
@@ -565,8 +561,8 @@
                   sizeof(struct tfm_conn_handle_t),
                   CONFIG_TFM_CONN_HANDLE_MAX_NUM);
 
-    UNI_LISI_INIT_HEAD(PARTITION_LIST_ADDR);
-    UNI_LISI_INIT_HEAD(&services_listhead);
+    UNI_LISI_INIT_NODE(PARTITION_LIST_ADDR, next);
+    UNI_LISI_INIT_NODE(&services_listhead, next);
 
     /* Init the nonsecure context. */
     tfm_nspm_ctx_init();
diff --git a/secure_fw/spm/cmsis_psa/spm_ipc.h b/secure_fw/spm/cmsis_psa/spm_ipc.h
index b2506a1..516217c 100644
--- a/secure_fw/spm/cmsis_psa/spm_ipc.h
+++ b/secure_fw/spm/cmsis_psa/spm_ipc.h
@@ -103,7 +103,7 @@
 #if PSA_FRAMEWORK_HAS_MM_IOVEC
     uint32_t iovec_status;             /* MM-IOVEC status                */
 #endif
-    struct bi_list_node_t msg_node;    /* For list operators             */
+    struct tfm_msg_body_t *p_messages; /* Message(s) link                */
 };
 
 /* Partition runtime type */
@@ -121,10 +121,7 @@
         struct thread_t                thrd;            /* IPC model */
         uint32_t                       state;           /* SFN model */
     };
-    union {
-        struct bi_list_node_t          msg_list;        /* IPC model */
-        struct tfm_msg_body_t          *p_msg;          /* SFN model */
-    };
+    struct tfm_msg_body_t              *p_messages;
     struct partition_t                 *next;
 };
 
@@ -209,21 +206,19 @@
 
 /******************** Partition management functions *************************/
 
-/**
- * \brief                   Get the msg context by signal.
+/*
+ * Lookup and fetch the last spotted message in the partition messages
+ * by the given signal. Only ONE signal bit can be accepted in 'signal',
+ * multiple bits lead to 'no matched message found to that signal'.
  *
- * \param[in] partition     Partition context pointer
- *                          \ref partition_t structures
- * \param[in] signal        Signal associated with inputs to the Secure
- *                          Partition, \ref psa_signal_t
- *
- * \retval NULL             Failed
- * \retval "Not NULL"       Target service context pointer,
- *                          \ref tfm_msg_body_t structures
+ * Returns NULL if no message matched with the given signal.
+ * Returns an internal message instance if spotted, the instance
+ * is moved out of partition messages. Partition available signals
+ * also get updated based on the count of message with given signal
+ * still in the partition messages.
  */
-struct tfm_msg_body_t *tfm_spm_get_msg_by_signal(struct partition_t *partition,
-                                                 psa_signal_t signal);
-
+struct tfm_msg_body_t *spm_get_msg_with_signal(struct partition_t *p_ptn,
+                                               psa_signal_t signal);
 
 /**
  * \brief                   Get partition by Partition ID.
diff --git a/secure_fw/spm/cmsis_psa/static_load.c b/secure_fw/spm/cmsis_psa/static_load.c
index 16e797f..c6baa64 100644
--- a/secure_fw/spm/cmsis_psa/static_load.c
+++ b/secure_fw/spm/cmsis_psa/static_load.c
@@ -108,7 +108,7 @@
 
     ldinf_sa += LOAD_INFSZ_BYTES(p_ptldinf);
 
-    UNI_LIST_INSERT_AFTER(head, partition);
+    UNI_LIST_INSERT_AFTER(head, partition, next);
 
     return partition;
 }
@@ -163,7 +163,7 @@
             stateless_services_ref_tbl[hidx] = &services[i];
         }
 
-        UNI_LIST_INSERT_AFTER(services_listhead, &services[i]);
+        UNI_LIST_INSERT_AFTER(services_listhead, &services[i], next);
     }
 
     return service_setting;
diff --git a/secure_fw/spm/cmsis_psa/tfm_pools.c b/secure_fw/spm/cmsis_psa/tfm_pools.c
index 0caaf6d..fe98426 100644
--- a/secure_fw/spm/cmsis_psa/tfm_pools.c
+++ b/secure_fw/spm/cmsis_psa/tfm_pools.c
@@ -38,11 +38,11 @@
     spm_memset(pool, 0, poolsz);
 
     /* Chain pool chunks */
-    BI_LIST_INIT_NODE(&pool->chunks_list);
+    UNI_LISI_INIT_NODE(pool, next);
 
     pchunk = (struct tfm_pool_chunk_t *)pool->chunks;
     for (i = 0; i < num; i++) {
-        BI_LIST_INSERT_BEFORE(&pool->chunks_list, &pchunk->list);
+        UNI_LIST_INSERT_AFTER(pool, pchunk, next);
         pchunk = (struct tfm_pool_chunk_t *)&pchunk->data[chunksz];
     }
 
@@ -55,24 +55,20 @@
 
 void *tfm_pool_alloc(struct tfm_pool_instance_t *pool)
 {
-    struct bi_list_node_t *node;
-    struct tfm_pool_chunk_t *pchunk;
+    struct tfm_pool_chunk_t *node;
 
     if (!pool) {
         return NULL;
     }
 
-    if (BI_LIST_IS_EMPTY(&pool->chunks_list)) {
+    if (UNI_LIST_IS_EMPTY(pool, next)) {
         return NULL;
     }
 
-    node = BI_LIST_NEXT_NODE(&pool->chunks_list);
-    pchunk = TO_CONTAINER(node, struct tfm_pool_chunk_t, list);
+    node = UNI_LIST_NEXT_NODE(pool, next);
+    UNI_LIST_REMOVE_NODE(pool, node, next);
 
-    /* Remove node from list node, it will be added when pool free */
-    BI_LIST_REMOVE_NODE(node);
-
-    return &pchunk->data;
+    return &(((struct tfm_pool_chunk_t *)node)->data);
 }
 
 void tfm_pool_free(struct tfm_pool_instance_t *pool, void *ptr)
@@ -80,7 +76,8 @@
     struct tfm_pool_chunk_t *pchunk;
 
     pchunk = TO_CONTAINER(ptr, struct tfm_pool_chunk_t, data);
-    BI_LIST_INSERT_BEFORE(&pool->chunks_list, &pchunk->list);
+
+    UNI_LIST_INSERT_AFTER(pool, pchunk, next);
 }
 
 bool is_valid_chunk_data_in_pool(struct tfm_pool_instance_t *pool,
diff --git a/secure_fw/spm/cmsis_psa/tfm_pools.h b/secure_fw/spm/cmsis_psa/tfm_pools.h
index fe70ca3..650c2a0 100644
--- a/secure_fw/spm/cmsis_psa/tfm_pools.h
+++ b/secure_fw/spm/cmsis_psa/tfm_pools.h
@@ -16,15 +16,15 @@
  *  [ Pool Instance ] + N * [ Pool Chunks ]
  */
 struct tfm_pool_chunk_t {
-    struct bi_list_node_t list;         /* Chunk list                     */
-    uint8_t data[];                     /* Data indicator                 */
+    struct tfm_pool_chunk_t *next;        /* Chunk list                     */
+    uint8_t data[];                       /* Data indicator                 */
 };
 
 struct tfm_pool_instance_t {
-    size_t chunksz;                     /* Chunks size of pool member     */
-    size_t chunk_count;                 /* A number of chunks in the pool */
-    struct bi_list_node_t chunks_list;  /* Chunk list head in pool        */
-    uint8_t chunks[];                   /* Data indicator                 */
+    struct tfm_pool_chunk_t *next;        /* Point to the first free node   */
+    size_t chunksz;                       /* Chunks size of pool member     */
+    size_t chunk_count;                   /* A number of chunks in the pool */
+    uint8_t chunks[];                     /* Data indicator                 */
 };
 
 /*
diff --git a/secure_fw/spm/ffm/backend_ipc.c b/secure_fw/spm/ffm/backend_ipc.c
index 0cfbcb5..48c7899 100644
--- a/secure_fw/spm/ffm/backend_ipc.c
+++ b/secure_fw/spm/ffm/backend_ipc.c
@@ -60,8 +60,8 @@
     signal = service->p_ldinf->signal;
 
     CRITICAL_SECTION_ENTER(cs_assert);
-    /* Add message to partition message list tail */
-    BI_LIST_INSERT_BEFORE(&p_owner->msg_list, &msg->msg_node);
+
+    UNI_LIST_INSERT_AFTER(p_owner, msg, p_messages);
 
     /* Messages put. Update signals */
     p_owner->signals_asserted |= signal;
@@ -110,7 +110,7 @@
     p_pt->signals_allowed |= PSA_DOORBELL | service_setting;
 
     THRD_SYNC_INIT(&p_pt->waitobj);
-    BI_LIST_INIT_NODE(&p_pt->msg_list);
+    UNI_LISI_INIT_NODE(p_pt, p_messages);
 
     THRD_INIT(&p_pt->thrd, &p_pt->ctx_ctrl,
               TO_THREAD_PRIORITY(PARTITION_PRIORITY(p_pldi->flags)));
diff --git a/secure_fw/spm/ffm/backend_sfn.c b/secure_fw/spm/ffm/backend_sfn.c
index 3904e0a..b84c307 100644
--- a/secure_fw/spm/ffm/backend_sfn.c
+++ b/secure_fw/spm/ffm/backend_sfn.c
@@ -47,7 +47,7 @@
 
     msg->sfn_magic = TFM_MSG_MAGIC_SFN;
     p_target = service->partition;
-    p_target->p_msg = msg;
+    p_target->p_messages = msg;
 
     SET_CURRENT_COMPONENT(p_target);
 
@@ -89,7 +89,7 @@
 {
     const struct partition_load_info_t *p_pldi = p_pt->p_ldinf;
 
-    p_pt->p_msg = NULL;
+    p_pt->p_messages = NULL;
     p_pt->state = SFN_PARTITION_STATE_NOT_INITED;
 
     THRD_SYNC_INIT(&p_pt->waitobj);
@@ -124,7 +124,7 @@
 
     p_curr = GET_CURRENT_COMPONENT();
     /* Call partition initialization routine one by one. */
-    UNI_LIST_FOR_EACH(p_part, PARTITION_LIST_ADDR) {
+    UNI_LIST_FOREACH(p_part, PARTITION_LIST_ADDR, next) {
         if (IS_PARTITION_IPC_MODEL(p_part->p_ldinf)) {
             continue;
         }
diff --git a/secure_fw/spm/ffm/psa_api.c b/secure_fw/spm/ffm/psa_api.c
index ccc0838..1588263 100644
--- a/secure_fw/spm/ffm/psa_api.c
+++ b/secure_fw/spm/ffm/psa_api.c
@@ -526,7 +526,7 @@
      * Get message by signal from partition. It is a fatal error if getting
      * failed, which means the input signal is not correspond to an RoT service.
      */
-    tmp_msg = tfm_spm_get_msg_by_signal(partition, signal);
+    tmp_msg = spm_get_msg_with_signal(partition, signal);
     if (!tmp_msg) {
         return PSA_ERROR_DOES_NOT_EXIST;
     }
diff --git a/secure_fw/spm/include/lists.h b/secure_fw/spm/include/lists.h
index 1a11dee..68a53fa 100644
--- a/secure_fw/spm/include/lists.h
+++ b/secure_fw/spm/include/lists.h
@@ -45,52 +45,66 @@
 /* Is the head empty? */
 #define BI_LIST_IS_EMPTY(head)      ((head)->bnext == (head))
 
-/* The node's next node */
+/* The node's next node. */
 #define BI_LIST_NEXT_NODE(node)     ((node)->bnext)
 
-/* Go through each node of a list */
+/* Go through each node of a list. */
 #define BI_LIST_FOR_EACH(node, head)              \
     for (node = (head)->bnext; node != head; node = (node)->bnext)
 
 /********* Uni-directional list operations ********/
-/*
- * To use these single linked list operations, a head node must have been
- * defined already, and the "next" pointer initialized to "NULL". Like:
- * struct head_t {
- *      uint32_t data;
- *      User_Type *next;
- * } head;
- */
-
-/* Initialize the head node */
-#define UNI_LISI_INIT_HEAD(head) do {             \
-    if ((head) != NULL) {                         \
-        (head)->next = NULL;                      \
-    }                                             \
+/* Initialize the head node. */
+#define UNI_LISI_INIT_NODE(head, link) do {             \
+    if ((head) != NULL) {                               \
+        (head)->link = NULL;                            \
+    }                                                   \
 } while (0)
 
-/* Insert a node after current node */
-#define UNI_LIST_INSERT_AFTER(curr, node) do {    \
-    (node)->next = (curr)->next;                  \
-    (curr)->next = node;                          \
+/* Insert a new node after given position node. */
+#define UNI_LIST_INSERT_AFTER(posi, node, link) do {    \
+    (node)->link = (posi)->link;                        \
+    (posi)->link = node;                                \
 } while (0)
 
-/* Move a node after posi node */
-#define UNI_LIST_MOVE_AFTER(posi, prev, node) do {\
-    if (prev != NULL) {                           \
-        (prev)->next = (node)->next;              \
-        (node)->next = (posi)->next;              \
-        (posi)->next = node;                      \
-    }                                             \
+/* Is list empty? */
+#define UNI_LIST_IS_EMPTY(node, link)                   \
+    ((node == NULL) || ((node)->link == NULL))
+
+/* Pick the next node. */
+#define UNI_LIST_NEXT_NODE(node, link) ((node)->link)
+
+/* Remove one node from the list. */
+#define UNI_LIST_REMOVE_NODE(prev, node, link) do {     \
+    (prev)->link = (node)->link;                        \
+    (node)->link = NULL;                                \
 } while (0)
 
-/* Go through each node of a list */
-#define UNI_LIST_FOR_EACH(node, head)             \
-    for (node = (head)->next; node != NULL; node = (node)->next)
+/* Remove node by its pointer ('pointer = &prev_of_node->link'). */
+#define UNI_LIST_REMOVE_NODE_BY_PNODE(pnode, link)      \
+    *(pnode) = (*(pnode))->link
 
-/* Go through each node of a list with prev node */
-#define UNI_LIST_FOR_EACH_PREV(prev, node, head)  \
-    for (prev = NULL, node = (head)->next;        \
-                 node != NULL; prev = node, node = (prev)->next)
+/* Move a node after posi node. */
+#define UNI_LIST_MOVE_AFTER(posi, prev, node, link) do {\
+    if (prev != NULL) {                                 \
+        (prev)->link = (node)->link;                    \
+        (node)->link = (posi)->link;                    \
+        (posi)->link = node;                            \
+    }                                                   \
+} while (0)
+
+/* Go through each node of a list. */
+#define UNI_LIST_FOREACH(node, head, link)              \
+    for (node = (head)->link; node != NULL; node = (node)->link)
+
+/* Go through each node of a list with prev node recorded. */
+#define UNI_LIST_FOREACH_NODE_PREV(prev, node, head, link)   \
+    for (prev = NULL, node = (head)->link;                   \
+                 node != NULL; prev = node, node = (prev)->link)
+
+/* Go through each node of a list with recording node and its holder. */
+#define UNI_LIST_FOREACH_NODE_PNODE(pnode, node, head, link) \
+    for (pnode = &(head)->link, node = (head)->link;         \
+         node != NULL;                                       \
+         pnode = &(node)->link, node = (node)->link)
 
 #endif /* __LISTS_H__ */