[Docs] Added Information about Secure Monitor
[3rdparty/ote_partner/tlk.git] / kernel / task_unload.c
1 /*
2  * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include <debug.h>
24 #include <sys/types.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <err.h>
28 #include <malloc.h>
29 #include <arch/arm/mmu.h>
30 #include <kernel/task.h>
31 #include <kernel/thread.h>
32 #include <kernel/task_load.h>
33 #include <kernel/event.h>
34 #include <ote_intf.h>
35
36 static event_t task_reaper_event;
37 static uint32_t task_unload_counter;
38
39 /*! Release resources of a terminated task.
40  *
41  * Called by the task_reaper thread.
42  */
43 static void task_free_resources(task_t **taskp_p)
44 {
45         if (taskp_p && *taskp_p) {
46                 task_map_t *mptr = NULL;
47                 task_map_t *mptr_tmp = NULL;
48                 task_t *taskp = *taskp_p;
49
50                 ASSERT(taskp->task_state == TASK_STATE_TERMINATED);
51                 ASSERT(taskp->elf_hdr);
52                 ASSERT(taskp->task_size > 0);
53                 ASSERT(list_is_empty(&taskp->thread_node));
54                 ASSERT(list_in_list(&taskp->node));
55
56                 memset(&taskp->props.uuid, 0, sizeof(te_service_id_t));
57
58                 list_for_every_entry_safe(&taskp->map_list, mptr, mptr_tmp, task_map_t, node) {
59                         arch_task_unmap_memory(taskp, mptr);
60                 }
61
62                 /* deallocate the task page tables */
63                 arm_mmu_dealloc_upagetable(taskp);
64
65                 /* clear task image in tlk heap before deallocating it */
66                 memset(taskp->elf_hdr, 0, taskp->task_size);
67
68                 if (taskp->task_type == TASK_TYPE_LOADED) {
69                         /* dealloc task image memory, if allocated from TLK heap */
70                         task_dealloc_memory((vaddr_t)taskp->elf_hdr, taskp->task_size);
71                 }
72
73                 enter_critical_section();
74                 list_delete(&taskp->node);
75                 exit_critical_section();
76
77                 arch_task_killed(taskp);
78
79                 /* Clear task header in task_list */
80                 memset(taskp, 0, sizeof(task_t));
81
82                 free(taskp);
83                 *taskp_p = NULL;
84
85                 task_unload_counter++;
86         }
87 }
88
89 /*! A reaper thread that gets an event up every time a kernel thread associated
90  * with a task gets killed.
91  *
92  * This scans the task list for any tasks that has no threads left AND
93  * is flagged as terminated. This means tasks started without threads or tasks committing
94  * suicide will not get reaped, they need to be explicitely unloaded.
95  *
96  * If it finds such a zombie task it will release its resources.
97  */
98 static int task_reaper(void *arg)
99 {
100         (void)arg;
101
102         while(1) {
103                 uint32_t index = 0;
104                 task_t *taskp = NULL;
105                 u_int task_next_index = 0;
106
107                 event_wait(&task_reaper_event);
108
109                 enter_critical_section();
110
111                 task_next_index = task_get_count();
112
113                 /* Scan task list for terminated tasks that have no threads left.
114                  *
115                  * Scan the list every time from the beginning to find all terminated
116                  * tasks since last scan.
117                  */
118                 while(1) {
119                         if (index >= task_next_index) {
120                                 taskp = NULL;
121                                 break;
122                         }
123
124                         taskp = task_find_task_by_index(index++);
125                         if (!taskp)
126                                 continue;
127
128                         if ((taskp->task_state == TASK_STATE_TERMINATED) &&
129                             (list_is_empty(&taskp->thread_node))) {
130                                 /*
131                                  * taskp has no threads left and in terminated
132                                  * state => release it...
133                                  */
134                                 break;
135                         }
136                 }
137
138                 exit_critical_section();
139
140                 if (taskp) {
141                         /* and then free the resources */
142                         task_free_resources(&taskp);
143
144                         /* Do not unsignal event when dead task found,
145                          * instead scan for more.
146                          */
147                         continue;
148                 }
149
150                 event_unsignal(&task_reaper_event);
151         }
152
153         return 0;
154 }
155
156 void task_unload_init(void)
157 {
158         event_init(&task_reaper_event, false, 0);
159
160         (void)thread_resume(thread_create("task reaper", &task_reaper,
161                                           NULL, DEFAULT_PRIORITY,
162                                           DEFAULT_STACK_SIZE));
163
164         dprintf(SPEW, "task reaper started\n");
165 }
166
167 /*! Remove a dead thread from the thread list of a task.
168  *
169  * Called by thread cleanup before the thread resources
170  * are deallocated when the thread belongs to a task.
171  */
172 void task_thread_killed(thread_t *thread)
173 {
174         task_t *taskp = NULL;
175
176         if (!thread)
177                 return;
178
179         ASSERT(thread->state == THREAD_DEATH);
180         ASSERT(thread->arch.task);
181         ASSERT(list_in_list(&thread->task_node));
182
183         arch_task_thread_killed(thread);
184
185         /* remove the thread from TE session lists */
186         te_session_cancel_thread(thread);
187
188         taskp = thread->arch.task;
189
190         /* remove dead thread from task's thread list */
191         enter_critical_section();
192         list_delete(&thread->task_node);
193         exit_critical_section();
194
195         /* Was this the last thread of a task?
196          * If so, flag the task terminated and wakeup the Grim Reaper.
197          */
198         if (list_is_empty(&taskp->thread_node)) {
199                 taskp->task_state = TASK_STATE_TERMINATED;
200                 memset(&taskp->props.uuid, 0, sizeof(te_service_id_t));
201                 event_signal(&task_reaper_event, false);
202         }
203 }
204
205 /*! Initiate unloading of a task.
206  *
207  *  Called by task load syscall handler.
208  *
209  * Note that after unloading the last task which has installer privileges
210  * you can no longer install anything to the system.
211  */
212 status_t task_unload(task_t **taskp_p)
213 {
214 #if HAVE_UNLOAD_TASKS == 0
215         dprintf(INFO, "task unloading not enabled\n");
216         return ERR_NOT_ALLOWED;
217 #else
218         status_t err = NO_ERROR;
219         thread_t *thread = NULL;
220         thread_t *th_tmp = NULL;
221         int killed_thread_count = 0;
222         const char *ch_state = NULL;
223         char tp_name[OTE_TASK_NAME_MAX_LENGTH+3];
224         task_t *taskp = NULL;
225
226         if (!taskp_p || !*taskp_p) {
227                 err = ERR_INVALID_ARGS;
228                 goto exit;
229         }
230         taskp = *taskp_p;
231
232         task_print_id(INFO, "task unload request ", taskp);
233
234 #if HAVE_UNLOAD_STATIC_TASKS == 0
235         if (taskp->task_type != TASK_TYPE_LOADED) {
236                 dprintf(INFO, "Can only unload previously loaded tasks\n");
237                 err = ERR_NOT_ALLOWED;
238                 goto exit;
239         }
240 #endif
241
242         switch (taskp->task_state) {
243         case TASK_STATE_BLOCKED:
244                 if (!ch_state)
245                         ch_state = "blocked";
246                 /* FALLTHROUGH */
247
248         case TASK_STATE_ACTIVE:
249                 if (!ch_state)
250                         ch_state = "active";
251
252                 if (taskp->props.initial_state & OTE_MANIFEST_TASK_ISTATE_STICKY) {
253                         dprintf(INFO, "Sticky %s task#%u%s can not be unloaded\n",
254                                 ch_state,
255                                 taskp->task_index,
256                                 task_get_name_str(taskp, " (", ")", tp_name, sizeof(tp_name)));
257                         err = ERR_NOT_ALLOWED;
258                         goto exit;
259                 }
260
261                 taskp->task_state = TASK_STATE_BLOCKED;
262                 /* FALLTHROUGH */
263
264         case TASK_STATE_STARTING:
265                 /* Task starting, thread exist but not yet running; error case */
266                 if (!ch_state)
267                         ch_state = "STARTING";
268
269                 /* The loop kills all kernel threads of the task which will wakeup
270                  * task reaper which will reclaim resources when the task dies.
271                  */
272                 list_for_every_entry_safe(&taskp->thread_node, thread, th_tmp, thread_t, task_node) {
273                         dprintf(INFO, "killing %s task#%u%s thread#%u\n",
274                                 ch_state, taskp->task_index,
275                                 task_get_name_str(taskp, " (", ")", tp_name, sizeof(tp_name)),
276                                 killed_thread_count);
277
278                         killed_thread_count++;
279                         thread_kill(thread);
280                 }
281
282                 /* All active tasks should have at least one thread, but
283                  * if there are no threads release the task resources directly.
284                  * (Task creation allows this when the EFL binary does not
285                  * have an entry point).
286                  */
287                 if (killed_thread_count == 0) {
288                         /* task had no associated threads so just release resources
289                          */
290                         dprintf(INFO, "releasing resources of %s task#%u%s -- no threads\n",
291                                 ch_state, taskp->task_index,
292                                 task_get_name_str(taskp, " (", ")", tp_name, sizeof(tp_name)));
293
294                         taskp->task_state = TASK_STATE_TERMINATED;
295                         task_free_resources(&taskp);
296                 }
297                 break;
298
299         case TASK_STATE_INIT:
300                 /* No threads have yet been created for a task in INIT state */
301                 if (!ch_state)
302                         ch_state = "INIT";
303                 /* FALLTHROUGH */
304
305         case TASK_STATE_TERMINATED:
306                 /* all threads must be killed before task enters TERMINATED state */
307                 if (!ch_state)
308                         ch_state = "TERMINATED";
309
310                 ASSERT(list_is_empty(&taskp->thread_node));
311
312                 dprintf(INFO, "releasing resources of %s task#%u%s -- no threads\n",
313                         ch_state, taskp->task_index,
314                         task_get_name_str(taskp, " (", ")", tp_name, sizeof(tp_name)));
315
316                 taskp->task_state = TASK_STATE_TERMINATED;
317                 task_free_resources(&taskp);
318                 break;
319
320         case TASK_STATE_UNKNOWN:
321                 if (!ch_state)
322                         ch_state = "UNKNOWN";
323                 /* FALLTHROUGH */
324
325         default:
326                 if (!ch_state)
327                         ch_state = "(unknown)";
328
329                 dprintf(INFO, "Attempt to unload task#%u with obscure state %s(%u)?\n",
330                         taskp->task_index, ch_state, taskp->task_state);
331                 break;
332         }
333
334 exit:
335         if (taskp_p)
336                 *taskp_p = taskp;
337         return err;
338 #endif /* HAVE_UNLOAD_TASKS */
339 }
340
341 uint32_t task_get_unload_counter()
342 {
343         return task_unload_counter;
344 }