1 /*
2 * Copyright (C) 2022 Huawei Technologies Co., Ltd.
3 * Licensed under the Mulan PSL v2.
4 * You can use this software according to the terms and conditions of the Mulan PSL v2.
5 * You may obtain a copy of Mulan PSL v2 at:
6 * http://license.coscl.org.cn/MulanPSL2
7 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9 * PURPOSE.
10 * See the Mulan PSL v2 for more details.
11 */
12
13 #define _GNU_SOURCE
14
15 #include "ta_mt.h"
16 #include <stdlib.h>
17 #include <string.h>
18 #include <inttypes.h>
19 #include <dlfcn.h>
20 #include <pthread.h>
21 #include <ipclib.h>
22 #include <unistd.h>
23 #include <ta_framework.h>
24 #include <tee_log.h>
25 #include "load_init.h"
26 #include <ipclib_hal.h>
27 #include <spawn_ext.h>
28 #include <priorities.h>
29
30 #ifndef THREAD_STACK_SIZE
31 #define THREAD_STACK_SIZE (4096 * 4 * 5)
32 #endif
33
34 #define CREATE_IPC_CHANNEL_NUM 2
35
36 struct thread_info {
37 struct {
38 ta_entry_type ta_entry;
39 uint32_t inited;
40 int32_t priority;
41 const char *name;
42 const struct ta_routine_info *append_args;
43 } args;
44 pthread_t thread; /* pthread handler */
45 cref_t thread_ref; /* thread cref, used to terminated it. */
46 uint32_t tid;
47 cref_t t_channel[CH_CNT_MAX];
48 cref_t t_msghdl;
49 };
50 static struct thread_info g_tinfos[TA_SESSION_MAX];
51
52 struct create_thread_info {
53 size_t stack_size;
54 int32_t priority;
55 };
56
find_thread_by_tid(uint32_t tid)57 static struct thread_info *find_thread_by_tid(uint32_t tid)
58 {
59 for (int32_t i = 0; i < TA_SESSION_MAX; i++) {
60 if (g_tinfos[i].thread != 0 && g_tinfos[i].tid == tid)
61 return &g_tinfos[i];
62 }
63
64 return NULL;
65 }
66
get_free_thread_info_slot(void)67 static struct thread_info *get_free_thread_info_slot(void)
68 {
69 for (int32_t i = 0; i < TA_SESSION_MAX; i++) {
70 if (g_tinfos[i].thread == 0)
71 return &g_tinfos[i];
72 }
73
74 return NULL;
75 }
76
77 /* pti: no concurrent write access, write access is atomic. */
release_thread_info(struct thread_info * pti)78 static void release_thread_info(struct thread_info *pti)
79 {
80 pti->thread = 0;
81 }
82
83 #define INVALID_SENDER 0xffffffffU
84 /*
85 * This is a wrapper of ipc_msg_rcv_a.
86 * It use to process the possible result of ipc_msg_rcv_a,
87 * So no need to process this function's return value.
88 * if ipc_msg_rcv_a return NOT OK, only log it and return.
89 * if ipc_msg_rcv_a return OK, but the MsgSender is NOT GLOBAL_HANDLE,
90 * then try ipc_msg_rcv_a again, until get the Msg from globaltask.
91 */
msg_rcv_elf(uint32_t timeout,uint32_t * msg_id,void * msgp,uint16_t size)92 static void msg_rcv_elf(uint32_t timeout, uint32_t *msg_id, void *msgp, uint16_t size)
93 {
94 uint32_t ret;
95 uint32_t sender = INVALID_SENDER;
96
97 while (sender != GLOBAL_HANDLE) {
98 ret = ipc_msg_rcv_a(timeout, msg_id, msgp, size, &sender);
99 if (ret != SRE_OK) {
100 tloge("Msg rcv failed, ret = %u\n", ret);
101 return;
102 }
103
104 if (sender != GLOBAL_HANDLE)
105 tlogw("Msg recv from sender = %u\n", sender);
106 }
107 }
108
remove_all_ipc_channel(uint32_t tid,const struct thread_info * pti)109 static void remove_all_ipc_channel(uint32_t tid, const struct thread_info *pti)
110 {
111 int32_t i;
112 int32_t rc;
113 pid_t pid;
114
115 for (i = 0; i < CH_CNT_MAX; i++) {
116 pid = getpid();
117 if (pid == -1) {
118 tloge("get pid failed\n");
119 continue;
120 }
121
122 rc = ipc_remove_channel((taskid_t)pid_to_taskid(tid, pid), NULL, i, pti->t_channel[i]);
123 if (rc != 0)
124 tloge("remove ipc channel #%d failed: rc=%d\n", i, rc);
125 }
126 }
127
ta_recycle_thread(uint32_t tid)128 static TEE_Result ta_recycle_thread(uint32_t tid)
129 {
130 struct thread_info *pti = NULL;
131 int32_t rc;
132
133 pti = find_thread_by_tid(tid);
134 if ((pti == NULL) || (pti->thread == NULL)) {
135 tloge("Cannot find dest thread to recycle, tid = 0x%x\n", tid);
136 return TEE_ERROR_GENERIC;
137 }
138 tlogi("Suspend thread, tid=0x%x\n", tid);
139
140 /* cleanup thread resources, ignore any failed cleanup */
141 rc = thread_terminate(pti->thread);
142 if (rc != 0)
143 tloge("terminate thread failed tid=0x%" PRIx32 " rc=%d\n", tid, rc);
144
145 rc = pthread_join(pti->thread, NULL);
146 if (rc != 0)
147 tloge("pthread join failed: rc=%d\n", rc);
148
149 remove_all_ipc_channel(tid, pti);
150 ipc_msg_delete_hdl(pti->t_msghdl);
151 /* clear thread info struct */
152 release_thread_info(pti);
153
154 return TEE_SUCCESS;
155 }
156
is_agent(const char * task_name)157 static bool is_agent(const char *task_name)
158 {
159 return strncmp(task_name, SSA_SERVICE_NAME, sizeof(SSA_SERVICE_NAME)) == 0;
160 }
161
create_ipc_channel(const char * task_name,cref_t * ch[])162 static int32_t create_ipc_channel(const char *task_name, cref_t *ch[])
163 {
164 bool reg_tamgr = is_agent(task_name);
165 struct reg_items_st reg_items;
166 reg_items.reg_pid = true;
167 reg_items.reg_name = false;
168 reg_items.reg_tamgr = reg_tamgr;
169 if (ipc_create_channel(task_name, CREATE_IPC_CHANNEL_NUM, ch, reg_items) != 0) {
170 tloge("Cannot create thread channel\n");
171 return -1;
172 }
173
174 return 0;
175 }
176
call_task_entry(const struct thread_info * pti)177 static void call_task_entry(const struct thread_info *pti)
178 {
179 int32_t rc = set_priority(pti->args.priority);
180 if (rc < 0)
181 tloge("set priority failed: %x\n", rc);
182
183 /* call real TA entry */
184 if (pti->args.append_args != NULL)
185 (*pti->args.ta_entry.ta_entry)(pti->args.inited, pti->args.append_args);
186 else
187 (*pti->args.ta_entry.ta_entry_orig)(pti->args.inited);
188
189 /* should never get here, crash myself */
190 tee_abort("tee task entry exit!\n");
191 }
192
tee_task_entry_thread(void * data)193 static void *tee_task_entry_thread(void *data)
194 {
195 struct thread_info *pti = data;
196 int32_t tid;
197 cref_t msghdl;
198 cref_t *ch[CH_CNT_MAX];
199 int32_t i;
200 const char *name = pti->args.name;
201
202 /* get self tid */
203 tid = gettid();
204 if (tid < 0) {
205 tloge("thread self failed: ret=%d\n", tid);
206 goto err_get_tid;
207 }
208 pti->tid = (uint32_t)tid;
209
210 /* prepare message handle */
211 msghdl = ipc_msg_create_hdl();
212 if (!check_ref_valid(msghdl)) {
213 tloge("Cannot create msg_hdl\n");
214 goto err_get_tid;
215 }
216 pti->t_msghdl = msghdl;
217
218 /* store msghdl in self tls */
219 if(ipc_save_my_msghdl(msghdl) != 0) {
220 tloge("save hdl error");
221 goto err_save_hdl;
222 }
223 /* create IPC channel, and save to tls */
224 for (i = 0; i < CH_CNT_MAX; i++)
225 ch[i] = &pti->t_channel[i];
226
227 if (create_ipc_channel(name, ch) != 0)
228 goto err_save_hdl;
229
230 /* send tid reply to gtask, just pass msg id as 0 */
231 if (ipc_msg_qsend(pti->tid, GLOBAL_HANDLE, SECOND_CHANNEL) != SRE_OK) {
232 tloge("Msg send failed\n");
233 goto err_reply_tid;
234 }
235
236 call_task_entry(pti);
237
238 err_reply_tid:
239 remove_all_ipc_channel(tid, pti);
240
241 err_save_hdl:
242 ipc_msg_delete_hdl(pti->t_msghdl);
243
244 err_get_tid:
245 /* reply error for TaskCreate */
246 if (ipc_msg_qsend(CREATE_THREAD_FAIL, GLOBAL_HANDLE, SECOND_CHANNEL) != SRE_OK)
247 tloge("Msg send 1 failed\n");
248 release_thread_info(pti);
249 return NULL;
250 }
251
ta_create_thread(ta_entry_type entry,uint32_t inited,const struct create_thread_info * info,const char * name,const struct ta_routine_info * append_args)252 static TEE_Result ta_create_thread(ta_entry_type entry, uint32_t inited, const struct create_thread_info *info,
253 const char *name, const struct ta_routine_info *append_args)
254 {
255 int32_t rc;
256 pthread_attr_t attr;
257 struct thread_info *pti = NULL;
258
259 pti = get_free_thread_info_slot();
260 if (pti == NULL) {
261 tloge("out of thread\n");
262 return TEE_ERROR_SESSION_MAXIMUM;
263 }
264
265 if (pthread_attr_init(&attr) != 0) {
266 tloge("pthread attr init failed\n");
267 goto err_out;
268 }
269
270 /* set stack size for new thread */
271 if (pthread_attr_setstacksize(&attr, info->stack_size) != 0) {
272 tloge("pthread attr set stack size failed, size=0x%zx\n", info->stack_size);
273 goto err_out;
274 }
275
276 /* set thread args */
277 pti->args.ta_entry = entry;
278 pti->args.inited = inited;
279 pti->args.priority = info->priority;
280 pti->args.name = name;
281 pti->args.append_args = append_args;
282
283 /* create working thread, and get its thread ref */
284 rc = pthread_create(&pti->thread, &attr, tee_task_entry_thread, pti);
285 if (rc) {
286 tloge("pthread create failed: %d\n", rc);
287 goto err_out;
288 }
289
290 return TEE_SUCCESS;
291
292 err_out:
293 release_thread_info(pti);
294 return TEE_ERROR_GENERIC;
295 }
296
close_ta2ta_session(uint32_t tid)297 static void close_ta2ta_session(uint32_t tid)
298 {
299 void (*delete_ta2ta_session)(uint32_t tid) = NULL;
300 void *libtee_handle = NULL;
301
302 libtee_handle = get_libtee_handle();
303 if (libtee_handle == NULL) {
304 tloge("libtee has not open\n");
305 return;
306 }
307
308 delete_ta2ta_session = dlsym(libtee_handle, "delete_all_ta2ta_session");
309 if (delete_ta2ta_session == NULL) {
310 tloge("cannot get delete ta2ta session symbol\n");
311 return;
312 }
313 delete_ta2ta_session(tid);
314 }
315
clear_session(uint32_t session_id)316 static void clear_session(uint32_t session_id)
317 {
318 void (*clear_session_ops)(uint32_t session_id) = NULL;
319 void *libtee_handle = NULL;
320
321 libtee_handle = get_libtee_handle();
322 if (libtee_handle == NULL) {
323 tloge("libtee has not open\n");
324 return;
325 }
326
327 clear_session_ops = dlsym(libtee_handle, "clear_session_exception");
328 if (clear_session_ops == NULL) {
329 tloge("cannot get clear session symbol\n");
330 return;
331 }
332 clear_session_ops(session_id);
333 }
334
close_session_exception(uint32_t session_id)335 static void close_session_exception(uint32_t session_id)
336 {
337 clear_session(session_id);
338 }
339
handle_thread_create(ta_entry_type entry,const struct create_thread_info * info,const char * name,const struct ta_routine_info * append_args)340 static void handle_thread_create(ta_entry_type entry, const struct create_thread_info *info, const char *name,
341 const struct ta_routine_info *append_args)
342 {
343 TEE_Result ret;
344
345 ret = ta_create_thread(entry, NON_INIT_BUILD, info, name, append_args);
346 if (ret != TEE_SUCCESS) {
347 tloge("ta create thread error!!! %x\n", ret);
348 if (ipc_msg_qsend(CREATE_THREAD_FAIL, GLOBAL_HANDLE, SECOND_CHANNEL) != SRE_OK)
349 tloge("Msg send failed\n");
350 }
351 }
352
handle_thread_remove(uint32_t tid,uint32_t session_id)353 static void handle_thread_remove(uint32_t tid, uint32_t session_id)
354 {
355 TEE_Result ret;
356 ret = ta_recycle_thread(tid);
357 if (ret != TEE_SUCCESS)
358 tloge("ta recycle thread stack error!!! %x\n", ret);
359 /* close all ta2ta session opened by this thread */
360 close_ta2ta_session(tid);
361 close_session_exception(session_id);
362 /* send reply to gtask, just pass msg id as 0, ret as TEE_SUCCESS for success */
363 if (ipc_msg_qsend((uint32_t)ret, GLOBAL_HANDLE, SECOND_CHANNEL) != SRE_OK)
364 tloge("Msg send failed\n");
365 }
366
tee_task_entry_handle(ta_entry_type ta_entry,int32_t priority,const char * name,const struct ta_routine_info * append_args)367 static void tee_task_entry_handle(ta_entry_type ta_entry, int32_t priority, const char *name,
368 const struct ta_routine_info *append_args)
369 {
370 uint32_t cmd;
371 uint32_t tid;
372 uint32_t session_id;
373 struct create_thread_info info;
374 while (1) {
375 struct global_to_service_thread_msg entry_msg = { { { 0 } } };
376 cmd = 0;
377 tlogd("++ Service TA task enter suspend\n");
378 msg_rcv_elf(OS_WAIT_FOREVER, (uint32_t *)(&cmd), &entry_msg, sizeof(entry_msg));
379 tlogd("-- Service TA rsv cmd : 0x%x\n", cmd);
380 switch (cmd) {
381 case CALL_TA_CREATE_THREAD:
382 tlogd("++ CALL TA CREATE THREAD\n");
383 info.stack_size = entry_msg.create_msg.stack_size;
384 info.priority = priority;
385 handle_thread_create(ta_entry, &info, name, append_args);
386 break;
387 case CALL_TA_REMOVE_THREAD:
388 tlogd("++ CALL TA REMOVE THREAD\n");
389 tid = entry_msg.remove_msg.tid;
390 session_id = entry_msg.remove_msg.session_id;
391 handle_thread_remove(tid, session_id);
392 break;
393 case CALL_TA_STHREAD_EXIT: /* no need to break, cos this proc exit directly */
394 tlogd("++ CALL TA STHREAD EXIT\n");
395 exit(0);
396 default:
397 tloge("invalid cmdid 0x%x\n", cmd);
398 break;
399 }
400 }
401 }
402
403 /* return from this function will cause taldr crash itself */
tee_task_entry_mt(ta_entry_type ta_entry,int32_t priority,const char * name,const struct ta_routine_info * append_args)404 void tee_task_entry_mt(ta_entry_type ta_entry, int32_t priority, const char *name,
405 const struct ta_routine_info *append_args)
406 {
407 TEE_Result ret;
408 size_t stack_size;
409 struct create_thread_info info;
410
411 /* no need check ta_entry_orig since ta_entry_type is a union */
412 if (ta_entry.ta_entry == NULL || name == NULL) {
413 tloge("bad TA entry\n");
414 return;
415 }
416
417 stack_size = getstacksize();
418 if (stack_size == 0) {
419 tloge("get stack size failed, use default stack size 0x%x\n", THREAD_STACK_SIZE);
420 stack_size = THREAD_STACK_SIZE;
421 }
422
423 info.stack_size = stack_size;
424 info.priority = priority;
425 /* Create a working thread at startup */
426 ret = ta_create_thread(ta_entry, INIT_BUILD, &info, name, append_args);
427 if (ret != TEE_SUCCESS) {
428 tloge("ta create thread error!!! %x\n", ret);
429 /* notify gtask that thread creating fails */
430 if (ipc_msg_qsend(CREATE_THREAD_FAIL, GLOBAL_HANDLE, SECOND_CHANNEL) != SRE_OK)
431 tloge("Msg send failed\n");
432 return;
433 }
434
435 tee_task_entry_handle(ta_entry, priority, name, append_args);
436 }
437