Parcourir la source

[component/lwp] support more feature of signal from IEEE Std 1003.1-2017 (#7828)

Signed-off-by: Shell <smokewood@qq.com>
Shell il y a 1 an
Parent
commit
58e0ddf287

+ 64 - 8
components/drivers/tty/console.c

@@ -20,7 +20,67 @@
 #endif /* RT_TTY_DEBUG */
 #include <rtdbg.h>
 
+#include <ipc/waitqueue.h>
+#include <ipc/ringbuffer.h>
+
 static struct tty_struct console_dev;
+static struct rt_ringbuffer console_rx_ringbuffer;
+static struct rt_wqueue console_rx_wqueue;
+static rt_thread_t console_rx_thread;
+static const size_t rb_bufsz = 0x1000;
+
+static void console_rx_work(void *parameter)
+{
+    int len;
+    char ch;
+    int lens;
+    static char buf[0x1000];
+
+    struct tty_struct *console;
+    console = &console_dev;
+
+    while (1)
+    {
+        rt_wqueue_wait(&console_rx_wqueue, 0, RT_WAITING_FOREVER);
+        lens = 0;
+
+        while (lens < sizeof(buf))
+        {
+            len = rt_ringbuffer_get(&console_rx_ringbuffer, (void *)&ch, sizeof(ch));
+            if (len == 0)
+            {
+                break;
+            }
+            lens += len;
+            buf[lens-1] = ch;
+        }
+
+        if (lens && console->ldisc->ops->receive_buf)
+        {
+            console->ldisc->ops->receive_buf((struct tty_struct *)console, buf, lens);
+        }
+    }
+}
+
+static int rx_thread_init(void)
+{
+    void *rb_buffer;
+    rt_thread_t thread;
+
+    rb_buffer = rt_malloc(rb_bufsz);
+    rt_ringbuffer_init(&console_rx_ringbuffer, rb_buffer, rb_bufsz);
+    rt_wqueue_init(&console_rx_wqueue);
+
+    thread = rt_thread_create("console_rx", console_rx_work, &console_dev, rb_bufsz, 10, 10);
+    if (thread != RT_NULL)
+    {
+        rt_thread_startup(thread);
+        console_rx_thread = thread;
+    }
+
+    return 0;
+}
+INIT_COMPONENT_EXPORT(rx_thread_init);
 
 static void console_rx_notify(struct rt_device *dev)
 {
@@ -28,7 +88,6 @@ static void console_rx_notify(struct rt_device *dev)
     int len = 0;
     int lens = 0;
     char ch = 0;
-    char buf[1024] = {0};
 
     console = (struct tty_struct *)dev;
     RT_ASSERT(console != RT_NULL);
@@ -41,17 +100,14 @@ static void console_rx_notify(struct rt_device *dev)
             break;
         }
         lens += len;
-        buf[lens-1] = ch;
-        if (lens > 1024)
+        rt_ringbuffer_put(&console_rx_ringbuffer, (void *)&ch, sizeof(ch));
+        if (lens > rb_bufsz)
         {
             break;
         }
     }
-
-    if (console->ldisc->ops->receive_buf)
-    {
-        console->ldisc->ops->receive_buf((struct tty_struct *)console, buf, lens);
-    }
+    if (console_rx_thread)
+        rt_wqueue_wakeup(&console_rx_wqueue, 0);
 }
 
 struct tty_struct *console_tty_get(void)

+ 9 - 3
components/drivers/tty/n_tty.c

@@ -50,7 +50,6 @@
 #define ECHO_BLOCK      256
 #define ECHO_DISCARD_WATERMARK  RT_TTY_BUF - (ECHO_BLOCK + 32)
 
-
 struct n_tty_data
 {
     /* producer-published */
@@ -136,6 +135,12 @@ rt_inline int test_and_clear_bit(int nr, volatile void *addr)
     return retval;
 }
 
+#ifdef __GNUC__
+rt_inline unsigned long __ffs(unsigned long word)
+{
+    return __builtin_ffsl(word);
+}
+#else
 rt_inline unsigned long __ffs(unsigned long word)
 {
     int num = 0;
@@ -174,6 +179,7 @@ rt_inline unsigned long __ffs(unsigned long word)
 
     return num;
 }
+#endif
 
 #define BITS_PER_LONG       32
 #define BITOP_WORD(nr)      ((nr) / BITS_PER_LONG)
@@ -703,13 +709,13 @@ static void __isig(int sig, struct tty_struct *tty)
                     ld->ops->set_termios(tty, &old_termios);
                 }
             }
-            tty_sigaddset(&lwp->signal_mask, SIGTTOU);
+            lwp_signal_kill(lwp, SIGTTOU, SI_USER, 0);
             old_lwp = tty_pop(&tty->head, RT_NULL);
             tty->foreground = old_lwp;
         }
         else
         {
-            lwp_kill(lwp_to_pid(lwp), sig);
+            lwp_signal_kill(lwp, sig, SI_USER, 0);
         }
     }
 }

+ 1 - 35
components/drivers/tty/tty.c

@@ -126,26 +126,6 @@ struct rt_lwp *tty_pop(struct tty_node **head, struct rt_lwp *target_lwp)
     return lwp;
 }
 
-rt_inline int tty_sigismember(lwp_sigset_t *set, int _sig)
-{
-    unsigned long sig = _sig - 1;
-
-    if (_LWP_NSIG_WORDS == 1)
-    {
-        return 1 & (set->sig[0] >> sig);
-    }
-    else
-    {
-        return 1 & (set->sig[sig / _LWP_NSIG_BPW] >> (sig % _LWP_NSIG_BPW));
-    }
-}
-
-static int is_ignored(int sig)
-{
-    return (tty_sigismember(&current->signal_mask, sig) ||
-        current->signal_handler[sig-1] == SIG_IGN);
-}
-
 /**
  *  tty_check_change    -   check for POSIX terminal changes
  *  @tty: tty to check
@@ -160,7 +140,6 @@ static int is_ignored(int sig)
 int __tty_check_change(struct tty_struct *tty, int sig)
 {
     pid_t pgrp = 0, tty_pgrp = 0;
-    struct rt_lwp *lwp = tty->foreground;
     int ret = 0;
     int level = 0;
 
@@ -182,20 +161,7 @@ int __tty_check_change(struct tty_struct *tty, int sig)
 
     if (tty_pgrp && (pgrp != tty->pgrp))
     {
-        if (is_ignored(sig))
-        {
-            if (sig == SIGTTIN)
-            {
-                ret = -EIO;
-            }
-        }
-        else
-        {
-            if (lwp)
-            {
-                lwp_kill(lwp_to_pid(lwp), sig);
-            }
-        }
+        lwp_signal_kill(current, sig, SI_USER, 0);
     }
     rt_hw_interrupt_enable(level);
 

+ 88 - 2
components/libc/compilers/common/ctime.c

@@ -20,6 +20,8 @@
  * 2021-05-01     Meco Man     support fixed timezone
  * 2021-07-21     Meco Man     implement that change/set timezone APIs
  * 2023-07-03     xqyjlj       refactor posix time and timer
+ * 2023-07-16     Shell        update signal generation routine for lwp
+ *                             adapt to new api and do the signal handling in thread context
  */
 
 #include "sys/time.h"
@@ -713,9 +715,56 @@ struct timer_obj
     clockid_t clockid;
 #ifdef RT_USING_SMART
     pid_t pid;
+    struct rt_work *work;
 #endif
 };
 
+#ifdef RT_USING_SMART
+struct lwp_timer_event_param
+{
+    struct rt_work work;
+
+    union
+    {
+        int tid;
+        pid_t pid;
+    };
+    int signo;
+};
+
+static void _lwp_timer_event_from_tid(struct rt_work *work, void *param)
+{
+    rt_err_t ret;
+    struct lwp_timer_event_param *data = (void *)work;
+    rt_thread_t thread;
+
+    RT_ASSERT(data->tid);
+
+    thread = lwp_tid_get_thread(data->tid);
+    ret = lwp_thread_signal_kill(thread, data->signo, SI_TIMER, 0);
+    if (ret)
+    {
+        LOG_W("%s: Do kill failed(tid %d) returned %d", __func__, data->tid, ret);
+    }
+
+    rt_free(work);
+}
+
+static void _lwp_timer_event_from_pid(struct rt_work *work, void *param)
+{
+    rt_err_t ret;
+    struct lwp_timer_event_param *data = (void *)work;
+
+    ret = lwp_signal_kill(lwp_from_pid(data->pid), data->signo, SI_TIMER, 0);
+    if (ret)
+    {
+        LOG_W("%s: Do kill failed(pid %d) returned %d", __func__, data->pid, ret);
+    }
+
+    rt_free(work);
+}
+#endif /* RT_USING_SMART */
+
 static void rtthread_timer_wrapper(void *timerobj)
 {
     struct timer_obj *timer;
@@ -735,7 +784,24 @@ static void rtthread_timer_wrapper(void *timerobj)
         rt_ktime_hrtimer_start(&timer->hrtimer);
     }
 #ifdef RT_USING_SMART
-    sys_kill(timer->pid, timer->sigev_signo);
+    /* this field is named as tid in musl */
+    int tid = *(int *)&timer->sigev_notify_function;
+    struct lwp_timer_event_param *data = (void *)timer->work;
+    data->signo = timer->sigev_signo;
+
+    if (!tid)
+    {
+        data->pid = timer->pid;
+        rt_work_init(timer->work, _lwp_timer_event_from_pid, 0);
+    }
+    else
+    {
+        data->tid = tid;
+        rt_work_init(timer->work, _lwp_timer_event_from_tid, 0);
+    }
+
+    if (rt_work_submit(timer->work, 0))
+        RT_ASSERT(0);
 #else
     if(timer->sigev_notify_function != RT_NULL)
     {
@@ -802,7 +868,27 @@ int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid)
     num %= 100;
     timer->sigev_signo = evp->sigev_signo;
 #ifdef RT_USING_SMART
-    timer->pid = lwp_self()->pid;
+    struct rt_work *work;
+    struct rt_lwp *lwp = lwp_self();
+
+    work = rt_malloc(sizeof(struct lwp_timer_event_param));
+    if (!work)
+    {
+        rt_set_errno(ENOMEM);
+        return -1;
+    }
+
+    if (lwp)
+    {
+        timer->pid = lwp_self()->pid;
+    }
+    else
+    {
+        timer->pid = 0; /* pid 0 is never used */
+    }
+
+    timer->work = work;
+
 #endif
     timer->sigev_notify_function = evp->sigev_notify_function;
     timer->val = evp->sigev_value;

+ 78 - 1
components/lwp/arch/aarch64/cortex-a/lwp_arch.c

@@ -1,15 +1,20 @@
 /*
- * Copyright (c) 2006-2021, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
  * 2021-05-18     Jesven       first version
+ * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
  */
 
+#include <armv8.h>
 #include <rthw.h>
 #include <rtthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <lwp_signal.h>
 
 #ifdef ARCH_MM_MMU
 
@@ -92,3 +97,75 @@ int arch_expand_user_stack(void *addr)
 }
 
 #endif
+#define ALGIN_BYTES (16)
+
+struct signal_ucontext
+{
+    rt_int64_t sigreturn;
+    lwp_sigset_t save_sigmask;
+
+    siginfo_t si;
+
+    rt_align(16)
+    struct rt_hw_exp_stack frame;
+};
+
+void *arch_signal_ucontext_restore(rt_base_t user_sp)
+{
+    struct signal_ucontext *new_sp;
+    new_sp = (void *)user_sp;
+
+    if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
+    {
+        lwp_thread_signal_mask(rt_thread_self(), LWP_SIG_MASK_CMD_SET_MASK, &new_sp->save_sigmask, RT_NULL);
+    }
+    else
+    {
+        LOG_I("User frame corrupted during signal handling\nexiting...");
+        sys_exit(EXIT_FAILURE);
+    }
+
+    return (char *)&new_sp->frame + sizeof(struct rt_hw_exp_stack);
+}
+
+void *arch_signal_ucontext_save(rt_base_t user_sp, siginfo_t *psiginfo,
+                                struct rt_hw_exp_stack *exp_frame,
+                                rt_base_t elr, rt_base_t spsr,
+                                lwp_sigset_t *save_sig_mask)
+{
+    struct signal_ucontext *new_sp;
+    new_sp = (void *)(user_sp - sizeof(struct signal_ucontext));
+
+    if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
+    {
+        /* push psiginfo */
+        if (psiginfo)
+        {
+            memcpy(&new_sp->si, psiginfo, sizeof(*psiginfo));
+        }
+
+        /* exp frame is already aligned as AAPCS64 required */
+        memcpy(&new_sp->frame, exp_frame, sizeof(*exp_frame));
+
+        /* fix the 3 fields in exception frame, so that memcpy will be fine */
+        new_sp->frame.pc = elr;
+        new_sp->frame.cpsr = spsr;
+        new_sp->frame.sp_el0 = user_sp;
+
+        /* copy the save_sig_mask */
+        memcpy(&new_sp->save_sigmask, save_sig_mask, sizeof(lwp_sigset_t));
+
+        /* copy lwp_sigreturn */
+        const size_t lwp_sigreturn_bytes = 8;
+        extern void lwp_sigreturn(void);
+        /* -> ensure that the sigreturn start at the outer most boundary */
+        memcpy(&new_sp->sigreturn,  &lwp_sigreturn, lwp_sigreturn_bytes);
+    }
+    else
+    {
+        LOG_I("%s: User stack overflow", __func__);
+        sys_exit(EXIT_FAILURE);
+    }
+
+    return new_sp;
+}

+ 22 - 0
components/lwp/arch/aarch64/cortex-a/lwp_arch.h

@@ -36,6 +36,28 @@ rt_inline void icache_invalid_all(void)
     asm volatile ("ic ialluis\n\tisb sy":::"memory");
 }
 
+/**
+ * @brief Save signal-related context to user stack
+ *
+ * @param user_sp the current sp of user
+ * @param exp_frame exception frame to resume former execution
+ * @param psiginfo pointer to the siginfo
+ * @param elr pc of former execution
+ * @param spsr program status of former execution
+ * @return void* the new user sp
+ */
+void *arch_signal_ucontext_save(rt_base_t user_sp, siginfo_t *psiginfo,
+                                struct rt_hw_exp_stack *exp_frame,
+                                rt_base_t elr, rt_base_t spsr,
+                                lwp_sigset_t *save_sig_mask);
+
+/**
+ * @brief Restore the signal mask after return
+ *
+ * @param user_sp sp of user
+ * @return void*
+ */
+void *arch_signal_ucontext_restore(rt_base_t user_sp);
 #ifdef __cplusplus
 }
 #endif

+ 86 - 62
components/lwp/arch/aarch64/cortex-a/lwp_gcc.S

@@ -1,11 +1,12 @@
 /*
- * Copyright (c) 2006-2021, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
  * 2021-05-18     Jesven       first version
+ * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
  */
 
 #ifndef __ASSEMBLY__
@@ -192,8 +193,9 @@ arch_syscall_exit:
     add sp, sp, #0x40
     RESTORE_FPU sp
 
-.global arch_ret_to_user
-arch_ret_to_user:
+/* the sp is reset to the outer most level */
+START_POINT(arch_ret_to_user)
+    /* save exception frame */
     SAVE_FPU sp
     stp x0, x1, [sp, #-0x10]!
     stp x2, x3, [sp, #-0x10]!
@@ -216,12 +218,16 @@ arch_ret_to_user:
     stp x0, x1, [sp, #-0x10]!
     stp x29, x30, [sp, #-0x10]!
 
+    /* pre-action */
     bl lwp_check_debug
+
     bl lwp_check_exit_request
     cbz w0, 1f
+    /* exit on event */
     mov x0, xzr
     b sys_exit
 1:
+    /* check if dbg ops exist */
     ldr x0, =rt_dbg_ops
     ldr x0, [x0]
     cbz x0, 3f
@@ -236,9 +242,17 @@ arch_ret_to_user:
     bic x2, x2, x1
     msr spsr_el1, x2
 3:
-    bl lwp_signal_check
-    cmp x0, xzr
 
+    /**
+     * push 2 dummy words to simulate a exception frame of interrupt
+     */
+    add sp, sp, #-0x10
+    mov x0, sp
+    bl lwp_thread_signal_catch
+    add sp, sp, #0x10
+
+    /* check debug */
+    /* restore exception frame */
     ldp x29, x30, [sp], #0x10
     ldp x0, x1, [sp], #0x10
     msr fpcr, x0
@@ -261,14 +275,14 @@ arch_ret_to_user:
     ldp x0, x1, [sp], #0x10
     RESTORE_FPU sp
 
-    bne user_do_signal
-
     stp x0, x1, [sp, #-0x10]!
     ldr x0, =rt_dbg_ops
     ldr x0, [x0]
     cmp x0, xzr
     ldp x0, x1, [sp], #0x10
     beq 1f
+
+    /* save */
     SAVE_FPU sp
     stp x0, x1, [sp, #-0x10]!
     stp x2, x3, [sp, #-0x10]!
@@ -289,8 +303,11 @@ arch_ret_to_user:
     mrs x1, fpsr
     stp x0, x1, [sp, #-0x10]!
     stp x29, x30, [sp, #-0x10]!
+
     mrs x0, elr_el1
     bl dbg_attach_req
+
+    /* restore */
     ldp x29, x30, [sp], #0x10
     ldp x0, x1, [sp], #0x10
     msr fpcr, x0
@@ -313,6 +330,7 @@ arch_ret_to_user:
     RESTORE_FPU sp
 1:
     eret
+START_POINT_END(arch_ret_to_user)
 
 .global lwp_check_debug
 lwp_check_debug:
@@ -371,26 +389,28 @@ lwp_check_debug_quit:
 
 arch_signal_quit:
     msr daifset, #3
-/*
-    drop stack data
-*/
+
+    /* drop current exception frame */
     add sp, sp, #CONTEXT_SIZE
-    bl lwp_signal_restore
-    /* x0 is user_ctx : ori sp, pc, cpsr */
-    ldr x1, [x0]
-    ldr x2, [x0, #8]
-    ldr x3, [x0, #16]
-    msr spsr_el1, x3
-    msr elr_el1, x2
-    add x1, x1, #16
-    msr sp_el0, x1
+    mrs x0, sp_el0
+    bl  arch_signal_ucontext_restore
+    add x0, x0, #-CONTEXT_SIZE
+    msr sp_el0, x0
 
+    /* restore previous exception frame */
     msr spsel, #0
 
+    ldp x2, x3, [sp], #0x10
+    msr elr_el1, x2
+    msr spsr_el1, x3
+
     ldp x29, x30, [sp], #0x10
+    // msr sp_el0, x29
+
     ldp x28, x29, [sp], #0x10
     msr fpcr, x28
     msr fpsr, x29
+
     ldp x28, x29, [sp], #0x10
     ldp x26, x27, [sp], #0x10
     ldp x24, x25, [sp], #0x10
@@ -412,65 +432,69 @@ arch_signal_quit:
 
     b arch_ret_to_user
 
-user_do_signal:
-    msr spsel, #0
-    SAVE_FPU sp
-    stp x0, x1, [sp, #-0x10]!
-    stp x2, x3, [sp, #-0x10]!
-    stp x4, x5, [sp, #-0x10]!
-    stp x6, x7, [sp, #-0x10]!
-    stp x8, x9, [sp, #-0x10]!
-    stp x10, x11, [sp, #-0x10]!
-    stp x12, x13, [sp, #-0x10]!
-    stp x14, x15, [sp, #-0x10]!
-    stp x16, x17, [sp, #-0x10]!
-    stp x18, x19, [sp, #-0x10]!
-    stp x20, x21, [sp, #-0x10]!
-    stp x22, x23, [sp, #-0x10]!
-    stp x24, x25, [sp, #-0x10]!
-    stp x26, x27, [sp, #-0x10]!
-    stp x28, x29, [sp, #-0x10]!
-    mrs x28, fpcr
-    mrs x29, fpsr
-    stp x28, x29, [sp, #-0x10]!
-    stp x29, x30, [sp, #-0x10]!
+/**
+ * rt_noreturn
+ * void arch_thread_signal_enter(
+ *      int signo,                      -> x0
+ *      siginfo_t *psiginfo,            -> x1
+ *      void *exp_frame,                -> x2
+ *      void *entry_uaddr,              -> x3
+ *      lwp_sigset_t *save_sig_mask,    -> x4
+ *      )
+ */
+.global arch_thread_signal_enter
+arch_thread_signal_enter:
+    mov x19, x0
+    mov x20, x2 /* exp_frame */
+    mov x21, x3
 
-    sub sp, sp, #0x10
-    adr x0, lwp_sigreturn
-    ldr w1, [x0]
-    str w1, [sp]
-    ldr w1, [x0, #4]
-    str w1, [sp, #4]
+    /**
+     * move exception frame to user stack
+     */
+    mrs x0, sp_el0
+    mrs x3, elr_el1
+    mov x5, x4
+    /** FIXME: spsr must restore from exception frame */
+    mrs x4, spsr_el1
 
-    mov x20, sp /* lwp_sigreturn */
-    mov x0, sp
+    /* arch_signal_ucontext_save(user_sp, psiginfo, exp_frame, elr, spsr, save_sig_mask); */
+    bl arch_signal_ucontext_save
 
     dc cvau, x0
     dsb sy
     ic ialluis
     dsb sy
 
-    msr spsel, #1
+    /**
+     * @brief Prepare the environment for signal handler
+     */
 
-    mrs x1, elr_el1
-    mrs x2, spsr_el1
-    bl lwp_signal_backup
-    /* x0 is signal */
-    mov x19, x0
-    bl lwp_sighandler_get
-    adds x1, x0, xzr
+    /** drop exp frame on kernel stack, reset kernel sp */
+    add sp, x20, #CONTEXT_SIZE
+
+    /** reset user sp */
+    msr sp_el0, x0
+    /** set the return address to the sigreturn */
+    mov x30, x0
+
+    /** set the entry address of signal handler */
+    msr elr_el1, x21
+
+    /* siginfo is above the return address */
+    add x2, x30, 16
+    add x1, x2, #CONTEXT_SIZE
     mov x0, x19
-    bne 1f
-    mov x1, x20
-1:
-    msr elr_el1, x1
-    mov x30, x20
+
+    /**
+     * handler(signo, psi, ucontext);
+     */
     eret
 
 lwp_debugreturn:
     mov x8, 0xf000
     svc #0
 
+.global lwp_sigreturn
 lwp_sigreturn:
     mov x8, #0xe000
     svc #0

+ 86 - 2
components/lwp/arch/arm/cortex-a/lwp_arch.c

@@ -1,16 +1,18 @@
 /*
- * Copyright (c) 2006-2018, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
  * 2019-10-28     Jesven       first version
+ * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
  */
 
 #include <rthw.h>
 #include <rtthread.h>
 #include <stddef.h>
+#include <stdlib.h>
 
 #ifdef ARCH_MM_MMU
 
@@ -54,7 +56,7 @@ void arch_kuser_init(rt_aspace_t aspace, void *vectors)
 {
     const size_t kuser_size = 0x1000;
     int err;
-    extern char __kuser_helper_start[], __kuser_helper_end[];
+    extern char *__kuser_helper_start, *__kuser_helper_end;
     int kuser_sz = __kuser_helper_end - __kuser_helper_start;
 
     err = rt_aspace_map_static(aspace, &kuser_varea, &vectors, kuser_size,
@@ -109,6 +111,88 @@ int arch_expand_user_stack(void *addr)
     }
     return ret;
 }
+#define ALGIN_BYTES 8
+#define lwp_sigreturn_bytes 8
+struct signal_regs {
+    rt_base_t lr;
+    rt_base_t spsr;
+    rt_base_t r0_to_r12[13];
+    rt_base_t ip;
+};
+
+struct signal_ucontext
+{
+    rt_base_t sigreturn[lwp_sigreturn_bytes / sizeof(rt_base_t)];
+    lwp_sigset_t save_sigmask;
+
+    siginfo_t si;
+
+    rt_align(8)
+    struct signal_regs frame;
+};
+
+void *arch_signal_ucontext_restore(rt_base_t user_sp)
+{
+    struct signal_ucontext *new_sp;
+    rt_base_t ip;
+    new_sp = (void *)user_sp;
+
+    if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
+    {
+        lwp_thread_signal_mask(rt_thread_self(), LWP_SIG_MASK_CMD_SET_MASK, &new_sp->save_sigmask, RT_NULL);
+        ip = new_sp->frame.ip;
+        /* let user restore its lr from frame.ip */
+        new_sp->frame.ip = new_sp->frame.lr;
+        /* kernel will pick eip from frame.lr */
+        new_sp->frame.lr = ip;
+    }
+    else
+    {
+        LOG_I("User frame corrupted during signal handling\nexiting...");
+        sys_exit(EXIT_FAILURE);
+    }
+
+    return (void *)&new_sp->frame;
+}
+
+void *arch_signal_ucontext_save(rt_base_t lr, siginfo_t *psiginfo,
+                                struct signal_regs *exp_frame, rt_base_t user_sp,
+                                lwp_sigset_t *save_sig_mask)
+{
+    rt_base_t spsr;
+    struct signal_ucontext *new_sp;
+    new_sp = (void *)(user_sp - sizeof(struct signal_ucontext));
+
+    if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
+    {
+        /* push psiginfo */
+        if (psiginfo)
+        {
+            memcpy(&new_sp->si, psiginfo, sizeof(*psiginfo));
+        }
+
+        memcpy(&new_sp->frame.r0_to_r12, exp_frame, sizeof(new_sp->frame.r0_to_r12) + sizeof(rt_base_t));
+        new_sp->frame.lr = lr;
+
+        __asm__ volatile("mrs %0, spsr":"=r"(spsr));
+        new_sp->frame.spsr = spsr;
+
+        /* copy the save_sig_mask */
+        memcpy(&new_sp->save_sigmask, save_sig_mask, sizeof(lwp_sigset_t));
+
+        /* copy lwp_sigreturn */
+        extern void lwp_sigreturn(void);
+        /* -> ensure that the sigreturn start at the outer most boundary */
+        memcpy(&new_sp->sigreturn,  &lwp_sigreturn, lwp_sigreturn_bytes);
+    }
+    else
+    {
+        LOG_I("%s: User stack overflow", __func__);
+        sys_exit(EXIT_FAILURE);
+    }
+
+    return new_sp;
+}
 
 #ifdef LWP_ENABLE_ASID
 #define MAX_ASID_BITS 8

+ 6 - 0
components/lwp/arch/arm/cortex-a/lwp_arch.h

@@ -40,6 +40,12 @@ rt_inline void icache_invalid_all(void)
 
 unsigned int arch_get_asid(struct rt_lwp *lwp);
 
+struct signal_regs;
+void *arch_signal_ucontext_restore(rt_base_t user_sp);
+void *arch_signal_ucontext_save(rt_base_t lr, siginfo_t *psiginfo,
+                                struct signal_regs *exp_frame, rt_base_t user_sp,
+                                lwp_sigset_t *save_sig_mask);
+
 #ifdef __cplusplus
 }
 #endif

+ 65 - 36
components/lwp/arch/arm/cortex-a/lwp_gcc.S

@@ -6,6 +6,7 @@
  * Change Logs:
  * Date           Author       Notes
  * 2018-12-10     Jesven       first version
+ * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
  */
 
 #include "rtconfig.h"
@@ -191,7 +192,7 @@ arch_syscall_exit:
 
 .global arch_ret_to_user
 arch_ret_to_user:
-    push {r0-r3, r12, lr}
+    push {r0-r12, lr}
     bl lwp_check_debug
     bl lwp_check_exit_request
     cmp r0, #0
@@ -199,10 +200,10 @@ arch_ret_to_user:
     mov r0, #0
     b sys_exit
 1:
-    bl lwp_signal_check
-    cmp r0, #0
-    pop {r0-r3, r12, lr}
-    bne user_do_signal
+    mov r0, sp
+    /* r0 -> exp frame */
+    bl lwp_thread_signal_catch
+    pop {r0-r12, lr}
 
     push {r0}
     ldr r0, =rt_dbg_ops
@@ -273,35 +274,65 @@ lwp_check_debug_quit:
 
 arch_signal_quit:
     cpsid i
+    /* drop context of signal handler */
     pop {r0 - r3, r12}
     pop {r4, r5, lr}
     pop {lr}
-    bl lwp_signal_restore
-    /* r0 is user_ctx : ori sp, pc, cpsr*/
-    ldr r1, [r0]
-    ldr r2, [r0, #4]
-    ldr r3, [r0, #8]
-    msr spsr_cxsf, r3
-    mov lr, r2
+
+    /* restore context */
     cps #Mode_SYS
-    mov sp, r1
+    mov r0, sp
+    cps #Mode_SVC
+    bl arch_signal_ucontext_restore
+
+    /* lr <- *(&frame.ip) */
+    ldr lr, [r0]
+    cps #Mode_SYS
+    mov sp, r0
+
+    /* drop ip in the frame and restore cpsr */
+    pop {r0}
+    pop {r0}
+    msr spsr_cxsf, r0
     pop {r0-r12, lr}
     cps #Mode_SVC
+
     b arch_ret_to_user
 
-user_do_signal:
-    mov r0, r0
+/**
+ * rt_noreturn
+ * void arch_thread_signal_enter(
+ *      int signo,                      -> r0
+ *      siginfo_t *psiginfo,            -> r1
+ *      void *exp_frame,                -> r2
+ *      void *entry_uaddr,              -> r3
+ *      lwp_sigset_t *save_sig_mask,    -> ??
+ * )
+ */
+.global arch_thread_signal_enter
+arch_thread_signal_enter:
+    mov r4, r0
+    mov r5, r3
+
     cps #Mode_SYS
-    push {r0-r12, lr}
+    mov r0, lr
+    mov r3, sp
+    cps #Mode_SVC
+    bl arch_signal_ucontext_save
 
-    sub sp, #8
-    ldr r0, =lwp_sigreturn
-    ldr r1, [r0]
-    str r1, [sp]
-    ldr r1, [r0, #4]
-    str r1, [sp, #4]
+    /* reset user sp */
+    cps #Mode_SYS
+    mov sp, r0
+    mov lr, r0
+    cps #Mode_SVC
 
-    mov r1, sp
+    /* r1,r2 <- new_user_sp */
+    mov r1, r0
+    mov r2, r0
+    /* r0 <- signo */
+    mov r0, r4
+
+    mov r1, r0
     mcr p15, 0, r1, c7, c11, 1   ;//dc cmvau
     add r1, #4
     mcr p15, 0, r1, c7, c11, 1   ;//dc cmvau
@@ -311,27 +342,25 @@ user_do_signal:
     dsb
     isb
 
-    mov r5, sp  ;//if func is 0
-    mov lr, sp
+    /* r4 <- &sigreturn */
+    mov r4, r2
 
-    add r0, sp, #8 /* lwp_sigreturn */
-    cps #Mode_SVC
-    mov r1, lr
-    mrs r2, spsr
-    bl  lwp_signal_backup
-    /* r0 is signal */
-    mov r4, r0
-    bl lwp_sighandler_get
-    mov lr, r0
+    /* lr <- user_handler() */
+    mov lr, r5
     cmp lr, #0
-    moveq lr, r5
-    mov r0, r4
+    moveq lr, r4
+
+    /* r1 <- siginfo */
+    mov r1, r2
+    add r1, #8
+    /* handler(signo, siginfo, ucontext) */
     movs pc, lr
 
 lwp_debugreturn:
     mov r7, #0xf000
     svc #0
 
+.global lwp_sigreturn
 lwp_sigreturn:
     mov r7, #0xe000
     svc #0

+ 75 - 5
components/lwp/arch/risc-v/rv64/lwp_arch.c

@@ -14,6 +14,7 @@
  * 2021-03-04     lizhirui     modify for new version of rt-smart
  * 2021-11-22     JasonHu      add lwp_set_thread_context
  * 2021-11-30     JasonHu      add clone/fork support
+ * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
  */
 #include <rthw.h>
 #include <rtthread.h>
@@ -34,6 +35,7 @@
 #include <cpuport.h>
 #include <encoding.h>
 #include <stack.h>
+#include <cache.h>
 
 extern rt_ubase_t MMUTable[];
 
@@ -245,6 +247,79 @@ int arch_set_thread_context(void (*exit)(void), void *new_thread_stack,
      */
 }
 
+#define ALGIN_BYTES (16)
+
+struct signal_ucontext
+{
+    rt_int64_t sigreturn;
+    lwp_sigset_t save_sigmask;
+
+    siginfo_t si;
+
+    rt_align(16)
+    struct rt_hw_stack_frame frame;
+};
+
+void *arch_signal_ucontext_restore(rt_base_t user_sp)
+{
+    struct signal_ucontext *new_sp;
+    new_sp = (void *)user_sp;
+
+    if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
+    {
+        lwp_thread_signal_mask(rt_thread_self(), LWP_SIG_MASK_CMD_SET_MASK, &new_sp->save_sigmask, RT_NULL);
+    }
+    else
+    {
+        LOG_I("User frame corrupted during signal handling\nexiting...");
+        sys_exit(EXIT_FAILURE);
+    }
+
+    return (void *)&new_sp->frame;
+}
+
+void *arch_signal_ucontext_save(int signo, siginfo_t *psiginfo,
+                         struct rt_hw_stack_frame *exp_frame, rt_base_t user_sp,
+                         lwp_sigset_t *save_sig_mask)
+{
+    struct signal_ucontext *new_sp;
+    new_sp = (void *)(user_sp - sizeof(struct signal_ucontext));
+
+    if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
+    {
+        /* push psiginfo */
+        if (psiginfo)
+        {
+            memcpy(&new_sp->si, psiginfo, sizeof(*psiginfo));
+        }
+
+        memcpy(&new_sp->frame, exp_frame, sizeof(*exp_frame));
+
+        /* copy the save_sig_mask */
+        memcpy(&new_sp->save_sigmask, save_sig_mask, sizeof(lwp_sigset_t));
+
+        /* copy lwp_sigreturn */
+        const size_t lwp_sigreturn_bytes = 8;
+        extern void lwp_sigreturn(void);
+        /* -> ensure that the sigreturn start at the outer most boundary */
+        memcpy(&new_sp->sigreturn,  &lwp_sigreturn, lwp_sigreturn_bytes);
+
+        /**
+         * synchronize dcache & icache if target is
+         * a Harvard Architecture machine, otherwise
+         * do nothing
+         */
+        rt_hw_sync_cache_local(&new_sp->sigreturn, 8);
+    }
+    else
+    {
+        LOG_I("%s: User stack overflow", __func__);
+        sys_exit(EXIT_FAILURE);
+    }
+
+    return new_sp;
+}
+
 /**
  * void lwp_exec_user(void *args, void *kernel_stack, void *user_entry)
  */
@@ -253,9 +328,4 @@ void lwp_exec_user(void *args, void *kernel_stack, void *user_entry)
     arch_start_umode(args, user_entry, (void *)USER_STACK_VEND, kernel_stack);
 }
 
-void *arch_get_usp_from_uctx(struct rt_user_context *uctx)
-{
-    return uctx->sp;
-}
-
 #endif /* ARCH_MM_MMU */

+ 5 - 0
components/lwp/arch/risc-v/rv64/lwp_arch.h

@@ -59,6 +59,11 @@ rt_inline void icache_invalid_all(void)
     rt_hw_cpu_icache_invalidate_all();
 }
 
+struct rt_hw_stack_frame;
+void *arch_signal_ucontext_restore(rt_base_t user_sp);
+void *arch_signal_ucontext_save(int signo, siginfo_t *psiginfo,
+                                struct rt_hw_stack_frame *exp_frame, rt_base_t user_sp,
+                                lwp_sigset_t *save_sig_mask);
 #ifdef __cplusplus
 }
 #endif

+ 48 - 96
components/lwp/arch/risc-v/rv64/lwp_gcc.S

@@ -10,6 +10,7 @@
  * 2021-02-19     lizhirui     port to new version of rt-smart
  * 2022-11-08     Wangxiaoyao  Cleanup codes;
  *                             Support new context switch
+ * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
  */
 
 #include "rtconfig.h"
@@ -89,9 +90,8 @@ arch_ret_to_user:
     call sys_exit
 
 1:
-    call lwp_signal_check
-    beqz a0, ret_to_user_exit
-    J user_do_signal
+    mv a0, sp
+    call lwp_thread_signal_catch
 
 ret_to_user_exit:
     RESTORE_ALL
@@ -103,129 +103,80 @@ ret_to_user_exit:
  * And handle pending signals;
  */
 arch_signal_quit:
-    call lwp_signal_restore
-    call arch_get_usp_from_uctx
-    // return value is user sp
+    LOAD a0, FRAME_OFF_SP(sp)
+    call arch_signal_ucontext_restore
+
+    /* reset kernel sp to the stack */
+    STORE sp, FRAME_OFF_SP(a0)
+    /* return value is user sp */
     mv sp, a0
 
-    // restore user sp before enter trap
+    /* restore user sp before enter trap */
     addi a0, sp, CTX_REG_NR * REGBYTES 
     csrw sscratch, a0
 
+
     RESTORE_ALL
     SAVE_ALL
     j arch_ret_to_user
 
 /**
- * Prepare and enter user signal handler
- * Move user exception frame and setup signal return
- * routine in user stack
+ * rt_noreturn
+ * void arch_thread_signal_enter(
+ *      int signo,                      -> a0
+ *      siginfo_t *psiginfo,            -> a1
+ *      void *exp_frame,                -> a2
+ *      void *entry_uaddr,              -> a3
+ *      lwp_sigset_t *save_sig_mask,    -> a4
+ *      )
  */
-user_do_signal:
-    /* prefetch ustack to avoid corrupted status in RESTORE/STORE pair below */
-    LOAD t0, FRAME_OFF_SP(sp)
-    addi t1, t0, -CTX_REG_NR * REGBYTES
-    LOAD t2, (t0)
-    li t3, -0x1000
-1:
-    add t0, t0, t3
-    LOAD t2, (t0)
-    bgt t0, t1, 1b
-
-    /** restore and backup kernel sp carefully to avoid leaking */
-    addi t0, sp, CTX_REG_NR * REGBYTES
-    csrw sscratch, t0
-
-    RESTORE_ALL
-    SAVE_ALL
-
-    /**
-     * save lwp_sigreturn in user memory
-     */
-    mv s0, sp
-    la t0, lwp_sigreturn
-    la t1, lwp_sigreturn_end
-    // t1 <- size
-    sub t1, t1, t0
-    // s0 <- dst
-    sub s0, s0, t1
-    mv s2, t1
-lwp_sigreturn_copy_loop:
-    addi t2, t1, -1
-    add t3, t0, t2
-    add t4, s0, t2
-    lb t5, 0(t3)
-    sb t5, 0(t4)
-    mv t1, t2
-    bnez t1, lwp_sigreturn_copy_loop
-
-    /**
-     * 1. clear sscratch & restore kernel sp to 
-     *    enter kernel mode routine
-     * 2. storage exp frame address to restore context, 
-     *    by calling to lwp_signal_backup
-     * 3. storage lwp_sigreturn entry address
-     * 4. get signal id as param for signal handler
-     */
-    mv s1, sp
-    csrrw sp, sscratch, x0
-
-    /**
-     * synchronize dcache & icache if target is
-     * a Harvard Architecture machine, otherwise
-     * do nothing
-     */
-    mv a0, s0
-    mv a1, s2
-    call rt_hw_sync_cache_local
-
-    /**
-     * backup user sp (point to saved exception frame, skip sigreturn routine)
-     * And get signal id
+.global arch_thread_signal_enter
+arch_thread_signal_enter:
+    mv s3, a2
+    mv s2, a0
+    mv s1, a3
 
-     * a0: user sp 
-     * a1: user_pc (not used, marked as 0 to avoid abuse)
-     * a2: user_flag (not used, marked as 0 to avoid abuse)
-     */
-    mv a0, s1
-    mv a1, zero
-    mv a2, zero
-    call lwp_signal_backup
+    LOAD t0, FRAME_OFF_SP(a2)
+    mv a3, t0
+    call arch_signal_ucontext_save
 
-    /**
-     * backup signal id in s2, 
-     * and get sighandler by signal id
-     */
-    mv s2, a0
-    call lwp_sighandler_get
+    /** restore kernel sp */
+    addi sp, s3, CTX_REG_NR * REGBYTES
 
     /**
      * set regiter RA to user signal handler
      * set sp to user sp & save kernel sp in sscratch
      */
-    mv ra, s0
+    mv ra, a0
     csrw sscratch, sp
-    mv sp, s0
+    mv sp, a0
 
     /**
-     * a0 is signal_handler,
-     * s1 = s0 == NULL ? lwp_sigreturn : s0;
+     * s1 is signal_handler,
+     * s1 = !s1 ? lwp_sigreturn : s1;
      */
-    mv s1, s0
-    beqz a0, skip_user_signal_handler
-    mv s1, a0
+    bnez s1, 1f
+    mv s1, ra
 
-skip_user_signal_handler:
-    // enter user mode and enable interrupt when return to user mode
+1:
+    /* enter user mode and enable interrupt when return to user mode */
     li t0, SSTATUS_SPP
     csrc sstatus, t0
     li t0, SSTATUS_SPIE
     csrs sstatus, t0
 
-    // sepc <- signal_handler
+    /* sepc <- signal_handler */
     csrw sepc, s1
-    // a0 <- signal id
+    /* a0 <- signal id */
     mv a0, s2
+    /* a1 <- siginfo */
+    add a1, sp, 16
+    /* dummy a2 */
+    mv a2, a1
+
+    /**
+     * handler(signo, psi, ucontext);
+     */
     sret
 
 .align 3
@@ -234,6 +185,7 @@ lwp_debugreturn:
     ecall
 
 .align 3
+.global lwp_sigreturn
 lwp_sigreturn:
     li a7, 0xfe
     ecall

+ 15 - 18
components/lwp/lwp.c

@@ -13,6 +13,10 @@
  * 2023-02-20     wangxiaoyao  fix bug on foreground app switch
  */
 
+#define DBG_TAG "LWP"
+#define DBG_LVL DBG_WARNING
+#include <rtdbg.h>
+
 #include <rthw.h>
 #include <rtthread.h>
 
@@ -32,12 +36,10 @@
 #include "lwp.h"
 #include "lwp_arch.h"
 #include "lwp_arch_comm.h"
+#include "lwp_signal.h"
+#include "lwp_dbg.h"
 #include "console.h"
 
-#define DBG_TAG "LWP"
-#define DBG_LVL DBG_WARNING
-#include <rtdbg.h>
-
 #ifdef ARCH_MM_MMU
 #include <lwp_user_mm.h>
 #endif /* end of ARCH_MM_MMU */
@@ -1025,37 +1027,32 @@ out:
     return ret;
 }
 
+/* lwp thread clean up */
 void lwp_cleanup(struct rt_thread *tid)
 {
     rt_base_t level;
     struct rt_lwp *lwp;
-    struct tty_node *tty_head = RT_NULL;
 
     if (tid == NULL)
     {
+        LOG_I("%s: invalid parameter tid == NULL", __func__);
         return;
     }
-
-    LOG_I("cleanup thread: %s, stack_addr: %08X", tid->parent.name, tid->stack_addr);
+    else
+        LOG_D("cleanup thread: %s, stack_addr: 0x%x", tid->parent.name, tid->stack_addr);
 
     level = rt_hw_interrupt_disable();
     lwp = (struct rt_lwp *)tid->lwp;
 
+    /* lwp thread cleanup */
     lwp_tid_put(tid->tid);
     rt_list_remove(&tid->sibling);
+    lwp_thread_signal_detach(&tid->signal);
+
     rt_hw_interrupt_enable(level);
-    if (lwp->tty != RT_NULL)
-    {
-        tty_head = lwp->tty->head;
-    }
-    if (!lwp_ref_dec(lwp))
-    {
-        if (tty_head)
-        {
-            tty_pop(&tty_head, lwp);
-        }
-    }
 
+    /* tty will be release in lwp_ref_dec() if ref is cleared */
+    lwp_ref_dec(lwp);
     return;
 }
 

+ 7 - 10
components/lwp/lwp.h

@@ -88,8 +88,10 @@ struct rt_lwp
     struct rt_lwp *sibling;
 
     rt_list_t wait_list;
-    int32_t  finish;
-    int  lwp_ret;
+    rt_bool_t finish;
+    rt_bool_t terminated;
+    rt_bool_t background;
+    int lwp_ret;
 
     void *text_entry;
     uint32_t text_size;
@@ -109,12 +111,8 @@ struct rt_lwp
     struct dfs_fdtable fdt;
     char cmd[RT_NAME_MAX];
 
-    int sa_flags;
-    lwp_sigset_t signal;
-    lwp_sigset_t signal_mask;
-    int signal_mask_bak;
-    rt_uint32_t signal_in_process;
-    lwp_sighandler_t signal_handler[_LWP_NSIG];
+    /* POSIX signal */
+    struct lwp_signal signal;
 
     struct lwp_avl_struct *object_root;
     struct rt_mutex object_mutex;
@@ -123,10 +121,9 @@ struct rt_lwp
     struct rt_wqueue wait_queue; /*for console */
     struct tty_struct *tty; /* NULL if no tty */
 
-    struct lwp_avl_struct *address_search_head; /* for addressed object fast rearch */
+    struct lwp_avl_struct *address_search_head; /* for addressed object fast search */
     char working_directory[DFS_PATH_MAX];
     int debug;
-    int background;
     uint32_t bak_first_ins;
 
 #ifdef LWP_ENABLE_ASID

+ 8 - 1
components/lwp/lwp_arch_comm.h

@@ -1,10 +1,12 @@
 /*
- * Copyright (c) 2006-2021, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
+ * 2022-09-30     RT-Thread    the general porting API for lwp
+ * 2023-07-18     Shell        New signal arch API arch_thread_signal_enter
  */
 
 #ifndef __LWP_ARCH_COMM__
@@ -54,4 +56,9 @@ void arch_set_thread_area(void *p);
 void* arch_get_tidr(void);
 void arch_set_tidr(void *p);
 
+/** entry point of architecture signal handling */
+rt_noreturn void arch_thread_signal_enter(int signo, siginfo_t *psiginfo,
+                                          void *exp_frame, void *entry_uaddr,
+                                          lwp_sigset_t *save_sig_mask);
+
 #endif /* __LWP_ARCH_COMM__ */

+ 47 - 0
components/lwp/lwp_clone.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-07-06     RT-Thread    the first version
+ */
+#ifndef __LWP_CLONE_H__
+#define __LWP_CLONE_H__
+
+#define CLONE_VM    0x00000100
+#define CLONE_FS    0x00000200
+#define CLONE_FILES 0x00000400
+#define CLONE_SIGHAND   0x00000800
+#define CLONE_PTRACE    0x00002000
+#define CLONE_VFORK 0x00004000
+#define CLONE_PARENT    0x00008000
+#define CLONE_THREAD    0x00010000
+#define CLONE_NEWNS 0x00020000
+#define CLONE_SYSVSEM   0x00040000
+#define CLONE_SETTLS    0x00080000
+#define CLONE_PARENT_SETTID 0x00100000
+#define CLONE_CHILD_CLEARTID    0x00200000
+#define CLONE_DETACHED  0x00400000
+#define CLONE_UNTRACED  0x00800000
+#define CLONE_CHILD_SETTID  0x01000000
+#define CLONE_NEWCGROUP 0x02000000
+#define CLONE_NEWUTS    0x04000000
+#define CLONE_NEWIPC    0x08000000
+#define CLONE_NEWUSER   0x10000000
+#define CLONE_NEWPID    0x20000000
+#define CLONE_NEWNET    0x40000000
+#define CLONE_IO    0x80000000
+
+/* arg[] -> flags
+ *          stack
+ *          new_tid
+ *          tls
+ *          set_clear_tid_address
+ *          quit_func
+ *          start_args
+ *          */
+#define SYS_CLONE_ARGS_NR 7
+
+#endif /* __LWP_CLONE_H__ */

+ 29 - 0
components/lwp/lwp_dbg.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-07-11     RT-Thread    first version
+ */
+
+#ifndef __LWP_DBG_H__
+#define __LWP_DBG_H__
+
+#include <rtthread.h>
+#include <rthw.h>
+#include <lwp.h>
+
+int dbg_thread_in_debug(void);
+void dbg_register(struct dbg_ops_t *dbg_ops);
+uint32_t dbg_get_ins(void);
+void dbg_activate_step(void);
+void dbg_deactivate_step(void);
+int dbg_check_event(struct rt_hw_exp_stack *regs, unsigned long esr);
+rt_channel_t gdb_server_channel(void);
+int dbg_step_type(void);
+void dbg_attach_req(void *pc);
+int dbg_check_suspend(void);
+
+#endif /* __LWP_DBG_H__ */

+ 33 - 25
components/lwp/lwp_pid.c

@@ -22,6 +22,7 @@
 
 #include "lwp.h"
 #include "lwp_pid.h"
+#include "lwp_signal.h"
 #include "tty.h"
 
 #ifdef ARCH_MM_MMU
@@ -319,7 +320,7 @@ struct rt_lwp* lwp_new(void)
     {
         return lwp;
     }
-    rt_memset(lwp, 0, sizeof(*lwp));
+    memset(lwp, 0, sizeof(*lwp));
     //lwp->tgroup_leader = RT_NULL;
     rt_list_init(&lwp->wait_list);
     lwp->leader = 0;
@@ -331,6 +332,8 @@ struct rt_lwp* lwp_new(void)
     rt_wqueue_init(&lwp->wait_queue);
     lwp->ref = 1;
 
+    lwp_signal_init(&lwp->signal);
+
     level = rt_hw_interrupt_disable();
     pid = lwp_pid_get();
     if (pid == 0)
@@ -475,34 +478,33 @@ void lwp_free(struct rt_lwp* lwp)
     }
 
     /* for parent */
+    if (lwp->parent)
     {
-        if (lwp->parent)
+        struct rt_thread *thread;
+        if (!rt_list_isempty(&lwp->wait_list))
         {
-            struct rt_thread *thread;
-            if (!rt_list_isempty(&lwp->wait_list))
-            {
-                thread = rt_list_entry(lwp->wait_list.next, struct rt_thread, tlist);
-                thread->error = RT_EOK;
-                thread->msg_ret = (void*)(rt_size_t)lwp->lwp_ret;
-                rt_thread_resume(thread);
-                rt_hw_interrupt_enable(level);
-                return;
-            }
-            else
-            {
-                struct rt_lwp **it = &lwp->parent->first_child;
+            thread = rt_list_entry(lwp->wait_list.next, struct rt_thread, tlist);
+            thread->error = RT_EOK;
+            thread->msg_ret = (void*)(rt_size_t)lwp->lwp_ret;
+            rt_thread_resume(thread);
+            rt_hw_interrupt_enable(level);
+            return;
+        }
+        else
+        {
+            struct rt_lwp **it = &lwp->parent->first_child;
 
-                while (*it != lwp)
-                {
-                    it = &(*it)->sibling;
-                }
-                *it = lwp->sibling;
+            while (*it != lwp)
+            {
+                it = &(*it)->sibling;
             }
+            *it = lwp->sibling;
         }
-        lwp_pid_put(lwp_to_pid(lwp));
-        rt_hw_interrupt_enable(level);
-        rt_free(lwp);
     }
+
+    lwp_pid_put(lwp_to_pid(lwp));
+    rt_hw_interrupt_enable(level);
+    rt_free(lwp);
 }
 
 int lwp_ref_inc(struct rt_lwp *lwp)
@@ -537,6 +539,7 @@ int lwp_ref_dec(struct rt_lwp *lwp)
             memset(&msg, 0, sizeof msg);
             rt_raw_channel_send(gdb_server_channel(), &msg);
         }
+        lwp_signal_detach(&lwp->signal);
 
 #ifndef ARCH_MM_MMU
 #ifdef RT_LWP_USING_SHM
@@ -846,7 +849,7 @@ static void cmd_kill(int argc, char** argv)
             sig = atoi(argv[3]);
         }
     }
-    lwp_kill(pid, sig);
+    lwp_signal_kill(lwp_from_pid(pid), sig, SI_USER, 0);
 }
 MSH_CMD_EXPORT_ALIAS(cmd_kill, kill, send a signal to a process);
 
@@ -861,7 +864,7 @@ static void cmd_killall(int argc, char** argv)
 
     while((pid = lwp_name2pid(argv[1])) > 0)
     {
-        lwp_kill(pid, SIGKILL);
+        lwp_signal_kill(lwp_from_pid(pid), SIGKILL, SI_USER, 0);
         rt_thread_mdelay(100);
     }
 }
@@ -979,6 +982,11 @@ void lwp_terminate(struct rt_lwp *lwp)
     }
 
     level = rt_hw_interrupt_disable();
+
+    /* stop the receiving of signals */
+    lwp->terminated = RT_TRUE;
+
+    /* broadcast exit request for sibling threads */
     for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next)
     {
         rt_thread_t thread;

+ 2 - 0
components/lwp/lwp_pid.h

@@ -11,6 +11,8 @@
 #ifndef LWP_PID_H__
 #define LWP_PID_H__
 
+#include "lwp.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 757 - 331
components/lwp/lwp_signal.c

@@ -1,21 +1,115 @@
 /*
- * Copyright (c) 2006-2020, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
  * 2019-11-12     Jesven       first version
+ * 2023-02-23     Shell        Support sigtimedwait
+ * 2023-07-04     Shell        Support siginfo, sigqueue
+ *                             remove lwp_signal_backup/restore() to reduce architecture codes
+ *                             update the generation, pending and delivery routines
  */
 
+#define DBG_TAG    "LWP_SIGNAL"
+#define DBG_LVL    DBG_INFO
+#include <rtdbg.h>
+
 #include <rthw.h>
 #include <rtthread.h>
+#include <string.h>
 
 #include "lwp.h"
 #include "lwp_arch.h"
+#include "lwp_signal.h"
 #include "sys/signal.h"
+#include "syscall_generic.h"
+
+static lwp_siginfo_t siginfo_create(int signo, int code, int value)
+{
+    lwp_siginfo_t siginfo;
+    struct rt_lwp *self_lwp;
+    rt_thread_t self_thr;
+
+    siginfo = rt_malloc(sizeof(*siginfo));
+    if (siginfo)
+    {
+        siginfo->ksiginfo.signo = signo;
+        siginfo->ksiginfo.code = code;
+        siginfo->ksiginfo.value = value;
+
+        self_lwp = lwp_self();
+        self_thr = rt_thread_self();
+        if (self_lwp)
+        {
+            siginfo->ksiginfo.from_pid = self_lwp->pid;
+            siginfo->ksiginfo.from_tid = self_thr->tid;
+        }
+        else
+        {
+            siginfo->ksiginfo.from_pid = 0;
+            siginfo->ksiginfo.from_tid = 0;
+        }
+    }
+
+    return siginfo;
+}
+
+rt_inline void siginfo_delete(lwp_siginfo_t siginfo)
+{
+    rt_free(siginfo);
+}
+
+rt_inline void _sigorsets(lwp_sigset_t *dset, const lwp_sigset_t *set0, const lwp_sigset_t *set1)
+{
+    switch (_LWP_NSIG_WORDS)
+    {
+        case 4:
+            dset->sig[3] = set0->sig[3] | set1->sig[3];
+            dset->sig[2] = set0->sig[2] | set1->sig[2];
+        case 2:
+            dset->sig[1] = set0->sig[1] | set1->sig[1];
+        case 1:
+            dset->sig[0] = set0->sig[0] | set1->sig[0];
+        default:
+            return;
+    }
+}
+
+rt_inline void _sigandsets(lwp_sigset_t *dset, const lwp_sigset_t *set0, const lwp_sigset_t *set1)
+{
+    switch (_LWP_NSIG_WORDS)
+    {
+        case 4:
+            dset->sig[3] = set0->sig[3] & set1->sig[3];
+            dset->sig[2] = set0->sig[2] & set1->sig[2];
+        case 2:
+            dset->sig[1] = set0->sig[1] & set1->sig[1];
+        case 1:
+            dset->sig[0] = set0->sig[0] & set1->sig[0];
+        default:
+            return;
+    }
+}
+
+rt_inline void _signotsets(lwp_sigset_t *dset, const lwp_sigset_t *set)
+{
+    switch (_LWP_NSIG_WORDS)
+    {
+        case 4:
+            dset->sig[3] = ~set->sig[3];
+            dset->sig[2] = ~set->sig[2];
+        case 2:
+            dset->sig[1] = ~set->sig[1];
+        case 1:
+            dset->sig[0] = ~set->sig[0];
+        default:
+            return;
+    }
+}
 
-rt_inline void lwp_sigaddset(lwp_sigset_t *set, int _sig)
+rt_inline void _sigaddset(lwp_sigset_t *set, int _sig)
 {
     unsigned long sig = _sig - 1;
 
@@ -29,7 +123,7 @@ rt_inline void lwp_sigaddset(lwp_sigset_t *set, int _sig)
     }
 }
 
-rt_inline void lwp_sigdelset(lwp_sigset_t *set, int _sig)
+rt_inline void _sigdelset(lwp_sigset_t *set, int _sig)
 {
     unsigned long sig = _sig - 1;
 
@@ -43,7 +137,7 @@ rt_inline void lwp_sigdelset(lwp_sigset_t *set, int _sig)
     }
 }
 
-rt_inline int lwp_sigisemptyset(lwp_sigset_t *set)
+rt_inline int _sigisemptyset(lwp_sigset_t *set)
 {
     switch (_LWP_NSIG_WORDS)
     {
@@ -59,7 +153,7 @@ rt_inline int lwp_sigisemptyset(lwp_sigset_t *set)
     }
 }
 
-rt_inline int lwp_sigismember(lwp_sigset_t *set, int _sig)
+rt_inline int _sigismember(lwp_sigset_t *set, int _sig)
 {
     unsigned long sig = _sig - 1;
 
@@ -73,7 +167,7 @@ rt_inline int lwp_sigismember(lwp_sigset_t *set, int _sig)
     }
 }
 
-rt_inline int next_signal(lwp_sigset_t *pending, lwp_sigset_t *mask)
+rt_inline int _next_signal(lwp_sigset_t *pending, lwp_sigset_t *mask)
 {
     unsigned long i, *s, *m, x;
     int sig = 0;
@@ -116,7 +210,218 @@ rt_inline int next_signal(lwp_sigset_t *pending, lwp_sigset_t *mask)
     return sig;
 }
 
-int lwp_suspend_sigcheck(rt_thread_t thread, int suspend_flag)
+#define _SIGQ(tp) (&(tp)->signal.sig_queue)
+
+rt_inline int sigqueue_isempty(lwp_sigqueue_t sigqueue)
+{
+    return _sigisemptyset(&sigqueue->sigset_pending);
+}
+
+rt_inline int sigqueue_ismember(lwp_sigqueue_t sigqueue, int signo)
+{
+    return _sigismember(&sigqueue->sigset_pending, signo);
+}
+
+rt_inline int sigqueue_peek(lwp_sigqueue_t sigqueue, lwp_sigset_t *mask)
+{
+    return _next_signal(&sigqueue->sigset_pending, mask);
+}
+
+rt_inline int sigqueue_examine(lwp_sigqueue_t sigqueue, lwp_sigset_t *pending)
+{
+    lwp_sigset_t not_mask;
+    int is_empty = sigqueue_isempty(sigqueue);
+    if (!is_empty)
+    {
+        _sigorsets(pending, &sigqueue->sigset_pending, &not_mask);
+    }
+    return is_empty;
+}
+
+static void sigqueue_enqueue(lwp_sigqueue_t sigqueue, lwp_siginfo_t siginfo)
+{
+    lwp_siginfo_t idx;
+    rt_bool_t inserted = RT_FALSE;
+    rt_list_for_each_entry(idx, &sigqueue->siginfo_list, node)
+    {
+        if (idx->ksiginfo.signo >= siginfo->ksiginfo.signo)
+        {
+            rt_list_insert_after(&idx->node, &siginfo->node);
+            inserted = RT_TRUE;
+            break;
+        }
+    }
+
+    if (!inserted)
+        rt_list_insert_before(&sigqueue->siginfo_list, &siginfo->node);
+
+    _sigaddset(&sigqueue->sigset_pending, siginfo->ksiginfo.signo);
+    return ;
+}
+
+/**
+ * dequeue a siginfo matching the signo which is likely to be existed, and
+ * test if any other siblings remains
+ */
+static lwp_siginfo_t sigqueue_dequeue(lwp_sigqueue_t sigqueue, int signo)
+{
+    lwp_siginfo_t found;
+    lwp_siginfo_t candidate;
+    lwp_siginfo_t next;
+    rt_bool_t is_empty;
+
+    found = RT_NULL;
+    is_empty = RT_TRUE;
+    rt_list_for_each_entry_safe(candidate, next, &sigqueue->siginfo_list, node)
+    {
+        if (candidate->ksiginfo.signo == signo)
+        {
+            if (found)
+            {
+                /* already found */
+                is_empty = RT_FALSE;
+                break;
+            }
+            else
+            {
+                /* found first */
+                found = candidate;
+                rt_list_remove(&found->node);
+            }
+        }
+        else if (candidate->ksiginfo.signo > signo)
+            break;
+    }
+
+    if (found && is_empty)
+        _sigdelset(&sigqueue->sigset_pending, signo);
+
+    return found;
+}
+
+static void sigqueue_discard(lwp_sigqueue_t sigqueue, int signo)
+{
+    lwp_siginfo_t queuing_si;
+    while (!sigqueue_isempty(sigqueue))
+    {
+        queuing_si = sigqueue_dequeue(sigqueue, signo);
+        siginfo_delete(queuing_si);
+    }
+}
+
+/* assuming that (void *) is compatible to long at length */
+RT_CTASSERT(lp_width_same, sizeof(void *) == sizeof(long));
+
+/** translate lwp siginfo to user siginfo_t  */
+rt_inline void siginfo_k2u(lwp_siginfo_t ksigi, siginfo_t *usigi)
+{
+    usigi->si_code = ksigi->ksiginfo.code;
+    usigi->si_signo = ksigi->ksiginfo.signo;
+    usigi->si_value.sival_ptr = (void *)ksigi->ksiginfo.value;
+    usigi->si_pid = ksigi->ksiginfo.from_pid;
+
+    /* deprecated field */
+    usigi->si_errno = 0;
+}
+
+/* must called in locked context */
+rt_inline lwp_sighandler_t _get_sighandler_locked(struct rt_lwp *lwp, int signo)
+{
+    return lwp->signal.sig_action[signo - 1];
+}
+
+static lwp_sigset_t *_mask_block_fn(rt_thread_t thread, const lwp_sigset_t *sigset, lwp_sigset_t *new_set)
+{
+    _sigorsets(new_set, &thread->signal.sigset_mask, sigset);
+    return new_set;
+}
+
+static lwp_sigset_t *_mask_unblock_fn(rt_thread_t thread, const lwp_sigset_t *sigset, lwp_sigset_t *new_set)
+{
+    lwp_sigset_t complement;
+    _signotsets(&complement, sigset);
+    _sigandsets(new_set, &thread->signal.sigset_mask, &complement);
+    return new_set;
+}
+
+static lwp_sigset_t *_mask_set_fn(rt_thread_t thread, const lwp_sigset_t *sigset, lwp_sigset_t *new_set)
+{
+    memcpy(new_set, sigset, sizeof(*sigset));
+    return new_set;
+}
+
+static lwp_sigset_t *(*_sig_mask_fn[__LWP_SIG_MASK_CMD_WATERMARK])
+    (rt_thread_t thread, const lwp_sigset_t *sigset, lwp_sigset_t *new_set) = {
+        [LWP_SIG_MASK_CMD_BLOCK] = _mask_block_fn,
+        [LWP_SIG_MASK_CMD_UNBLOCK] = _mask_unblock_fn,
+        [LWP_SIG_MASK_CMD_SET_MASK] = _mask_set_fn,
+    };
+
+static void _thread_signal_mask(rt_thread_t thread, lwp_sig_mask_cmd_t how,
+                                const lwp_sigset_t *sigset, lwp_sigset_t *oset)
+{
+    lwp_sigset_t new_set;
+
+    /**
+     * @note POSIX wants this API to be capable to query the current mask
+     *       by passing NULL in `sigset`
+     */
+    if (oset)
+        memcpy(oset, &thread->signal.sigset_mask, sizeof(lwp_sigset_t));
+
+    if (sigset)
+    {
+        _sig_mask_fn[how](thread, sigset, &new_set);
+
+        /* remove un-maskable signal from set */
+        _sigdelset(&new_set, SIGKILL);
+        _sigdelset(&new_set, SIGSTOP);
+
+        memcpy(&thread->signal.sigset_mask, &new_set, sizeof(lwp_sigset_t));
+    }
+}
+
+void lwp_sigqueue_clear(lwp_sigqueue_t sigq)
+{
+    lwp_siginfo_t this, next;
+    if (!sigqueue_isempty(sigq))
+    {
+        rt_list_for_each_entry_safe(this, next, &sigq->siginfo_list, node)
+        {
+            siginfo_delete(this);
+        }
+    }
+}
+
+rt_err_t lwp_signal_init(struct lwp_signal *sig)
+{
+    rt_err_t rc;
+    rc = rt_mutex_init(&sig->sig_lock, "lwpsig", RT_IPC_FLAG_FIFO);
+    if (rc == RT_EOK)
+    {
+        memset(&sig->sig_dispatch_thr, 0, sizeof(sig->sig_dispatch_thr));
+
+        memset(&sig->sig_action, 0, sizeof(sig->sig_action));
+        memset(&sig->sig_action_nodefer, 0, sizeof(sig->sig_action_nodefer));
+        memset(&sig->sig_action_onstack, 0, sizeof(sig->sig_action_onstack));
+        memset(&sig->sig_action_restart, 0, sizeof(sig->sig_action_restart));
+        memset(&sig->sig_action_siginfo, 0, sizeof(sig->sig_action_siginfo));
+        lwp_sigqueue_init(&sig->sig_queue);
+    }
+    return rc;
+}
+
+rt_err_t lwp_signal_detach(struct lwp_signal *signal)
+{
+    rt_err_t ret;
+
+    lwp_sigqueue_clear(&signal->sig_queue);
+    ret = rt_mutex_detach(&signal->sig_lock);
+
+    return ret;
+}
+
+int lwp_thread_signal_suspend_check(rt_thread_t thread, int suspend_flag)
 {
     struct rt_lwp *lwp = (struct rt_lwp*)thread->lwp;
     int ret = 0;
@@ -124,22 +429,22 @@ int lwp_suspend_sigcheck(rt_thread_t thread, int suspend_flag)
     switch (suspend_flag)
     {
         case RT_INTERRUPTIBLE:
-            if (!lwp_sigisemptyset(&thread->signal))
+            if (!sigqueue_isempty(_SIGQ(thread)))
             {
                 break;
             }
-            if (thread->lwp && !lwp_sigisemptyset(&lwp->signal))
+            if (thread->lwp && !sigqueue_isempty(_SIGQ(lwp)))
             {
                 break;
             }
             ret = 1;
             break;
         case RT_KILLABLE:
-            if (lwp_sigismember(&thread->signal, SIGKILL))
+            if (sigqueue_ismember(_SIGQ(thread), SIGKILL))
             {
                 break;
             }
-            if (thread->lwp && lwp_sigismember(&lwp->signal, SIGKILL))
+            if (thread->lwp && sigqueue_ismember(_SIGQ(lwp), SIGKILL))
             {
                 break;
             }
@@ -155,452 +460,573 @@ int lwp_suspend_sigcheck(rt_thread_t thread, int suspend_flag)
     return ret;
 }
 
-int lwp_signal_check(void)
+void lwp_thread_signal_catch(void *exp_frame)
 {
     rt_base_t level;
+    int signo;
     struct rt_thread *thread;
     struct rt_lwp *lwp;
-    uint32_t have_signal = 0;
-
-    level = rt_hw_interrupt_disable();
+    lwp_siginfo_t siginfo;
+    lwp_sigqueue_t pending;
+    lwp_sigset_t *sig_mask;
+    lwp_sigset_t save_sig_mask;
+    lwp_sigset_t new_sig_mask;
+    lwp_sighandler_t handler;
+    siginfo_t usiginfo;
+    siginfo_t *p_usi;
 
     thread = rt_thread_self();
-
-    if (thread->signal_in_process)
-    {
-        goto out;
-    }
-
     lwp = (struct rt_lwp*)thread->lwp;
 
-    if (lwp->signal_in_process)
+    RT_ASSERT(!!lwp);
+    level = rt_hw_interrupt_disable();
+
+    /* check if signal exist */
+    if (!sigqueue_isempty(_SIGQ(thread)))
     {
-        goto out;
+        pending = _SIGQ(thread);
+        sig_mask = &thread->signal.sigset_mask;
     }
-
-    have_signal = !lwp_sigisemptyset(&thread->signal);
-    if (have_signal)
+    else if (!sigqueue_isempty(_SIGQ(lwp)))
     {
-        thread->signal_in_process = 1;
-        goto out;
+        pending = _SIGQ(lwp);
+        sig_mask = &thread->signal.sigset_mask;
     }
-    have_signal = !lwp_sigisemptyset(&lwp->signal);
-    if (have_signal)
+    else
     {
-        lwp->signal_in_process = 1;
+        pending = RT_NULL;
     }
-out:
-    rt_hw_interrupt_enable(level);
-    return have_signal;
-}
-
-int lwp_signal_backup(void *user_sp, void *user_pc, void* user_flag)
-{
-    rt_base_t level;
-    struct rt_thread *thread;
-    struct rt_lwp *lwp;
-    int signal;
 
-    level = rt_hw_interrupt_disable();
-    thread = rt_thread_self();
-    if (thread->signal_in_process)
+    if (pending)
     {
-        thread->user_ctx.sp = user_sp;
-        thread->user_ctx.pc = user_pc;
-        thread->user_ctx.flag = user_flag;
+        /* peek the pending signal */
+        signo = sigqueue_peek(pending, sig_mask);
+        if (signo)
+        {
+            siginfo = sigqueue_dequeue(pending, signo);
+            RT_ASSERT(siginfo != RT_NULL);
+            handler = _get_sighandler_locked(lwp, signo);
 
-        signal = next_signal(&thread->signal, &thread->signal_mask);
-        RT_ASSERT(signal != 0);
-        lwp_sigaddset(&thread->signal_mask, signal);
-        thread->signal_mask_bak = signal;
-        lwp_sigdelset(&thread->signal, signal);
-    }
-    else
-    {
-        lwp = (struct rt_lwp*)thread->lwp;
-        lwp->user_ctx.sp = user_sp;
-        lwp->user_ctx.pc = user_pc;
-        lwp->user_ctx.flag = user_flag;
+            /* IGN signal will never be queued */
+            RT_ASSERT(handler != LWP_SIG_ACT_IGN);
 
-        signal = next_signal(&lwp->signal, &lwp->signal_mask);
-        RT_ASSERT(signal != 0);
-        lwp_sigaddset(&lwp->signal_mask, signal);
-        lwp->signal_mask_bak = signal;
-        lwp_sigdelset(&lwp->signal, signal);
-    }
-    rt_hw_interrupt_enable(level);
-    return signal;
-}
+            /* copy the blocked signal mask from the registered signal action */
+            memcpy(&new_sig_mask, &lwp->signal.sig_action_mask[signo - 1], sizeof(new_sig_mask));
 
-struct rt_user_context *lwp_signal_restore(void)
-{
-    rt_base_t level;
-    struct rt_thread *thread;
-    struct rt_lwp *lwp;
-    struct rt_user_context *ctx;
+            if (!_sigismember(&lwp->signal.sig_action_nodefer, signo))
+                _sigaddset(&new_sig_mask, signo);
 
-    level = rt_hw_interrupt_disable();
-    thread = rt_thread_self();
-    if (thread->signal_in_process)
-    {
-        ctx = &thread->user_ctx;
-        thread->signal_in_process = 0;
+            _thread_signal_mask(thread, LWP_SIG_MASK_CMD_BLOCK, &new_sig_mask, &save_sig_mask);
 
-        lwp_sigdelset(&thread->signal_mask, thread->signal_mask_bak);
-        thread->signal_mask_bak = 0;
+            /* siginfo is need for signal action */
+            if (_sigismember(&lwp->signal.sig_action_siginfo, signo))
+            {
+                siginfo_k2u(siginfo, &usiginfo);
+                p_usi = &usiginfo;
+            }
+            else
+                p_usi = RT_NULL;
+        }
     }
-    else
+    rt_hw_interrupt_enable(level);
+
+    if (pending && signo)
     {
-        lwp = (struct rt_lwp*)thread->lwp;
-        ctx = &lwp->user_ctx;
-        RT_ASSERT(lwp->signal_in_process != 0);
-        lwp->signal_in_process = 0;
+        siginfo_delete(siginfo);
 
-        lwp_sigdelset(&lwp->signal_mask, lwp->signal_mask_bak);
-        lwp->signal_mask_bak = 0;
+        /* signal default handler */
+        if (handler == LWP_SIG_ACT_DFL)
+        {
+            LOG_D("%s: default handler; and exit", __func__);
+            sys_exit(0);
+        }
+
+        /**
+         * enter signal action of user
+         * @note that the p_usi is release before entering signal action by
+         * reseting the kernel sp.
+         */
+        arch_thread_signal_enter(signo, p_usi, exp_frame, handler, &save_sig_mask);
+        /* the arch_thread_signal_enter() never return */
+        RT_ASSERT(0);
     }
-    rt_hw_interrupt_enable(level);
-    return ctx;
 }
 
-rt_inline int _lwp_check_ignore(int sig)
+static int _do_signal_wakeup(rt_thread_t thread, int sig)
 {
-    if (sig == SIGCHLD || sig == SIGCONT)
+    int need_schedule;
+    if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)
     {
-        return 1;
+
+        if ((thread->stat & RT_SIGNAL_COMMON_WAKEUP_MASK) != RT_SIGNAL_COMMON_WAKEUP_MASK)
+        {
+            rt_thread_wakeup(thread);
+            need_schedule = 1;
+        }
+        else if ((sig == SIGKILL) && ((thread->stat & RT_SIGNAL_KILL_WAKEUP_MASK) != RT_SIGNAL_KILL_WAKEUP_MASK))
+        {
+            rt_thread_wakeup(thread);
+            need_schedule = 1;
+        }
+        else
+        {
+            need_schedule = 0;
+        }
     }
-    return 0;
+    else
+        need_schedule = 0;
+
+    return need_schedule;
 }
 
-void sys_exit(int value);
-lwp_sighandler_t lwp_sighandler_get(int sig)
+/** find a candidate to be notified of the arrival */
+static rt_thread_t _signal_find_catcher(struct rt_lwp *lwp, int signo)
 {
-    lwp_sighandler_t func = RT_NULL;
-    struct rt_lwp *lwp;
-    rt_thread_t thread;
-    rt_base_t level;
+    rt_thread_t catcher = RT_NULL;
+    rt_thread_t candidate;
 
-    if (sig == 0 || sig > _LWP_NSIG)
+    candidate = lwp->signal.sig_dispatch_thr[signo - 1];
+    if (candidate != RT_NULL && !_sigismember(&candidate->signal.sigset_mask, signo))
     {
-        return func;
+        catcher = candidate;
     }
-    level = rt_hw_interrupt_disable();
-    thread = rt_thread_self();
-#ifndef ARCH_MM_MMU
-    if (thread->signal_in_process)
+    else
     {
-        func = thread->signal_handler[sig - 1];
-        goto out;
-    }
-#endif
-    lwp = (struct rt_lwp*)thread->lwp;
+        candidate = rt_thread_self();
 
-    func = lwp->signal_handler[sig - 1];
-    if (!func)
-    {
-        if (_lwp_check_ignore(sig))
+        /** @note: lwp of current is a const value that can be safely read */
+        if (candidate->lwp == lwp &&
+            !_sigismember(&candidate->signal.sigset_mask, signo))
         {
-            goto out;
+            catcher = candidate;
         }
-        if (lwp->signal_in_process)
+        else
         {
-            lwp_terminate(lwp);
+            rt_list_for_each_entry(candidate, &lwp->t_grp, sibling)
+            {
+                if (!_sigismember(&candidate->signal.sigset_mask, signo))
+                {
+                    catcher = candidate;
+                    break;
+                }
+            }
+
+            /* fall back to main thread */
+            if (catcher == RT_NULL)
+                catcher = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
         }
-        sys_exit(0);
-    }
-out:
-    rt_hw_interrupt_enable(level);
 
-    if (func == (lwp_sighandler_t)SIG_IGN)
-    {
-        func = RT_NULL;
+        /* reset the cache thread to catcher (even if catcher is main thread) */
+        lwp->signal.sig_dispatch_thr[signo - 1] = catcher;
     }
-    return func;
+
+    return catcher;
 }
 
-void lwp_sighandler_set(int sig, lwp_sighandler_t func)
+static int _siginfo_deliver_to_lwp(struct rt_lwp *lwp, lwp_siginfo_t siginfo)
 {
-    rt_base_t level;
+    rt_thread_t catcher;
 
-    if (sig == 0 || sig > _LWP_NSIG)
-        return;
-    if (sig == SIGKILL || sig == SIGSTOP)
-        return;
-    level = rt_hw_interrupt_disable();
-    ((struct rt_lwp*)rt_thread_self()->lwp)->signal_handler[sig - 1] = func;
-    rt_hw_interrupt_enable(level);
+    catcher = _signal_find_catcher(lwp, siginfo->ksiginfo.signo);
+
+    sigqueue_enqueue(&lwp->signal.sig_queue, siginfo);
+    return _do_signal_wakeup(catcher, siginfo->ksiginfo.signo);
 }
 
-#ifndef ARCH_MM_MMU
-void lwp_thread_sighandler_set(int sig, lwp_sighandler_t func)
+static int _siginfo_deliver_to_thread(struct rt_lwp *lwp, rt_thread_t thread, lwp_siginfo_t siginfo)
 {
-    rt_base_t level;
+    sigqueue_enqueue(_SIGQ(thread), siginfo);
+    return _do_signal_wakeup(thread, siginfo->ksiginfo.signo);
+}
 
-    if (sig == 0 || sig > _LWP_NSIG)
-        return;
-    level = rt_hw_interrupt_disable();
-    rt_thread_self()->signal_handler[sig - 1] = func;
-    rt_hw_interrupt_enable(level);
+rt_inline rt_bool_t _sighandler_is_ignored(struct rt_lwp *lwp, int signo)
+{
+    rt_bool_t is_ignored;
+    lwp_sighandler_t action;
+    lwp_sigset_t ign_set = lwp_sigset_init(LWP_SIG_IGNORE_SET);
+
+    action = _get_sighandler_locked(lwp, signo);
+
+    if (action == LWP_SIG_ACT_IGN)
+        is_ignored = RT_TRUE;
+    else if (action == LWP_SIG_ACT_DFL && _sigismember(&ign_set, signo))
+        is_ignored = RT_TRUE;
+    else
+        is_ignored = RT_FALSE;
+
+    return is_ignored;
+}
+
+rt_inline rt_bool_t _sighandler_cannot_caught(struct rt_lwp *lwp, int signo)
+{
+    return signo == SIGKILL || signo == SIGSTOP;
 }
-#endif
 
-int lwp_sigaction(int sig, const struct lwp_sigaction *act,
-             struct lwp_sigaction *oact, size_t sigsetsize)
+rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value)
 {
+    rt_err_t ret = -1;
     rt_base_t level;
-    struct rt_lwp *lwp;
-    int ret = -RT_EINVAL;
-    lwp_sigset_t newset;
+    lwp_siginfo_t siginfo;
+    rt_bool_t terminated;
+    rt_bool_t need_schedule;
 
-    level = rt_hw_interrupt_disable();
-    lwp = (struct rt_lwp*)rt_thread_self()->lwp;
-    if (!lwp)
-    {
-        goto out;
-    }
-    if (sigsetsize != sizeof(lwp_sigset_t))
-    {
-        goto out;
-    }
-    if (!act && !oact)
-    {
-        goto out;
-    }
-    if (oact)
+    /** must be able to be suspended */
+    RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
+
+    if (!lwp || signo < 0 || signo >= _LWP_NSIG)
     {
-        oact->sa_flags = lwp->sa_flags;
-        oact->sa_mask = lwp->signal_mask;
-        oact->sa_restorer = RT_NULL;
-        oact->__sa_handler._sa_handler = lwp->signal_handler[sig - 1];
+        ret = -RT_EINVAL;
     }
-    if (act)
+    else
     {
-        lwp->sa_flags = act->sa_flags;
-        newset = act->sa_mask;
-        lwp_sigdelset(&newset, SIGKILL);
-        lwp_sigdelset(&newset, SIGSTOP);
-        lwp->signal_mask = newset;
-        lwp_sighandler_set(sig, act->__sa_handler._sa_handler);
+        need_schedule = RT_FALSE;
+
+        /* FIXME: acquire READ lock to lwp */
+        level = rt_hw_interrupt_disable();
+        terminated = lwp->terminated;
+
+        /* short-circuit code for inactive task, ignored signals */
+        if (terminated || _sighandler_is_ignored(lwp, signo))
+        {
+            ret = 0;
+        }
+        else
+        {
+            siginfo = siginfo_create(signo, code, value);
+
+            if (siginfo)
+            {
+                need_schedule = _siginfo_deliver_to_lwp(lwp, siginfo);
+                ret = 0;
+            }
+            else
+            {
+                LOG_I("%s: siginfo malloc failed", __func__);
+                ret = -RT_ENOMEM;
+            }
+        }
+
+        rt_hw_interrupt_enable(level);
+
+        if (need_schedule)
+            rt_schedule();
     }
-    ret = 0;
-out:
-    rt_hw_interrupt_enable(level);
     return ret;
 }
 
-rt_inline void sigorsets(lwp_sigset_t *dset, const lwp_sigset_t *set0, const lwp_sigset_t *set1)
+static void _signal_action_flag_k2u(int signo, struct lwp_signal *signal, struct lwp_sigaction *act)
 {
-    switch (_LWP_NSIG_WORDS)
-    {
-        case 4:
-            dset->sig[3] = set0->sig[3] | set1->sig[3];
-            dset->sig[2] = set0->sig[2] | set1->sig[2];
-        case 2:
-            dset->sig[1] = set0->sig[1] | set1->sig[1];
-        case 1:
-            dset->sig[0] = set0->sig[0] | set1->sig[0];
-        default:
-            return;
-    }
+    long flags = 0;
+    if (_sigismember(&signal->sig_action_nodefer, signo))
+        flags |= SA_NODEFER;
+    if (_sigismember(&signal->sig_action_onstack, signo))
+        flags |= SA_ONSTACK;
+    if (_sigismember(&signal->sig_action_restart, signo))
+        flags |= SA_RESTART;
+    if (_sigismember(&signal->sig_action_siginfo, signo))
+        flags |= SA_SIGINFO;
+
+    act->sa_flags = flags;
 }
 
-rt_inline void sigandsets(lwp_sigset_t *dset, const lwp_sigset_t *set0, const lwp_sigset_t *set1)
+static void _signal_action_flag_u2k(int signo, struct lwp_signal *signal, const struct lwp_sigaction *act)
 {
-    switch (_LWP_NSIG_WORDS)
-    {
-        case 4:
-            dset->sig[3] = set0->sig[3] & set1->sig[3];
-            dset->sig[2] = set0->sig[2] & set1->sig[2];
-        case 2:
-            dset->sig[1] = set0->sig[1] & set1->sig[1];
-        case 1:
-            dset->sig[0] = set0->sig[0] & set1->sig[0];
-        default:
-            return;
-    }
+    long flags = act->sa_flags;
+    if (flags & SA_NODEFER)
+        _sigaddset(&signal->sig_action_nodefer, signo);
+    if (flags & SA_ONSTACK)
+        _sigaddset(&signal->sig_action_onstack, signo);
+    if (flags & SA_RESTART)
+        _sigaddset(&signal->sig_action_restart, signo);
+    if (flags & SA_SIGINFO)
+        _sigaddset(&signal->sig_action_siginfo, signo);
 }
 
-int lwp_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset)
+rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
+                           const struct lwp_sigaction *restrict act,
+                           struct lwp_sigaction *restrict oact)
 {
-    int ret = -1;
+    lwp_sighandler_t prev_handler;
+    lwp_sigqueue_t thread_sigq;
+    rt_list_t *thread_list;
+    rt_err_t ret = RT_EOK;
     rt_base_t level;
-    struct rt_lwp *lwp;
-    struct rt_thread *thread;
-    lwp_sigset_t newset;
 
-    level = rt_hw_interrupt_disable();
-
-    thread = rt_thread_self();
-    lwp = (struct rt_lwp*)thread->lwp;
-    if (!lwp)
-    {
-        goto out;
-    }
-    if (oset)
+    if (lwp)
     {
-        rt_memcpy(oset, &lwp->signal_mask, sizeof(lwp_sigset_t));
-    }
+        /** acquire READ access to lwp */
+        level = rt_hw_interrupt_disable();
 
-    if (sigset)
-    {
-        switch (how)
+        if (oact)
         {
-            case SIG_BLOCK:
-                sigorsets(&newset, &lwp->signal_mask, sigset);
-                break;
-            case SIG_UNBLOCK:
-                sigandsets(&newset, &lwp->signal_mask, sigset);
-                break;
-            case SIG_SETMASK:
-                newset = *sigset;
-                break;
-            default:
+            oact->sa_mask = lwp->signal.sig_action_mask[signo - 1];
+            oact->__sa_handler._sa_handler = lwp->signal.sig_action[signo - 1];
+            oact->sa_restorer = RT_NULL;
+            _signal_action_flag_k2u(signo, &lwp->signal, oact);
+        }
+        if (act)
+        {
+            /**
+             * @note POSIX.1-2017 requires calls to sigaction() that supply a NULL act
+             * argument succeed, even in the case of signals that cannot be caught or ignored
+             */
+            if (_sighandler_cannot_caught(lwp, signo))
                 ret = -RT_EINVAL;
-                goto out;
+            else
+            {
+                prev_handler = _get_sighandler_locked(lwp, signo);
+                lwp->signal.sig_action_mask[signo - 1] = act->sa_mask;
+                if (act->__sa_handler._sa_handler == SIG_IGN)
+                    lwp->signal.sig_action[signo - 1] = LWP_SIG_ACT_IGN;
+                else
+                    lwp->signal.sig_action[signo - 1] = act->__sa_handler._sa_handler;
+
+                _signal_action_flag_u2k(signo, &lwp->signal, act);
+
+                /**
+                 * @brief Discard the pending signal if signal action is set to SIG_IGN
+                 *
+                 * @note POSIX.1-2017: Setting a signal action to SIG_IGN for a signal
+                 * that is pending shall cause the pending signal to be discarded,
+                 * whether or not it is blocked.
+                 */
+                if (prev_handler != LWP_SIG_ACT_IGN &&
+                    _get_sighandler_locked(lwp, signo) == LWP_SIG_ACT_IGN)
+                {
+                    sigqueue_discard(_SIGQ(lwp), signo);
+                    for (thread_list = lwp->t_grp.next;
+                         thread_list != &lwp->t_grp;
+                         thread_list = thread_list->next)
+                    {
+                        thread_sigq = _SIGQ(rt_list_entry(thread_list, struct rt_thread, sibling));
+                        sigqueue_discard(thread_sigq, signo);
+                    }
+                }
+            }
         }
 
-        lwp_sigdelset(&newset, SIGKILL);
-        lwp_sigdelset(&newset, SIGSTOP);
-
-        lwp->signal_mask = newset;
+        rt_hw_interrupt_enable(level);
     }
-    ret = 0;
-out:
-    rt_hw_interrupt_enable(level);
+    else
+        ret = -RT_EINVAL;
+
     return ret;
 }
 
-int lwp_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset)
+rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long value)
 {
+    rt_err_t ret = -1;
     rt_base_t level;
-    struct rt_thread *thread;
-    lwp_sigset_t newset;
+    struct rt_lwp *lwp;
+    lwp_siginfo_t siginfo;
+    rt_bool_t need_schedule;
 
-    level = rt_hw_interrupt_disable();
-    thread = rt_thread_self();
+    /** must be able to be suspended */
+    RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
 
-    if (oset)
+    if (!thread || signo < 0 || signo >= _LWP_NSIG)
     {
-        rt_memcpy(oset, &thread->signal_mask, sizeof(lwp_sigset_t));
+        ret = -RT_EINVAL;
     }
-
-    if (sigset)
+    else
     {
-        switch (how)
+        lwp = thread->lwp;
+        need_schedule = RT_FALSE;
+
+        RT_ASSERT(lwp);
+
+        /* FIXME: acquire READ lock to lwp */
+        level = rt_hw_interrupt_disable();
+
+        if (!lwp)
+            ret = -RT_EPERM;
+        else if (lwp->terminated || _sighandler_is_ignored(lwp, signo))
+            ret = 0;
+        else
         {
-            case SIG_BLOCK:
-                sigorsets(&newset, &thread->signal_mask, sigset);
-                break;
-            case SIG_UNBLOCK:
-                sigandsets(&newset, &thread->signal_mask, sigset);
-                break;
-            case SIG_SETMASK:
-                newset = *sigset;
-                break;
-            default:
-                goto out;
+            siginfo = siginfo_create(signo, code, value);
+
+            if (siginfo)
+            {
+                need_schedule = _siginfo_deliver_to_thread(lwp, thread, siginfo);
+                ret = 0;
+            }
+            else
+            {
+                LOG_I("%s: siginfo malloc failed", __func__);
+                ret = -RT_ENOMEM;
+            }
         }
 
-        lwp_sigdelset(&newset, SIGKILL);
-        lwp_sigdelset(&newset, SIGSTOP);
+        rt_hw_interrupt_enable(level);
 
-        thread->signal_mask = newset;
+        if (need_schedule)
+            rt_schedule();
     }
-out:
+
+    return ret;
+}
+
+#ifndef ARCH_MM_MMU
+void lwp_thread_sighandler_set(int sig, lwp_sighandler_t func)
+{
+    rt_base_t level;
+
+    if (sig == 0 || sig > _LWP_NSIG)
+        return;
+    level = rt_hw_interrupt_disable();
+    rt_thread_self()->signal_handler[sig - 1] = func;
     rt_hw_interrupt_enable(level);
-    return 0;
 }
+#endif
 
-static void _do_signal_wakeup(rt_thread_t thread, int sig)
+rt_err_t lwp_thread_signal_mask(rt_thread_t thread, lwp_sig_mask_cmd_t how,
+                                const lwp_sigset_t *sigset, lwp_sigset_t *oset)
 {
-    if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)
+    rt_err_t ret = -1;
+    rt_base_t level;
+    struct rt_lwp *lwp;
+
+    if (thread)
     {
-        int need_schedule = 1;
+        /** FIXME: acquire READ access to rt_thread */
+        level = rt_hw_interrupt_disable();
 
-        if ((thread->stat & RT_SIGNAL_COMMON_WAKEUP_MASK) != RT_SIGNAL_COMMON_WAKEUP_MASK)
-        {
-            rt_thread_wakeup(thread);
-        }
-        else if ((sig == SIGKILL) && ((thread->stat & RT_SIGNAL_KILL_WAKEUP_MASK) != RT_SIGNAL_KILL_WAKEUP_MASK))
+        lwp = (struct rt_lwp*)thread->lwp;
+        if (!lwp)
         {
-            rt_thread_wakeup(thread);
+            ret = -RT_EPERM;
         }
         else
         {
-            need_schedule = 0;
+            ret = 0;
+            _thread_signal_mask(thread, how, sigset, oset);
         }
 
-        /* do schedule */
-        if (need_schedule)
-        {
-            rt_schedule();
-        }
+        rt_hw_interrupt_enable(level);
     }
+    else
+        ret = -RT_EINVAL;
+
+    return ret;
 }
 
-int lwp_kill(pid_t pid, int sig)
+static int _dequeue_signal(rt_thread_t thread, lwp_sigset_t *mask, siginfo_t *usi)
 {
-    rt_base_t level;
+    int signo;
+    lwp_siginfo_t si;
     struct rt_lwp *lwp;
-    int ret = -1;
-    rt_thread_t thread;
+    lwp_sigset_t *pending;
 
-    if (sig < 0 || sig >= _LWP_NSIG)
-    {
-        rt_set_errno(EINVAL);
-        return ret;
-    }
-    level = rt_hw_interrupt_disable();
-    lwp = lwp_from_pid(pid);
-    if (!lwp || lwp->finish)
+    pending = &_SIGQ(thread)->sigset_pending;
+    signo = _next_signal(pending, mask);
+    if (!signo)
     {
-        rt_set_errno(ESRCH);
-        goto out;
+        lwp = thread->lwp;
+        RT_ASSERT(lwp);
+        pending = &_SIGQ(lwp)->sigset_pending;
+        signo = _next_signal(pending, mask);
     }
-    if (sig)
-    {
-        /* check main thread */
-        thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
-        if (!lwp_sigismember(&lwp->signal_mask, sig)) /* if signal masked */
-        {
-            lwp_sigaddset(&lwp->signal, sig);
-            _do_signal_wakeup(thread, sig);
-        }
-    }
-    ret = 0;
-out:
-    rt_hw_interrupt_enable(level);
-    return ret;
+
+    if (!signo)
+        return signo;
+
+    si = sigqueue_dequeue(_SIGQ(thread), signo);
+    RT_ASSERT(!!si);
+
+    siginfo_k2u(si, usi);
+    siginfo_delete(si);
+
+    return signo;
 }
 
-int lwp_thread_kill(rt_thread_t thread, int sig)
+rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
+                                     siginfo_t *usi, struct timespec *timeout)
 {
+    LOG_D("%s", __func__);
+
     rt_base_t level;
-    int ret = -RT_EINVAL;
+    rt_err_t ret;
+    int sig;
+
+    /**
+     * @brief POSIX
+     * If one of the signals in set is already pending for the calling thread,
+     * sigwaitinfo() will return immediately
+     */
 
-    if (!thread)
+    /* Create a mask of signals user dont want or cannot catch */
+    _sigdelset(sigset, SIGKILL);
+    _sigdelset(sigset, SIGSTOP);
+    _signotsets(sigset, sigset);
+
+    /* FIXME: acquire READ lock to lwp */
+    level = rt_hw_interrupt_disable();
+    sig = _dequeue_signal(thread, sigset, usi);
+    rt_hw_interrupt_enable(level);
+    if (sig)
+        return sig;
+
+    /* WARNING atomic problem, what if pending signal arrives before we sleep */
+
+    /**
+     * @brief POSIX
+     * if none of the signals specified by set are pending, sigtimedwait() shall
+     * wait for the time interval specified in the timespec structure referenced
+     * by timeout.
+     */
+    if (timeout)
     {
-        rt_set_errno(ESRCH);
-        return ret;
+        /* TODO: verify timeout valid ? not overflow 32bits, nanosec valid, ... */
+        rt_uint32_t time;
+        time = rt_timespec_to_tick(timeout);
+
+        /**
+         * @brief POSIX
+         * If the timespec structure pointed to by timeout is zero-valued and
+         * if none of the signals specified by set are pending, then
+         * sigtimedwait() shall return immediately with an error
+         */
+        if (time == 0)
+            return -EAGAIN;
+
+        ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE);
+        rt_timer_control(&(thread->thread_timer),
+                         RT_TIMER_CTRL_SET_TIME,
+                         &timeout);
+        rt_timer_start(&(thread->thread_timer));
+
     }
-    if (sig < 0 || sig >= _LWP_NSIG)
+    else
     {
-        rt_set_errno(EINVAL);
-        return ret;
+        /* suspend kernel forever until signal was received */
+        ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE);
     }
-    level = rt_hw_interrupt_disable();
-    if (!thread->lwp)
+
+    if (ret == RT_EOK)
     {
-        rt_set_errno(EPERM);
-        goto out;
+        rt_schedule();
+        ret = -EAGAIN;
     }
-    if (!lwp_sigismember(&thread->signal_mask, sig)) /* if signal masked */
+    /* else ret == -EINTR */
+
+    /* FIXME: acquire READ lock to lwp */
+    level = rt_hw_interrupt_disable();
+    sig = _dequeue_signal(thread, sigset, usi);
+    rt_hw_interrupt_enable(level);
+
+    return sig ? sig : ret;
+}
+
+void lwp_thread_signal_pending(rt_thread_t thread, lwp_sigset_t *pending)
+{
+    struct rt_lwp *lwp;
+    lwp = thread->lwp;
+
+    if (lwp)
     {
-        lwp_sigaddset(&thread->signal, sig);
-        _do_signal_wakeup(thread, sig);
+        memset(pending, 0, sizeof(*pending));
+        sigqueue_examine(_SIGQ(thread), pending);
+        sigqueue_examine(_SIGQ(lwp), pending);
     }
-    ret = 0;
-out:
-    rt_hw_interrupt_enable(level);
-    return ret;
 }

+ 152 - 15
components/lwp/lwp_signal.h

@@ -1,39 +1,176 @@
 /*
- * Copyright (c) 2006-2020, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
- * 2020-02-23     Jesven         first version.
+ * 2020-02-23     Jesven       first version.
+ * 2023-07-06     Shell        update the generation, pending and delivery API
  */
 
-#ifndef LWP_SIGNAL_H__
-#define LWP_SIGNAL_H__
+#ifndef __LWP_SIGNAL_H__
+#define __LWP_SIGNAL_H__
+
+#include "syscall_generic.h"
 
 #include <rtthread.h>
+#include <sys/signal.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-int lwp_signal_check(void);
-int lwp_signal_backup(void *user_sp, void *user_pc, void* user_flag);
-struct rt_user_context *lwp_signal_restore(void);
-lwp_sighandler_t lwp_sighandler_get(int sig);
-void lwp_sighandler_set(int sig, lwp_sighandler_t func);
+#define _USIGNAL_SIGMASK(signo) (1u << ((signo)-1))
+#define LWP_SIG_IGNORE_SET      (_USIGNAL_SIGMASK(SIGCHLD) | _USIGNAL_SIGMASK(SIGURG))
+#define LWP_SIG_ACT_DFL         ((lwp_sighandler_t)0)
+#define LWP_SIG_ACT_IGN         ((lwp_sighandler_t)1)
+#define LWP_SIG_USER_SA_FLAGS                                               \
+    (SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART |   \
+     SA_NODEFER | SA_RESETHAND | SA_EXPOSE_TAGBITS)
+
+typedef enum {
+    LWP_SIG_MASK_CMD_BLOCK,
+    LWP_SIG_MASK_CMD_UNBLOCK,
+    LWP_SIG_MASK_CMD_SET_MASK,
+    __LWP_SIG_MASK_CMD_WATERMARK
+} lwp_sig_mask_cmd_t;
+
+/**
+ * LwP implementation of POSIX signal
+ */
+struct lwp_signal {
+    struct rt_mutex sig_lock;
+
+    struct lwp_sigqueue sig_queue;
+    rt_thread_t sig_dispatch_thr[_LWP_NSIG];
+
+    lwp_sighandler_t sig_action[_LWP_NSIG];
+    lwp_sigset_t sig_action_mask[_LWP_NSIG];
+
+    lwp_sigset_t sig_action_nodefer;
+    lwp_sigset_t sig_action_onstack;
+    lwp_sigset_t sig_action_restart;
+    lwp_sigset_t sig_action_siginfo;
+};
+
+struct rt_lwp;
+
 #ifndef ARCH_MM_MMU
+void lwp_sighandler_set(int sig, lwp_sighandler_t func);
 void lwp_thread_sighandler_set(int sig, lwp_sighandler_t func);
 #endif
-int lwp_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset);
-int lwp_sigaction(int sig, const struct lwp_sigaction *act, struct lwp_sigaction * oact, size_t sigsetsize);
-int lwp_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset);
 
-int lwp_kill(pid_t pid, int sig);
-int lwp_thread_kill(rt_thread_t thread, int sig);
+rt_inline void lwp_sigqueue_init(lwp_sigqueue_t sigq)
+{
+    rt_memset(&sigq->sigset_pending, 0, sizeof(lwp_sigset_t));
+    rt_list_init(&sigq->siginfo_list);
+}
+
+/**
+ * @brief release the signal queue
+ *
+ * @param sigq target signal queue
+ */
+void lwp_sigqueue_clear(lwp_sigqueue_t sigq);
+
+rt_err_t lwp_signal_init(struct lwp_signal *sig);
+
+rt_err_t lwp_signal_detach(struct lwp_signal *signal);
+
+rt_inline void lwp_thread_signal_detach(struct lwp_thread_signal *tsig)
+{
+    lwp_sigqueue_clear(&tsig->sig_queue);
+}
+
+/**
+ * @brief send a signal to the process
+ *
+ * @param lwp the process to be killed
+ * @param signo the signal number
+ * @param code as in siginfo
+ * @param value as in siginfo
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as successful
+ *
+ * @note the *signal_kill have the same definition of a successful return as
+ *       kill() in IEEE Std 1003.1-2017
+ */
+rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value);
+
+/**
+ * @brief set or examine the signal action of signo
+ *
+ * @param signo signal number
+ * @param act the signal action
+ * @param oact the old signal action
+ * @return rt_err_t
+ */
+rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
+                           const struct lwp_sigaction *restrict act,
+                           struct lwp_sigaction *restrict oact);
+
+/**
+ * @brief send a signal to the thread
+ *
+ * @param thread target thread
+ * @param signo the signal number
+ * @param code as in siginfo
+ * @param value as in siginfo
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as successful
+ */
+rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long value);
+
+/**
+ * @brief set signal mask of target thread
+ *
+ * @param thread the target thread
+ * @param how command
+ * @param sigset operand
+ * @param oset the address to old set
+ * @return rt_err_t
+ */
+rt_err_t lwp_thread_signal_mask(rt_thread_t thread, lwp_sig_mask_cmd_t how,
+                                const lwp_sigset_t *sigset, lwp_sigset_t *oset);
+
+/**
+ * @brief Catch signal if exists and no return, otherwise return with no side effect
+ *
+ * @param exp_frame the exception frame on kernel stack
+ */
+void lwp_thread_signal_catch(void *exp_frame);
+
+/**
+ * @brief Check if it's okay to suspend for current lwp thread
+ *
+ * @param thread target thread
+ * @param suspend_flag suspend flag of target thread
+ * @return int 1 if can be suspended, otherwise not
+ */
+int lwp_thread_signal_suspend_check(rt_thread_t thread, int suspend_flag);
+
+/**
+ * @brief Asynchronously wait for signal
+ *
+ * @param thread target thread
+ * @param sigset the signals to be waited
+ * @param info address of user siginfo
+ * @param timeout timeout of waiting
+ * @return rt_err_t
+ */
+rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
+                                     siginfo_t *usi, struct timespec *timeout);
+
+/**
+ * @brief Examine the set of signals that are blocked from delivery to the
+ * calling thread and that are pending on the process or the calling thread
+ *
+ * @param thread target thread
+ * @param sigset where mask of pending signals is returned
+ */
+void lwp_thread_signal_pending(rt_thread_t thread, lwp_sigset_t *sigset);
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif
+#endif /* __LWP_SIGNAL_H__ */

+ 217 - 53
components/lwp/lwp_syscall.c

@@ -11,8 +11,11 @@
  * 2021-02-12     lizhirui     add 64-bit support for sys_brk
  * 2021-02-20     lizhirui     fix some warnings
  * 2023-03-13     WangXiaoyao  Format & fix syscall return value
+ * 2023-07-06     Shell        adapt the signal API, and clone, fork to new implementation of lwp signal
  */
+
 #define _GNU_SOURCE
+
 /* RT-Thread System call */
 #include <rtthread.h>
 #include <rthw.h>
@@ -28,6 +31,7 @@
 #include "syscall_generic.h"
 
 #include <lwp.h>
+#include "lwp_signal.h"
 #ifdef ARCH_MM_MMU
 #include <lwp_user_mm.h>
 #include <lwp_arch.h>
@@ -1007,11 +1011,80 @@ sysret_t sys_exec(char *filename, int argc, char **argv, char **envp)
     return lwp_execve(filename, 0, argc, argv, envp);
 }
 
-sysret_t sys_kill(int pid, int sig)
+sysret_t sys_kill(int pid, int signo)
 {
-    int ret = 0;
-    ret = lwp_kill(pid, sig);
-    return (ret < 0 ? GET_ERRNO() : ret);
+    rt_err_t kret;
+    sysret_t sysret;
+    rt_base_t level;
+    struct rt_lwp *lwp;
+
+    /* handling the semantics of sys_kill */
+    if (pid > 0)
+    {
+        /* TODO: lock the lwp strcut */
+        level = rt_hw_interrupt_disable();
+        lwp = lwp_from_pid(pid);
+
+        /* lwp_signal_kill() can handle NULL lwp */
+        if (lwp)
+            kret = lwp_signal_kill(lwp, signo, SI_USER, 0);
+        else
+            kret = -RT_ENOENT;
+
+        rt_hw_interrupt_enable(level);
+    }
+    else if (pid == 0)
+    {
+        /**
+         * sig shall be sent to all processes (excluding an unspecified set
+         * of system processes) whose process group ID is equal to the process
+         * group ID of the sender, and for which the process has permission to
+         * send a signal.
+         */
+        kret = -RT_ENOSYS;
+    }
+    else if (pid == -1)
+    {
+        /**
+         * sig shall be sent to all processes (excluding an unspecified set
+         * of system processes) for which the process has permission to send
+         * that signal.
+         */
+        kret = -RT_ENOSYS;
+    }
+    else /* pid < -1 */
+    {
+        /**
+         * sig shall be sent to all processes (excluding an unspecified set
+         * of system processes) whose process group ID is equal to the absolute
+         * value of pid, and for which the process has permission to send a signal.
+         */
+        kret = -RT_ENOSYS;
+    }
+
+    switch (kret)
+    {
+        case -RT_ENOENT:
+            sysret = -ESRCH;
+            break;
+        case -RT_EINVAL:
+            sysret = -EINVAL;
+            break;
+        case -RT_ENOSYS:
+            sysret = -ENOSYS;
+            break;
+
+        /**
+         * kill() never returns ENOMEM, so return normally to caller.
+         * IEEE Std 1003.1-2017 says the kill() function is successful
+         * if the process has permission to send sig to any of the
+         * processes specified by pid.
+         */
+        case -RT_ENOMEM:
+        default:
+            sysret = 0;
+    }
+    return sysret;
 }
 
 sysret_t sys_getpid(void)
@@ -1500,40 +1573,9 @@ fail:
     }
     return RT_NULL;
 }
+
 #ifdef ARCH_MM_MMU
-#define CLONE_VM    0x00000100
-#define CLONE_FS    0x00000200
-#define CLONE_FILES 0x00000400
-#define CLONE_SIGHAND   0x00000800
-#define CLONE_PTRACE    0x00002000
-#define CLONE_VFORK 0x00004000
-#define CLONE_PARENT    0x00008000
-#define CLONE_THREAD    0x00010000
-#define CLONE_NEWNS 0x00020000
-#define CLONE_SYSVSEM   0x00040000
-#define CLONE_SETTLS    0x00080000
-#define CLONE_PARENT_SETTID 0x00100000
-#define CLONE_CHILD_CLEARTID    0x00200000
-#define CLONE_DETACHED  0x00400000
-#define CLONE_UNTRACED  0x00800000
-#define CLONE_CHILD_SETTID  0x01000000
-#define CLONE_NEWCGROUP 0x02000000
-#define CLONE_NEWUTS    0x04000000
-#define CLONE_NEWIPC    0x08000000
-#define CLONE_NEWUSER   0x10000000
-#define CLONE_NEWPID    0x20000000
-#define CLONE_NEWNET    0x40000000
-#define CLONE_IO    0x80000000
-
-/* arg[] -> flags
- *          stack
- *          new_tid
- *          tls
- *          set_clear_tid_address
- *          quit_func
- *          start_args
- *          */
-#define SYS_CLONE_ARGS_NR 7
+#include "lwp_clone.h"
 
 long _sys_clone(void *arg[])
 {
@@ -1687,9 +1729,12 @@ static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src)
     dst->tty = src->tty;
     rt_memcpy(dst->cmd, src->cmd, RT_NAME_MAX);
 
-    dst->sa_flags = src->sa_flags;
-    dst->signal_mask = src->signal_mask;
-    rt_memcpy(dst->signal_handler, src->signal_handler, sizeof dst->signal_handler);
+    rt_memcpy(&dst->signal.sig_action, &src->signal.sig_action, sizeof(dst->signal.sig_action));
+    rt_memcpy(&dst->signal.sig_action_mask, &src->signal.sig_action_mask, sizeof(dst->signal.sig_action_mask));
+    rt_memcpy(&dst->signal.sig_action_nodefer, &src->signal.sig_action_nodefer, sizeof(dst->signal.sig_action_nodefer));
+    rt_memcpy(&dst->signal.sig_action_onstack, &src->signal.sig_action_onstack, sizeof(dst->signal.sig_action_onstack));
+    rt_memcpy(&dst->signal.sig_action_restart, &dst->signal.sig_action_restart, sizeof(dst->signal.sig_action_restart));
+    rt_memcpy(&dst->signal.sig_action_siginfo, &dst->signal.sig_action_siginfo, sizeof(dst->signal.sig_action_siginfo));
     rt_strcpy(dst->working_directory, src->working_directory);
 }
 
@@ -1797,7 +1842,7 @@ sysret_t _sys_fork(void)
     thread->user_entry = self_thread->user_entry;
     thread->user_stack = self_thread->user_stack;
     thread->user_stack_size = self_thread->user_stack_size;
-    thread->signal_mask = self_thread->signal_mask;
+    thread->signal.sigset_mask = self_thread->signal.sigset_mask;
     thread->thread_idr = self_thread->thread_idr;
     thread->lwp = (void *)lwp;
     thread->tid = tid;
@@ -2457,12 +2502,11 @@ sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
 
         _swap_lwp_data(lwp, new_lwp, void *, args);
 
-        rt_memset(&thread->signal_mask, 0, sizeof(thread->signal_mask));
-        rt_memset(&thread->signal_mask_bak, 0, sizeof(thread->signal_mask_bak));
-        lwp->sa_flags = 0;
-        rt_memset(&lwp->signal_mask, 0, sizeof(lwp->signal_mask));
-        rt_memset(&lwp->signal_mask_bak, 0, sizeof(lwp->signal_mask_bak));
-        rt_memset(lwp->signal_handler, 0, sizeof(lwp->signal_handler));
+        lwp_thread_signal_detach(&thread->signal);
+        rt_memset(&thread->signal.sigset_mask, 0, sizeof(thread->signal.sigset_mask));
+
+        lwp_signal_detach(&lwp->signal);
+        lwp_signal_init(&lwp->signal);
 
         /* to do: clsoe files with flag CLOEXEC */
 
@@ -2470,6 +2514,9 @@ sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
 
         rt_hw_interrupt_enable(level);
 
+        /* setup the signal for the dummy lwp, so that is can be smoothly recycled */
+        lwp_signal_init(&new_lwp->signal);
+
         lwp_ref_dec(new_lwp);
         arch_start_umode(lwp->args,
                 lwp->text_entry,
@@ -3267,9 +3314,10 @@ struct k_sigaction {
 };
 
 sysret_t sys_sigaction(int sig, const struct k_sigaction *act,
-                     struct k_sigaction *oact, size_t sigsetsize)
+                       struct k_sigaction *oact, size_t sigsetsize)
 {
     int ret = -RT_EINVAL;
+    struct rt_lwp *lwp;
     struct lwp_sigaction kact, *pkact = RT_NULL;
     struct lwp_sigaction koact, *pkoact = RT_NULL;
 
@@ -3310,7 +3358,9 @@ sysret_t sys_sigaction(int sig, const struct k_sigaction *act,
         pkact = &kact;
     }
 
-    ret = lwp_sigaction(sig, pkact, pkoact, sigsetsize);
+    lwp = lwp_self();
+    RT_ASSERT(lwp);
+    ret = lwp_signal_action(lwp, sig, pkact, pkoact);
 #ifdef ARCH_MM_MMU
     if (ret == 0 && oact)
     {
@@ -3324,6 +3374,12 @@ out:
     return (ret < 0 ? GET_ERRNO() : ret);
 }
 
+static int mask_command_u2k[] = {
+    [SIG_BLOCK] = LWP_SIG_MASK_CMD_BLOCK,
+    [SIG_UNBLOCK] = LWP_SIG_MASK_CMD_UNBLOCK,
+    [SIG_SETMASK] = LWP_SIG_MASK_CMD_SET_MASK,
+};
+
 sysret_t sys_sigprocmask(int how, const sigset_t *sigset, sigset_t *oset, size_t size)
 {
     int ret = -1;
@@ -3377,7 +3433,7 @@ sysret_t sys_sigprocmask(int how, const sigset_t *sigset, sigset_t *oset, size_t
         pnewset = (lwp_sigset_t *)sigset;
 #endif /* ARCH_MM_MMU */
     }
-    ret = lwp_sigprocmask(how, pnewset, poldset);
+    ret = lwp_thread_signal_mask(rt_thread_self(), mask_command_u2k[how], pnewset, poldset);
 #ifdef ARCH_MM_MMU
     if (ret < 0)
     {
@@ -3391,6 +3447,97 @@ sysret_t sys_sigprocmask(int how, const sigset_t *sigset, sigset_t *oset, size_t
     return (ret < 0 ? -EFAULT: ret);
 }
 
+sysret_t sys_sigpending(sigset_t *sigset, size_t sigsize)
+{
+    sysret_t ret = 0;
+    lwp_sigset_t lwpset;
+
+    /* Verify and Get sigset, timeout */
+    if (!sigset || !lwp_user_accessable((void *)sigset, sigsize))
+    {
+        ret = -EFAULT;
+    }
+    else
+    {
+        /* Fit sigset size to lwp set */
+        if (sizeof(lwpset) < sigsize)
+        {
+            LOG_I("%s: sigsize (%lx) extends lwp sigset chunk\n", __func__, sigsize);
+            sigsize = sizeof(lwpset);
+        }
+
+        lwp_thread_signal_pending(rt_thread_self(), &lwpset);
+
+        if (!lwp_put_to_user(sigset, &lwpset, sigsize))
+            RT_ASSERT(0);   /* should never happened */
+    }
+
+    return ret;
+}
+
+sysret_t sys_sigtimedwait(const sigset_t *sigset, siginfo_t *info, const struct timespec *timeout, size_t sigsize)
+{
+    int sig;
+    size_t ret;
+    lwp_sigset_t lwpset;
+    siginfo_t kinfo;
+    struct timespec ktimeout;
+    struct timespec *ptimeout;
+
+    /* Fit sigset size to lwp set */
+    if (sizeof(lwpset) < sigsize)
+    {
+        LOG_I("%s: sigsize (%lx) extends lwp sigset chunk\n", __func__, sigsize);
+        sigsize = sizeof(lwpset);
+    }
+    else
+    {
+        memset(&lwpset, 0, sizeof(lwpset));
+    }
+
+    /* Verify and Get sigset, timeout */
+    if (!sigset || !lwp_user_accessable((void *)sigset, sigsize))
+    {
+        return -EFAULT;
+    }
+    else
+    {
+        ret = lwp_get_from_user(&lwpset, (void *)sigset, sigsize);
+        RT_ASSERT(ret == sigsize);
+    }
+
+    if (timeout)
+    {
+        if (!lwp_user_accessable((void *)timeout, sizeof(*timeout)))
+            return -EFAULT;
+        else
+        {
+            ret = lwp_get_from_user(&ktimeout, (void *)timeout, sizeof(*timeout));
+            ptimeout = &ktimeout;
+            RT_ASSERT(ret == sizeof(*timeout));
+        }
+    }
+    else
+    {
+        ptimeout = RT_NULL;
+    }
+
+    sig = lwp_thread_signal_timedwait(rt_thread_self(), &lwpset, &kinfo, ptimeout);
+
+    if (info)
+    {
+        if (!lwp_user_accessable((void *)info, sizeof(*info)))
+            return -EFAULT;
+        else
+        {
+            ret = lwp_put_to_user(info, &kinfo, sizeof(*info));
+            RT_ASSERT(ret == sizeof(*info));
+        }
+    }
+
+    return sig;
+}
+
 sysret_t sys_tkill(int tid, int sig)
 {
 #ifdef ARCH_MM_MMU
@@ -3400,7 +3547,7 @@ sysret_t sys_tkill(int tid, int sig)
 
     level = rt_hw_interrupt_disable();
     thread = lwp_tid_get_thread(tid);
-    ret =  lwp_thread_kill(thread, sig);
+    ret = lwp_thread_signal_kill(thread, sig, SI_USER, 0);
     rt_hw_interrupt_enable(level);
     return ret;
 #else
@@ -3461,7 +3608,7 @@ sysret_t sys_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_
         pnewset = (lwp_sigset_t *)sigset;
 #endif
     }
-    ret = lwp_thread_sigprocmask(how, pnewset, poldset);
+    ret = lwp_thread_signal_mask(rt_thread_self(), mask_command_u2k[how], pnewset, poldset);
     if (ret < 0)
     {
         return ret;
@@ -5169,7 +5316,7 @@ sysret_t sys_symlink(const char *existing, const char *new)
     return (ret < 0 ? GET_ERRNO() : ret);
 }
 
-const static struct rt_syscall_def func_table[] =
+static const struct rt_syscall_def func_table[] =
 {
     SYSCALL_SIGN(sys_exit),            /* 01 */
     SYSCALL_SIGN(sys_read),
@@ -5391,6 +5538,15 @@ const static struct rt_syscall_def func_table[] =
     SYSCALL_SIGN(sys_symlink),
     SYSCALL_SIGN(sys_getaffinity),                      /* 180 */
     SYSCALL_SIGN(sys_sysinfo),
+    SYSCALL_SIGN(sys_notimpl),
+    SYSCALL_SIGN(sys_notimpl),
+    SYSCALL_SIGN(sys_notimpl),
+    SYSCALL_SIGN(sys_notimpl),                          /* 185 */
+    SYSCALL_SIGN(sys_notimpl),
+    SYSCALL_SIGN(sys_sigpending),
+    SYSCALL_SIGN(sys_sigtimedwait),
+    SYSCALL_SIGN(sys_notimpl),
+    SYSCALL_SIGN(sys_notimpl),                          /* 190 */
 };
 
 const void *lwp_get_sys_api(rt_uint32_t number)
@@ -5408,6 +5564,10 @@ const void *lwp_get_sys_api(rt_uint32_t number)
         {
             func = func_table[number].func;
         }
+        else
+        {
+            LOG_I("Unimplement syscall %d", number);
+        }
     }
 
     return func;
@@ -5428,6 +5588,10 @@ const char *lwp_get_syscall_name(rt_uint32_t number)
         {
             name = (char*)func_table[number].name;
         }
+        else
+        {
+            LOG_I("Unimplement syscall %d", number);
+        }
     }
 
     // skip sys_

+ 95 - 68
include/rtdef.h

@@ -218,6 +218,7 @@ typedef __gnuc_va_list              va_list;
 #define rt_used                     __attribute__((used))
 #define rt_align(n)                 __attribute__((aligned(n)))
 #define rt_weak                     __attribute__((weak))
+#define rt_noreturn                 __attribute__ ((noreturn))
 #define rt_inline                   static __inline
 #define RTT_API
 #elif defined (__ADSPBLACKFIN__)        /* for VisualDSP++ Compiler */
@@ -399,6 +400,7 @@ typedef int (*init_fn_t)(void);
 #define RT_ETRAP                        11              /**< Trap event */
 #define RT_ENOENT                       12              /**< No entry */
 #define RT_ENOSPC                       13              /**< No space left */
+#define RT_EPERM                        14              /**< Operation not permitted */
 
 /**@}*/
 
@@ -753,14 +755,20 @@ struct rt_wakeup
 #define _LWP_NSIG_BPW   32
 #endif
 
-#define _LWP_NSIG_WORDS (_LWP_NSIG / _LWP_NSIG_BPW)
+#define _LWP_NSIG_WORDS (RT_ALIGN(_LWP_NSIG, _LWP_NSIG_BPW) / _LWP_NSIG_BPW)
 
 typedef void (*lwp_sighandler_t)(int);
+typedef void (*lwp_sigaction_t)(int signo, siginfo_t *info, void *context);
 
 typedef struct {
     unsigned long sig[_LWP_NSIG_WORDS];
 } lwp_sigset_t;
 
+#if _LWP_NSIG <= 64
+#define lwp_sigmask(signo)      ((lwp_sigset_t){.sig = {[0] = ((long)(1u << ((signo)-1)))}})
+#define lwp_sigset_init(mask)   ((lwp_sigset_t){.sig = {[0] = (long)(mask)}})
+#endif
+
 struct lwp_sigaction {
     union {
         void (*_sa_handler)(int);
@@ -771,6 +779,29 @@ struct lwp_sigaction {
     void (*sa_restorer)(void);
 };
 
+typedef struct lwp_siginfo {
+    rt_list_t node;
+
+    struct {
+        int signo;
+        int code;
+        long value;
+
+        int from_tid;
+        pid_t from_pid;
+    } ksiginfo;
+} *lwp_siginfo_t;
+
+typedef struct lwp_sigqueue {
+    rt_list_t siginfo_list;
+    lwp_sigset_t sigset_pending;
+} *lwp_sigqueue_t;
+
+struct lwp_thread_signal {
+    lwp_sigset_t sigset_mask;
+    struct lwp_sigqueue sig_queue;
+};
+
 struct rt_user_context
 {
     void *sp;
@@ -779,120 +810,116 @@ struct rt_user_context
 };
 #endif
 
+typedef void (*rt_thread_cleanup_t)(struct rt_thread *tid);
+
 /**
  * Thread structure
  */
 struct rt_thread
 {
-    struct rt_object parent;
-    rt_list_t   tlist;                                  /**< the thread list */
+    struct rt_object            parent;
+    rt_list_t                   tlist;                  /**< the thread list */
 
     /* stack point and entry */
-    void       *sp;                                     /**< stack point */
-    void       *entry;                                  /**< entry */
-    void       *parameter;                              /**< parameter */
-    void       *stack_addr;                             /**< stack address */
-    rt_uint32_t stack_size;                             /**< stack size */
+    void                        *sp;                    /**< stack point */
+    void                        *entry;                 /**< entry */
+    void                        *parameter;             /**< parameter */
+    void                        *stack_addr;            /**< stack address */
+    rt_uint32_t                 stack_size;             /**< stack size */
 
     /* error code */
-    rt_err_t    error;                                  /**< error code */
+    rt_err_t                    error;                  /**< error code */
 
-    rt_uint8_t  stat;                                   /**< thread status */
+    rt_uint8_t                  stat;                   /**< thread status */
 
 #ifdef RT_USING_SMP
-    rt_uint8_t  bind_cpu;                               /**< thread is bind to cpu */
-    rt_uint8_t  oncpu;                                  /**< process on cpu */
+    rt_uint8_t                  bind_cpu;               /**< thread is bind to cpu */
+    rt_uint8_t                  oncpu;                  /**< process on cpu */
 
-    rt_uint16_t scheduler_lock_nest;                    /**< scheduler lock count */
-    rt_uint16_t cpus_lock_nest;                         /**< cpus lock count */
-    rt_uint16_t critical_lock_nest;                     /**< critical lock count */
+    rt_uint16_t                 scheduler_lock_nest;    /**< scheduler lock count */
+    rt_int16_t                  cpus_lock_nest;         /**< cpus lock count */
+    rt_uint16_t                 critical_lock_nest;     /**< critical lock count */
 #endif /*RT_USING_SMP*/
 
     /* priority */
-    rt_uint8_t  current_priority;                       /**< current priority */
-    rt_uint8_t  init_priority;                          /**< initialized priority */
+    rt_uint8_t                  current_priority;       /**< current priority */
+    rt_uint8_t                  init_priority;          /**< initialized priority */
 #if RT_THREAD_PRIORITY_MAX > 32
-    rt_uint8_t  number;
-    rt_uint8_t  high_mask;
+    rt_uint8_t                  number;
+    rt_uint8_t                  high_mask;
 #endif /* RT_THREAD_PRIORITY_MAX > 32 */
-    rt_uint32_t number_mask;                            /**< priority number mask */
+    rt_uint32_t                 number_mask;            /**< priority number mask */
 
 #ifdef RT_USING_MUTEX
     /* object for IPC */
-    rt_list_t taken_object_list;
-    rt_object_t pending_object;
+    rt_list_t                   taken_object_list;
+    rt_object_t                 pending_object;
 #endif
 
 #ifdef RT_USING_EVENT
     /* thread event */
-    rt_uint32_t event_set;
-    rt_uint8_t  event_info;
+    rt_uint32_t                 event_set;
+    rt_uint8_t                  event_info;
 #endif /* RT_USING_EVENT */
 
 #ifdef RT_USING_SIGNALS
-    rt_sigset_t     sig_pending;                        /**< the pending signals */
-    rt_sigset_t     sig_mask;                           /**< the mask bits of signal */
+    rt_sigset_t                 sig_pending;            /**< the pending signals */
+    rt_sigset_t                 sig_mask;               /**< the mask bits of signal */
 
 #ifndef RT_USING_SMP
-    void            *sig_ret;                           /**< the return stack pointer from signal */
+    void                        *sig_ret;               /**< the return stack pointer from signal */
 #endif /* RT_USING_SMP */
-    rt_sighandler_t *sig_vectors;                       /**< vectors of signal handler */
-    void            *si_list;                           /**< the signal infor list */
+    rt_sighandler_t             *sig_vectors;           /**< vectors of signal handler */
+    void                        *si_list;               /**< the signal infor list */
 #endif /* RT_USING_SIGNALS */
 
-#ifdef RT_USING_SMART
-    void            *msg_ret;                           /**< the return msg */
-#endif
-
-    rt_ubase_t  init_tick;                              /**< thread's initialized tick */
-    rt_ubase_t  remaining_tick;                         /**< remaining tick */
+    rt_ubase_t                  init_tick;              /**< thread's initialized tick */
+    rt_ubase_t                  remaining_tick;         /**< remaining tick */
 
 #ifdef RT_USING_CPU_USAGE
-    rt_uint64_t  duration_tick;                         /**< cpu usage tick */
+    rt_uint64_t                 duration_tick;          /**< cpu usage tick */
 #endif /* RT_USING_CPU_USAGE */
 
 #ifdef RT_USING_PTHREADS
-    void  *pthread_data;                                /**< the handle of pthread data, adapt 32/64bit */
+    void                        *pthread_data;          /**< the handle of pthread data, adapt 32/64bit */
 #endif /* RT_USING_PTHREADS */
 
-    struct rt_timer thread_timer;                       /**< built-in thread timer */
+    struct rt_timer             thread_timer;           /**< built-in thread timer */
 
-    void (*cleanup)(struct rt_thread *tid);             /**< cleanup function when thread exit */
+    rt_thread_cleanup_t         cleanup;                /**< cleanup function when thread exit */
 
     /* light weight process if present */
 #ifdef RT_USING_SMART
-    void        *lwp;
+    void                        *msg_ret;               /**< the return msg */
+
+    void                        *lwp;                   /**< the lwp reference */
     /* for user create */
-    void        *user_entry;
-    void        *user_stack;
-    rt_uint32_t user_stack_size;
-    rt_uint32_t *kernel_sp;                             /**< kernel stack point */
-    rt_list_t   sibling;                                /**< next thread of same process */
-
-    lwp_sigset_t signal;
-    lwp_sigset_t signal_mask;
-    int signal_mask_bak;
-    rt_uint32_t signal_in_process;
+    void                        *user_entry;
+    void                        *user_stack;
+    rt_uint32_t                 user_stack_size;
+    rt_uint32_t                 *kernel_sp;             /**< kernel stack point */
+    rt_list_t                   sibling;                /**< next thread of same process */
+
+    struct lwp_thread_signal    signal;                 /**< lwp signal for user-space thread */
+    struct rt_user_context      user_ctx;               /**< user space context */
+    struct rt_wakeup            wakeup;                 /**< wakeup data */
+    int                         exit_request;
+    int                         tid;
+
 #ifndef ARCH_MM_MMU
-    lwp_sighandler_t signal_handler[32];
-#endif
-    struct rt_user_context user_ctx;
-
-    struct rt_wakeup wakeup;                            /**< wakeup data */
-    int exit_request;
-#if defined(ARCH_MM_MMU)
-    int step_exec;
-    int debug_attach_req;
-    int debug_ret_user;
-    int debug_suspend;
-    struct rt_hw_exp_stack *regs;
-    void * thread_idr;                                 /** lwp thread indicator */
-    int *clear_child_tid;
-#endif
-    int tid;
-#endif
+    lwp_sighandler_t            signal_handler[32];
+#else
+    int                         step_exec;
+    int                         debug_attach_req;
+    int                         debug_ret_user;
+    int                         debug_suspend;
+    struct rt_hw_exp_stack      *regs;
+    void                        *thread_idr;            /** lwp thread indicator */
+    int                         *clear_child_tid;
+#endif /* ARCH_MM_MMU */
+#endif /* RT_USING_SMART */
 
-    rt_ubase_t user_data;                             /**< private user data beyond this thread */
+    rt_ubase_t                  user_data;              /**< private user data beyond this thread */
 };
 typedef struct rt_thread *rt_thread_t;
 

+ 8 - 6
src/thread.c

@@ -262,10 +262,12 @@ static rt_err_t _thread_init(struct rt_thread *thread,
 #ifdef RT_USING_SMART
     thread->lwp = RT_NULL;
     rt_list_init(&(thread->sibling));
-    rt_memset(&thread->signal, 0, sizeof(lwp_sigset_t));
-    rt_memset(&thread->signal_mask, 0, sizeof(lwp_sigset_t));
-    thread->signal_mask_bak = 0;
-    thread->signal_in_process = 0;
+
+    /* lwp thread-signal init */
+    rt_memset(&thread->signal.sigset_mask, 0, sizeof(lwp_sigset_t));
+    rt_memset(&thread->signal.sig_queue.sigset_pending, 0, sizeof(lwp_sigset_t));
+    rt_list_init(&thread->signal.sig_queue.siginfo_list);
+
     rt_memset(&thread->user_ctx, 0, sizeof thread->user_ctx);
 #endif
 
@@ -935,7 +937,7 @@ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg)
 RTM_EXPORT(rt_thread_control);
 
 #ifdef RT_USING_SMART
-int lwp_suspend_sigcheck(rt_thread_t thread, int suspend_flag);
+#include <lwp_signal.h>
 #endif
 
 static void rt_thread_set_suspend_state(struct rt_thread *thread, int suspend_flag)
@@ -1004,7 +1006,7 @@ rt_err_t rt_thread_suspend_with_flag(rt_thread_t thread, int suspend_flag)
         RT_ASSERT(thread == rt_thread_self());
     }
 #ifdef RT_USING_SMART
-    if (lwp_suspend_sigcheck(thread, suspend_flag) == 0)
+    if (lwp_thread_signal_suspend_check(thread, suspend_flag) == 0)
     {
         /* not to suspend */
         rt_hw_interrupt_enable(level);