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