feat: a pseudo shell environment for basic interacting and testing purpose
[lunaix-os.git] / lunaix-os / kernel / signal.c
1 #include <lunaix/lunistd.h>
2 #include <lunaix/lxsignal.h>
3 #include <lunaix/process.h>
4 #include <lunaix/sched.h>
5 #include <lunaix/signal.h>
6 #include <lunaix/spike.h>
7 #include <lunaix/status.h>
8 #include <lunaix/syscall.h>
9
10 extern struct scheduler sched_ctx; /* kernel/sched.c */
11
12 void __USER__
13 default_sighandler_term(int signum)
14 {
15     _exit(signum);
16 }
17
18 void* default_handlers[_SIG_NUM] = {
19     // TODO: 添加默认handler
20     [_SIGINT] = default_sighandler_term,  [_SIGTERM] = default_sighandler_term,
21     [_SIGKILL] = default_sighandler_term, [_SIGSEGV] = default_sighandler_term,
22     [_SIGINT] = default_sighandler_term,
23 };
24
25 volatile isr_param __temp_save;
26 // Referenced in kernel/asm/x86/interrupt.S
27 void*
28 signal_dispatch()
29 {
30     if (!__current->sig_pending) {
31         // 没有待处理信号
32         return 0;
33     }
34
35     int sig_selected =
36       31 - __builtin_clz(__current->sig_pending &
37                          ~(__current->sig_mask | __current->sig_inprogress));
38
39     __SIGCLEAR(__current->sig_pending, sig_selected);
40
41     if (sig_selected == 0) {
42         // SIG0 is reserved
43         return 0;
44     }
45
46     if (!__current->sig_handler[sig_selected] &&
47         !default_handlers[sig_selected]) {
48         // 如果该信号没有handler,则忽略
49         return 0;
50     }
51
52     uintptr_t ustack = __current->ustack_top & ~0xf;
53
54     if ((int)(ustack - USTACK_END) < (int)sizeof(struct proc_sig)) {
55         // 用户栈没有空间存放信号上下文
56         return 0;
57     }
58
59     struct proc_sig* sig_ctx =
60       (struct proc_sig*)(ustack - sizeof(struct proc_sig));
61
62     /*
63         这是一个相当恶心的坑。
64         问题是出在原本的sig_ctx->prev_context = __current->intr_ctx的上面
65         这个语句会被gcc在编译时,用更加高效的 rep movsl 来代替。
66
67         由于我们采用按需分页,所以在很多情况下,用户栈实际被分配的空间不允许我们进行完整的
68         注入,而需要走page fault handler进行动态分页。
69
70         竞态条件就出现在这里!
71
72         假若我们的__current->intr_ctx注入了一半,然后产生page-fault中断,
73         那么这就会导致我们的__current->intr_ctx被这个page-fault中断导致的
74         上下文信息覆盖。那么当page-fault handler成功分配了一个页,返回,
75         拷贝也就得以进行。遗憾的是,只不过这次拷贝的内容和前面的拷贝是没有任何的关系
76         (因为此时的intr_ctx已经不是之前的intr_ctx了!)
77         而这就会导致我们保存在信号上下文中的进程上下文信息不完整,从而在soft_iret时
78         触发#GP。
79
80         解决办法就是先吧intr_ctx拷贝到一个静态分配的区域里,然后再注入到用户栈。
81     */
82     __temp_save = __current->intr_ctx;
83     sig_ctx->prev_context = __temp_save;
84
85     sig_ctx->sig_num = sig_selected;
86     sig_ctx->signal_handler = __current->sig_handler[sig_selected];
87
88     if (!sig_ctx->signal_handler) {
89         // 如果没有用户自定义的Handler,则使用系统默认Handler。
90         sig_ctx->signal_handler = default_handlers[sig_selected];
91     }
92
93     __SIGSET(__current->sig_inprogress, sig_selected);
94
95     return sig_ctx;
96 }
97
98 int
99 signal_send(pid_t pid, int signum)
100 {
101     if (signum < 0 || signum >= _SIG_NUM) {
102         __current->k_status = EINVAL;
103         return -1;
104     }
105
106     struct proc_info* proc;
107     if (pid > 0) {
108         proc = get_process(pid);
109         goto send_single;
110     } else if (!pid) {
111         proc = __current;
112         goto send_grp;
113     } else if (pid < -1) {
114         proc = get_process(-pid);
115         goto send_grp;
116     } else {
117         // TODO: send to all process.
118         //  But I don't want to support it yet.
119         __current->k_status = EINVAL;
120         return -1;
121     }
122
123 send_grp:
124     struct proc_info *pos, *n;
125     llist_for_each(pos, n, &proc->grp_member, grp_member)
126     {
127         __SIGSET(pos->sig_pending, signum);
128     }
129     return 0;
130
131 send_single:
132     if (PROC_TERMINATED(proc->state)) {
133         __current->k_status = EINVAL;
134         return -1;
135     }
136     __SIGSET(proc->sig_pending, signum);
137     return 0;
138 }
139
140 __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
141 {
142     __current->intr_ctx = sig_ctx->prev_context;
143     __current->flags &= ~PROC_FINPAUSE;
144     __SIGCLEAR(__current->sig_inprogress, sig_ctx->sig_num);
145     schedule();
146 }
147
148 __DEFINE_LXSYSCALL3(int,
149                     sigprocmask,
150                     int,
151                     how,
152                     const sigset_t,
153                     *set,
154                     sigset_t,
155                     *oldset)
156 {
157     *oldset = __current->sig_mask;
158     if (how == _SIG_BLOCK) {
159         __current->sig_mask |= *set;
160     } else if (how == _SIG_UNBLOCK) {
161         __current->sig_mask &= ~(*set);
162     } else if (how == _SIG_SETMASK) {
163         __current->sig_mask = *set;
164     } else {
165         return 0;
166     }
167     __current->sig_mask &= ~_SIGNAL_UNMASKABLE;
168     return 1;
169 }
170
171 __DEFINE_LXSYSCALL2(int, signal, int, signum, sighandler_t, handler)
172 {
173     if (signum <= 0 || signum >= _SIG_NUM) {
174         return -1;
175     }
176
177     if ((__SIGNAL(signum) & _SIGNAL_UNMASKABLE)) {
178         return -1;
179     }
180
181     __current->sig_handler[signum] = (void*)handler;
182
183     return 0;
184 }
185
186 void
187 __do_pause()
188 {
189     __current->flags |= PROC_FINPAUSE;
190
191     while ((__current->flags & PROC_FINPAUSE)) {
192         sched_yieldk();
193     }
194
195     __current->k_status = EINTR;
196 }
197
198 __DEFINE_LXSYSCALL(int, pause)
199 {
200     __do_pause();
201     return -1;
202 }
203
204 __DEFINE_LXSYSCALL2(int, kill, pid_t, pid, int, signum)
205 {
206     return signal_send(pid, signum);
207 }
208
209 __DEFINE_LXSYSCALL1(int, sigpending, sigset_t, *sigset)
210 {
211     *sigset = __current->sig_pending;
212     return 0;
213 }
214
215 __DEFINE_LXSYSCALL1(int, sigsuspend, sigset_t, *mask)
216 {
217     sigset_t tmp = __current->sig_mask;
218     __current->sig_mask = (*mask) & ~_SIGNAL_UNMASKABLE;
219     __do_pause();
220     __current->sig_mask = tmp;
221     return -1;
222 }