1 /*
2  * Copyright (c) 2021-2024, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 #include <stdint.h>
8 #include <stdbool.h>
9 #include "tfm_hal_device_header.h"
10 #include "tfm_ns_ctx.h"
11 #include "tfm_nspm.h"
12 
13 /*
14  * NS context. Initialized to 0.
15  * All contexts are not used as the reference counter is 0 in initialization.
16  */
17 static struct tfm_ns_ctx_t ns_ctx_data[TFM_NS_CONTEXT_MAX] = {0};
18 
19 /* Current active NS context index. Default is invalid index */
20 static uint8_t active_ns_ctx_index = TFM_NS_CONTEXT_MAX;
21 
init_ns_ctx(void)22 bool init_ns_ctx(void)
23 {
24     uint32_t i;
25 
26     for (i = 0; i < TFM_NS_CONTEXT_MAX; i++) {
27         /* Only need to ensure the reference counter is 0 */
28         ns_ctx_data[i].ref_cnt = 0;
29     }
30 
31     active_ns_ctx_index = TFM_NS_CONTEXT_MAX;
32     return true;
33 }
34 
acquire_ns_ctx(uint8_t gid,uint8_t * idx)35 bool acquire_ns_ctx(uint8_t gid, uint8_t *idx)
36 {
37     /* Only support one context slot for now */
38     uint32_t i;
39     uint32_t empty_ctx_idx = TFM_NS_CONTEXT_MAX; /* default is invalid */
40 
41     __disable_irq();
42     /*
43      * Go through all context slots to get the context for the given group ID.
44      * It will not take a long time as the context number should be limited.
45      */
46     for (i = 0; i < TFM_NS_CONTEXT_MAX; i++) {
47         if (ns_ctx_data[i].ref_cnt > 0) {  /* This context has been taken */
48             if (ns_ctx_data[i].gid == gid) {
49                 /*
50                  * Found the context associated with the input group ID.
51                  * Check if the thread number reached the limit.
52                  */
53                 if (ns_ctx_data[i].ref_cnt < TFM_NS_CONTEXT_MAX_TID) {
54                     /* Reuse this context and increase the reference number */
55                     ns_ctx_data[i].ref_cnt++;
56                     *idx = i;
57                     __enable_irq();
58                     return true;
59                 } else {
60                     /* No more thread for this group */
61                     __enable_irq();
62                     return false;
63                 }
64             }
65         } else {    /* Found an empty context slot */
66             /* Save the first unused context index and continue */
67             if (empty_ctx_idx == TFM_NS_CONTEXT_MAX) {
68                 empty_ctx_idx = i;
69             }
70             /*
71              * Still need to go through the remaining slots to check if there
72              * is an existing context assigned to the given group ID.
73              */
74         }
75     }
76 
77     /* No existing context for the group ID, use the first free context */
78     if (empty_ctx_idx < TFM_NS_CONTEXT_MAX) {
79         ns_ctx_data[empty_ctx_idx].ref_cnt++;
80         ns_ctx_data[empty_ctx_idx].gid = gid;
81         *idx = empty_ctx_idx;
82         __enable_irq();
83         return true;
84     } else {
85         __enable_irq();
86         return false;   /* No available context */
87     }
88 }
89 
release_ns_ctx(uint8_t gid,uint8_t tid,uint8_t idx)90 bool release_ns_ctx(uint8_t gid, uint8_t tid, uint8_t idx)
91 {
92     /* Check if the index is in range */
93     if (idx >= TFM_NS_CONTEXT_MAX) {
94         return false;
95     }
96 
97     __disable_irq();
98 
99     /* Check if the context belongs to that group  */
100     if (ns_ctx_data[idx].gid != gid) {
101         __enable_irq();
102         return false;
103     }
104 
105     /*
106      * If it is to release the current active context, then set active context
107      * to invalid.
108      * Otherwise, just de-reference the context
109      */
110     if (idx == active_ns_ctx_index) {
111         if (ns_ctx_data[idx].tid == tid) {
112             /* Release the currrent active thread */
113             if (ns_ctx_data[idx].ref_cnt > 0) {
114                 ns_ctx_data[idx].ref_cnt--;
115             }
116             active_ns_ctx_index = TFM_NS_CONTEXT_MAX;
117         } else {
118             /*
119              * Release for another thread in the active context
120              * As there's an active thread ongoing, so the ref_counter should
121              * be at least 1 after the release.
122              */
123             if (ns_ctx_data[idx].ref_cnt > 1) {
124                 ns_ctx_data[idx].ref_cnt--;
125             }
126         }
127     } else {
128         /* Release in the non-active context */
129         if (ns_ctx_data[idx].ref_cnt > 0) {
130             ns_ctx_data[idx].ref_cnt--;
131         }
132     }
133 
134     __enable_irq();
135     return true;
136 }
137 
load_ns_ctx(uint8_t gid,uint8_t tid,int32_t nsid,uint8_t idx)138 bool load_ns_ctx(uint8_t gid, uint8_t tid, int32_t nsid, uint8_t idx)
139 {
140     /* Check if the index is in range */
141     if (idx >= TFM_NS_CONTEXT_MAX) {
142         return false;
143     }
144 
145     __disable_irq();
146 
147     /* Check group ID and reference counter */
148     if ((ns_ctx_data[idx].gid != gid) || (ns_ctx_data[idx].ref_cnt == 0)) {
149         __enable_irq();
150         return false;
151     }
152 
153     ns_ctx_data[idx].tid = tid;
154     ns_ctx_data[idx].nsid = nsid;
155     active_ns_ctx_index = idx;
156     __enable_irq();
157     return true;
158 }
159 
save_ns_ctx(uint8_t gid,uint8_t tid,uint8_t idx)160 bool save_ns_ctx(uint8_t gid, uint8_t tid, uint8_t idx)
161 {
162     __disable_irq();
163     /* Check if the given index is valid */
164     if ((idx != active_ns_ctx_index) || (idx >= TFM_NS_CONTEXT_MAX)) {
165         __enable_irq();
166         return false;
167     }
168 
169     /* Check group, thread ID and reference counter */
170     if ((ns_ctx_data[idx].gid != gid)
171         || (ns_ctx_data[idx].tid != tid)
172         || (ns_ctx_data[idx].ref_cnt == 0)) {
173         __enable_irq();
174         return false;
175     }
176 
177     /* Set active context index to invalid */
178     active_ns_ctx_index = TFM_NS_CONTEXT_MAX;
179     __enable_irq();
180     return true;
181 }
182 
get_nsid_from_active_ns_ctx(void)183 int32_t get_nsid_from_active_ns_ctx(void)
184 {
185     int32_t ret = TFM_NS_CLIENT_INVALID_ID;
186 
187     __disable_irq();
188 
189     if (active_ns_ctx_index < TFM_NS_CONTEXT_MAX) {
190         ret = ns_ctx_data[active_ns_ctx_index].nsid;
191     }
192 
193     __enable_irq();
194     return ret;
195 }
196