1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2006-03-12 Bernard first version
9 * 2018-11-02 heyuanjie fix complie error in iar
10 * 2021-02-03 lizhirui add 64-bit arch support and riscv64 arch support
11 * 2021-08-26 linzhenxing add lwp_setcwd\lwp_getcwd
12 * 2023-02-20 wangxiaoyao inv icache before new app startup
13 * 2023-02-20 wangxiaoyao fix bug on foreground app switch
14 * 2023-10-16 Shell Support a new backtrace framework
15 * 2023-11-17 xqyjlj add process group and session support
16 * 2023-11-30 Shell add lwp_startup()
17 */
18
19 #define DBG_TAG "lwp"
20 #define DBG_LVL DBG_INFO
21 #include <rtdbg.h>
22
23 #include <rthw.h>
24 #include <rtthread.h>
25
26 #include <dfs_file.h>
27 #include <unistd.h>
28 #include <stdio.h> /* rename() */
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <sys/statfs.h> /* statfs() */
32
33 #include <lwp_elf.h>
34
35 #ifndef RT_USING_DFS
36 #error "lwp need file system(RT_USING_DFS)"
37 #endif
38
39 #include "lwp_internal.h"
40 #include "lwp_arch.h"
41 #include "lwp_arch_comm.h"
42 #include "lwp_signal.h"
43 #include "lwp_dbg.h"
44 #include <terminal/terminal.h>
45
46 #ifdef ARCH_MM_MMU
47 #include <lwp_user_mm.h>
48 #endif /* end of ARCH_MM_MMU */
49
50
51 #ifndef O_DIRECTORY
52 #define O_DIRECTORY 0x200000
53 #endif
54
55 #ifndef O_BINARY
56 #define O_BINARY 0x10000
57 #endif
58
59 #ifdef DFS_USING_WORKDIR
60 extern char working_directory[];
61 #endif
62
lwp_component_init(void)63 static int lwp_component_init(void)
64 {
65 int rc;
66 if ((rc = lwp_tid_init()) != RT_EOK)
67 {
68 LOG_E("%s: lwp_component_init() failed", __func__);
69 }
70 else if ((rc = lwp_pid_init()) != RT_EOK)
71 {
72 LOG_E("%s: lwp_pid_init() failed", __func__);
73 }
74 else if ((rc = rt_channel_component_init()) != RT_EOK)
75 {
76 LOG_E("%s: rt_channel_component_init failed", __func__);
77 }
78 else if ((rc = lwp_futex_init()) != RT_EOK)
79 {
80 LOG_E("%s: lwp_futex_init() failed", __func__);
81 }
82 return rc;
83 }
84 INIT_COMPONENT_EXPORT(lwp_component_init);
85
lwp_setcwd(char * buf)86 void lwp_setcwd(char *buf)
87 {
88 struct rt_lwp *lwp = RT_NULL;
89
90 if(strlen(buf) >= DFS_PATH_MAX)
91 {
92 rt_kprintf("buf too long!\n");
93 return ;
94 }
95
96 lwp = (struct rt_lwp *)rt_thread_self()->lwp;
97 if (lwp)
98 {
99 rt_strncpy(lwp->working_directory, buf, DFS_PATH_MAX - 1);
100 }
101 else
102 {
103 rt_strncpy(working_directory, buf, DFS_PATH_MAX - 1);
104 }
105
106 return ;
107 }
108
lwp_getcwd(void)109 char *lwp_getcwd(void)
110 {
111 char *dir_buf = RT_NULL;
112 struct rt_lwp *lwp = RT_NULL;
113 rt_thread_t thread = rt_thread_self();
114
115 if (thread)
116 {
117 lwp = (struct rt_lwp *)thread->lwp;
118 }
119
120 if (lwp)
121 {
122 if(lwp->working_directory[0] != '/')
123 {
124 dir_buf = &working_directory[0];
125 }
126 else
127 {
128 dir_buf = &lwp->working_directory[0];
129 }
130 }
131 else
132 dir_buf = &working_directory[0];
133
134 return dir_buf;
135 }
136
137 /**
138 * RT-Thread light-weight process
139 */
lwp_set_kernel_sp(uint32_t * sp)140 void lwp_set_kernel_sp(uint32_t *sp)
141 {
142 rt_thread_self()->kernel_sp = (rt_uint32_t *)sp;
143 }
144
lwp_get_kernel_sp(void)145 uint32_t *lwp_get_kernel_sp(void)
146 {
147 #ifdef ARCH_MM_MMU
148 return (uint32_t *)rt_thread_self()->sp;
149 #else
150 uint32_t* kernel_sp;
151 extern rt_uint32_t rt_interrupt_from_thread;
152 extern rt_uint32_t rt_thread_switch_interrupt_flag;
153 if (rt_thread_switch_interrupt_flag)
154 {
155 kernel_sp = (uint32_t *)((rt_thread_t)rt_container_of(rt_interrupt_from_thread, struct rt_thread, sp))->kernel_sp;
156 }
157 else
158 {
159 kernel_sp = (uint32_t *)rt_thread_self()->kernel_sp;
160 }
161 return kernel_sp;
162 #endif
163 }
164
165
166 /* lwp-thread clean up routine */
lwp_cleanup(struct rt_thread * tid)167 void lwp_cleanup(struct rt_thread *tid)
168 {
169 struct rt_lwp *lwp;
170
171 if (tid == NULL)
172 {
173 LOG_I("%s: invalid parameter tid == NULL", __func__);
174 return;
175 }
176 else
177 LOG_D("cleanup thread: %s, stack_addr: 0x%x", tid->parent.name, tid->stack_addr);
178
179 /**
180 * Brief: lwp thread cleanup
181 *
182 * Note: Critical Section
183 * - thread control block (RW. It's ensured that no one else can access tcb
184 * other than itself)
185 */
186 lwp = (struct rt_lwp *)tid->lwp;
187 lwp_thread_signal_detach(&tid->signal);
188
189 /* tty will be release in lwp_ref_dec() if ref is cleared */
190 lwp_ref_dec(lwp);
191 return;
192 }
193
lwp_execve_setup_stdio(struct rt_lwp * lwp)194 static void lwp_execve_setup_stdio(struct rt_lwp *lwp)
195 {
196 struct dfs_fdtable *lwp_fdt;
197 struct dfs_file *cons_file;
198 int cons_fd;
199
200 lwp_fdt = &lwp->fdt;
201
202 /* open console */
203 cons_fd = open("/dev/console", O_RDWR);
204 if (cons_fd < 0)
205 {
206 LOG_E("%s: Cannot open console tty", __func__);
207 return ;
208 }
209 LOG_D("%s: open console as fd %d", __func__, cons_fd);
210
211 /* init 4 fds */
212 lwp_fdt->fds = rt_calloc(4, sizeof(void *));
213 if (lwp_fdt->fds)
214 {
215 cons_file = fd_get(cons_fd);
216 lwp_fdt->maxfd = 4;
217 fdt_fd_associate_file(lwp_fdt, 0, cons_file);
218 fdt_fd_associate_file(lwp_fdt, 1, cons_file);
219 fdt_fd_associate_file(lwp_fdt, 2, cons_file);
220 }
221
222 close(cons_fd);
223 return;
224 }
225
_lwp_thread_entry(void * parameter)226 static void _lwp_thread_entry(void *parameter)
227 {
228 rt_thread_t tid;
229 struct rt_lwp *lwp;
230
231 tid = rt_thread_self();
232 lwp = (struct rt_lwp *)tid->lwp;
233 tid->cleanup = lwp_cleanup;
234 tid->user_stack = RT_NULL;
235
236 if (lwp->debug)
237 {
238 lwp->bak_first_inst = *(uint32_t *)lwp->text_entry;
239 *(uint32_t *)lwp->text_entry = dbg_get_ins();
240 rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lwp->text_entry, sizeof(uint32_t));
241 icache_invalid_all();
242 }
243
244 /**
245 * without ASID support, it will be a special case when trying to run application
246 * and exit multiple times and a same page frame allocated to it bound to
247 * different text segment. Then we are in a situation where icache contains
248 * out-of-dated data and must be handle by the running core itself.
249 * with ASID support, this should be a rare case that ASID & page frame both
250 * identical to previous running application.
251 *
252 * For a new application loaded into memory, icache are seen as empty. And there
253 * should be nothing in the icache entry to match. So this icache invalidation
254 * operation should have barely influence.
255 */
256 rt_hw_icache_invalidate_all();
257
258 #ifdef ARCH_MM_MMU
259 arch_start_umode(lwp->args, lwp->text_entry, (void *)USER_STACK_VEND, (char *)tid->stack_addr + tid->stack_size);
260 #else
261 arch_start_umode(lwp->args, lwp->text_entry, lwp->data_entry, (void *)((uint32_t)lwp->data_entry + lwp->data_size));
262 #endif /* ARCH_MM_MMU */
263 }
264
lwp_self(void)265 struct rt_lwp *lwp_self(void)
266 {
267 rt_thread_t tid;
268
269 tid = rt_thread_self();
270 if (tid)
271 {
272 return (struct rt_lwp *)tid->lwp;
273 }
274
275 return RT_NULL;
276 }
277
lwp_children_register(struct rt_lwp * parent,struct rt_lwp * child)278 rt_err_t lwp_children_register(struct rt_lwp *parent, struct rt_lwp *child)
279 {
280 /* lwp add to children link */
281 LWP_LOCK(parent);
282 child->sibling = parent->first_child;
283 parent->first_child = child;
284 child->parent = parent;
285 LWP_UNLOCK(parent);
286
287 LOG_D("%s(parent=%p, child=%p)", __func__, parent, child);
288 /* parent holds reference to child */
289 lwp_ref_inc(parent);
290 /* child holds reference to parent */
291 lwp_ref_inc(child);
292
293 return 0;
294 }
295
lwp_children_unregister(struct rt_lwp * parent,struct rt_lwp * child)296 rt_err_t lwp_children_unregister(struct rt_lwp *parent, struct rt_lwp *child)
297 {
298 struct rt_lwp **lwp_node;
299
300 LWP_LOCK(parent);
301 /* detach from children link */
302 lwp_node = &parent->first_child;
303 while (*lwp_node != child)
304 {
305 RT_ASSERT(*lwp_node != RT_NULL);
306 lwp_node = &(*lwp_node)->sibling;
307 }
308 (*lwp_node) = child->sibling;
309 child->parent = RT_NULL;
310 LWP_UNLOCK(parent);
311
312 LOG_D("%s(parent=%p, child=%p)", __func__, parent, child);
313 lwp_ref_dec(child);
314 lwp_ref_dec(parent);
315
316 return 0;
317 }
318
argscopy(struct rt_lwp * lwp,int argc,char ** argv,char ** envp)319 struct process_aux *argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp)
320 {
321 struct lwp_args_info ai;
322 rt_err_t error;
323 struct process_aux *ua;
324 const char **tail_argv[2] = {0};
325
326 error = lwp_args_init(&ai);
327 if (error)
328 {
329 return RT_NULL;
330 }
331
332 if (argc > 0)
333 {
334 tail_argv[0] = (void *)argv[argc - 1];
335 argv[argc - 1] = NULL;
336 lwp_args_put(&ai, (void *)argv, LWP_ARGS_TYPE_KARG);
337 lwp_args_put(&ai, (void *)tail_argv, LWP_ARGS_TYPE_KARG);
338 }
339 lwp_args_put(&ai, (void *)envp, LWP_ARGS_TYPE_KENVP);
340
341 ua = lwp_argscopy(lwp, &ai);
342 lwp_args_detach(&ai);
343
344 return ua;
345 }
346
lwp_execve(char * filename,int debug,int argc,char ** argv,char ** envp)347 pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
348 {
349 int result;
350 struct rt_lwp *lwp;
351 char *thread_name;
352 struct process_aux *aux;
353 int tid = 0;
354
355 if (filename == RT_NULL)
356 {
357 return -EINVAL;
358 }
359
360 if (access(filename, X_OK) != 0)
361 {
362 return -EACCES;
363 }
364
365 lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID | LWP_CREATE_FLAG_NOTRACE_EXEC);
366
367 if (lwp == RT_NULL)
368 {
369 LOG_E("lwp struct out of memory!\n");
370 return -ENOMEM;
371 }
372 LOG_D("lwp malloc : %p, size: %d!", lwp, sizeof(struct rt_lwp));
373
374 if ((tid = lwp_tid_get()) == 0)
375 {
376 lwp_ref_dec(lwp);
377 return -ENOMEM;
378 }
379 #ifdef ARCH_MM_MMU
380 if (lwp_user_space_init(lwp, 0) != 0)
381 {
382 lwp_tid_put(tid);
383 lwp_ref_dec(lwp);
384 return -ENOMEM;
385 }
386 #endif
387
388 if ((aux = argscopy(lwp, argc, argv, envp)) == RT_NULL)
389 {
390 lwp_tid_put(tid);
391 lwp_ref_dec(lwp);
392 return -ENOMEM;
393 }
394
395 result = lwp_load(filename, lwp, RT_NULL, 0, aux);
396 if (result == RT_EOK)
397 {
398 rt_thread_t thread = RT_NULL;
399 rt_uint32_t priority = 25, tick = 200;
400
401 lwp_execve_setup_stdio(lwp);
402
403 /* obtain the base name */
404 thread_name = strrchr(filename, '/');
405 thread_name = thread_name ? thread_name + 1 : filename;
406 #ifndef ARCH_MM_MMU
407 struct lwp_app_head *app_head = lwp->text_entry;
408 if (app_head->priority)
409 {
410 priority = app_head->priority;
411 }
412 if (app_head->tick)
413 {
414 tick = app_head->tick;
415 }
416 #endif /* not defined ARCH_MM_MMU */
417 thread = rt_thread_create(thread_name, _lwp_thread_entry, RT_NULL,
418 LWP_TASK_STACK_SIZE, priority, tick);
419 if (thread != RT_NULL)
420 {
421 struct rt_lwp *self_lwp;
422 rt_session_t session;
423 rt_processgroup_t group;
424
425 thread->tid = tid;
426 lwp_tid_set_thread(tid, thread);
427 LOG_D("lwp kernel => (0x%08x, 0x%08x)\n", (rt_size_t)thread->stack_addr,
428 (rt_size_t)thread->stack_addr + thread->stack_size);
429 self_lwp = lwp_self();
430
431 /* when create init, self_lwp == null */
432 if (self_lwp == RT_NULL && lwp_to_pid(lwp) != 1)
433 {
434 self_lwp = lwp_from_pid_and_lock(1);
435 }
436
437 if (self_lwp)
438 {
439 /* lwp add to children link */
440 lwp_children_register(self_lwp, lwp);
441 }
442
443 session = RT_NULL;
444 group = RT_NULL;
445
446 group = lwp_pgrp_create(lwp);
447 if (group)
448 {
449 lwp_pgrp_insert(group, lwp);
450 if (self_lwp == RT_NULL)
451 {
452 session = lwp_session_create(lwp);
453 lwp_session_insert(session, group);
454 }
455 else
456 {
457 session = lwp_session_find(lwp_sid_get_byprocess(self_lwp));
458 lwp_session_insert(session, group);
459 }
460 }
461
462 thread->lwp = lwp;
463 #ifndef ARCH_MM_MMU
464 struct lwp_app_head *app_head = (struct lwp_app_head*)lwp->text_entry;
465 thread->user_stack = app_head->stack_offset ?
466 (void *)(app_head->stack_offset -
467 app_head->data_offset +
468 (uint32_t)lwp->data_entry) : RT_NULL;
469 thread->user_stack_size = app_head->stack_size;
470 /* init data area */
471 rt_memset(lwp->data_entry, 0, lwp->data_size);
472 /* init user stack */
473 rt_memset(thread->user_stack, '#', thread->user_stack_size);
474 #endif /* not defined ARCH_MM_MMU */
475 rt_list_insert_after(&lwp->t_grp, &thread->sibling);
476
477 lwp->did_exec = RT_TRUE;
478
479 if (debug && rt_dbg_ops)
480 {
481 lwp->debug = debug;
482 rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void*)0);
483 }
484
485 rt_thread_startup(thread);
486 return lwp_to_pid(lwp);
487 }
488 }
489
490 lwp_tid_put(tid);
491 lwp_ref_dec(lwp);
492
493 return -RT_ERROR;
494 }
495
496 #ifdef RT_USING_MUSLLIBC
497 extern char **__environ;
498 #else
499 char **__environ = 0;
500 #endif
501
exec(char * filename,int debug,int argc,char ** argv)502 pid_t exec(char *filename, int debug, int argc, char **argv)
503 {
504 setenv("OS", "RT-Thread", 1);
505 return lwp_execve(filename, debug, argc, argv, __environ);
506 }
507
508 #ifdef ARCH_MM_MMU
lwp_user_setting_save(rt_thread_t thread)509 void lwp_user_setting_save(rt_thread_t thread)
510 {
511 if (thread)
512 {
513 thread->thread_idr = arch_get_tidr();
514 }
515 }
516
lwp_user_setting_restore(rt_thread_t thread)517 void lwp_user_setting_restore(rt_thread_t thread)
518 {
519 if (!thread)
520 {
521 return;
522 }
523 #if !defined(ARCH_RISCV64)
524 /* tidr will be set in RESTORE_ALL in risc-v */
525 arch_set_tidr(thread->thread_idr);
526 #endif
527
528 if (rt_dbg_ops)
529 {
530 struct rt_lwp *l = (struct rt_lwp *)thread->lwp;
531
532 if (l != 0)
533 {
534 rt_hw_set_process_id((size_t)l->pid);
535 }
536 else
537 {
538 rt_hw_set_process_id(0);
539 }
540 if (l && l->debug)
541 {
542 uint32_t step_type = 0;
543
544 step_type = dbg_step_type();
545
546 if ((step_type == 2) || (thread->step_exec && (step_type == 1)))
547 {
548 dbg_activate_step();
549 }
550 else
551 {
552 dbg_deactivate_step();
553 }
554 }
555 }
556 }
557 #endif /* ARCH_MM_MMU */
558
lwp_uthread_ctx_save(void * ctx)559 void lwp_uthread_ctx_save(void *ctx)
560 {
561 rt_thread_t thread;
562 thread = rt_thread_self();
563 thread->user_ctx.ctx = ctx;
564 }
565
lwp_uthread_ctx_restore(void)566 void lwp_uthread_ctx_restore(void)
567 {
568 rt_thread_t thread;
569 thread = rt_thread_self();
570 thread->user_ctx.ctx = RT_NULL;
571 }
572
lwp_backtrace_frame(rt_thread_t uthread,struct rt_hw_backtrace_frame * frame)573 rt_err_t lwp_backtrace_frame(rt_thread_t uthread, struct rt_hw_backtrace_frame *frame)
574 {
575 rt_err_t rc = -RT_ERROR;
576 long nesting = 0;
577 char **argv;
578 rt_lwp_t lwp;
579
580 if (uthread && uthread->lwp && rt_scheduler_is_available())
581 {
582 lwp = uthread->lwp;
583 argv = lwp_get_command_line_args(lwp);
584 if (argv)
585 {
586 rt_kprintf("please use: addr2line -e %s -a -f\n", argv[0]);
587 lwp_free_command_line_args(argv);
588 }
589 else
590 {
591 rt_kprintf("please use: addr2line -e %s -a -f\n", lwp->cmd);
592 }
593
594 while (nesting < RT_BACKTRACE_LEVEL_MAX_NR)
595 {
596 rt_kprintf(" 0x%lx", frame->pc);
597 if (rt_hw_backtrace_frame_unwind(uthread, frame))
598 {
599 break;
600 }
601 nesting++;
602 }
603 rt_kprintf("\n");
604 rc = RT_EOK;
605 }
606 return rc;
607 }
608