optimize the menuconfig redrawing
[lunaix-os.git] / lunaix-os / kernel / process / signal.c
1 #include <lunaix/process.h>
2 #include <lunaix/sched.h>
3 #include <lunaix/signal.h>
4 #include <lunaix/spike.h>
5 #include <lunaix/status.h>
6 #include <lunaix/syscall.h>
7 #include <lunaix/syslog.h>
8 #include <lunaix/mm/valloc.h>
9 #include <lunaix/switch.h>
10 #include <lunaix/kpreempt.h>
11
12 #include <klibc/string.h>
13
14 #include <sys/mm/mempart.h>
15
16 LOG_MODULE("SIG")
17
18 extern struct scheduler sched_ctx; /* kernel/sched.c */
19
20 #define UNMASKABLE (sigset(SIGKILL) | sigset(SIGTERM) | sigset(SIGILL))
21 #define TERMSIG (sigset(SIGSEGV) | sigset(SIGINT) | UNMASKABLE)
22 #define CORE (sigset(SIGSEGV))
23 #define within_kstack(addr)                                                    \
24     (KSTACK_AREA <= (addr) && (addr) <= KSTACK_AREA_END)
25
26 static inline void
27 signal_terminate(int caused_by)
28 {
29     terminate_current(caused_by | PEXITSIG);
30 }
31
32 // Referenced in kernel/asm/x86/interrupt.S
33 void
34 signal_dispatch(struct signpost_result* result)
35 {
36     continue_switch(result);
37
38     if (kernel_process(__current)) {
39         // signal is undefined under 'kernel process'
40         return;
41     }
42
43     if (!pending_sigs(current_thread)) {
44         // 没有待处理信号
45         return;
46     }
47
48     struct sigregistry* sigreg = __current->sigreg;
49     struct sigctx* psig = &current_thread->sigctx;
50     struct sigact* prev_working = active_signal(current_thread);
51     sigset_t mask = psig->sig_mask | (prev_working ? prev_working->sa_mask : 0);
52
53     int sig_selected = msbiti - clz(psig->sig_pending & ~mask);
54     sigset_clear(psig->sig_pending, sig_selected);
55
56     if (!sig_selected) {
57         // SIG0 is reserved
58         return;
59     }
60
61     struct sigact* action = sigreg->signals[sig_selected];
62     if (!action || !action->sa_actor) {
63         if (sigset_test(TERMSIG, sig_selected)) {
64             signal_terminate(sig_selected);
65             giveup_switch(result);
66         }
67         return;
68     }
69
70     ptr_t ustack = current_thread->ustack_top;
71     ptr_t ustack_start = current_thread->ustack->start;
72     if ((int)(ustack - ustack_start) < (int)sizeof(struct proc_sig)) {
73         // 用户栈没有空间存放信号上下文
74         return;
75     }
76
77     struct proc_sig* sigframe =
78         (struct proc_sig*)((ustack - sizeof(struct proc_sig)) & ~0xf);
79
80     sigframe->sig_num = sig_selected;
81     sigframe->sigact = action->sa_actor;
82     sigframe->sighand = action->sa_handler;
83
84     sigframe->saved_hstate = current_thread->hstate;
85
86     sigactive_push(current_thread, sig_selected);
87
88     redirect_switch(result, __ptr(sigframe));
89 }
90
91 static inline void must_inline
92 __set_signal(struct thread* thread, signum_t signum) 
93 {
94     raise_signal(thread, signum);
95
96     // for these mutually exclusive signal
97     if (signum == SIGCONT || signum == SIGSTOP) {
98         sigset_clear(thread->sigctx.sig_pending, signum ^ 1);
99     }
100     
101     struct sigact* sig = sigact_of(thread->process, signum);
102     if (sig) {
103         sig->sender = __current->pid;
104     }
105 }
106
107 static inline void must_inline
108 __set_signal_all_threads(struct proc_info* proc, signum_t signum) 
109 {
110     struct thread *pos, *n;
111     llist_for_each(pos, n, &proc->threads, proc_sibs) {
112         __set_signal(pos, signum);
113     }
114 }
115
116 void
117 thread_setsignal(struct thread* thread, signum_t signum)
118 {
119     if (unlikely(kernel_process(thread->process))) {
120         return;
121     }
122
123     __set_signal(thread, signum);
124 }
125
126 void
127 proc_setsignal(struct proc_info* proc, signum_t signum)
128 {
129     if (unlikely(kernel_process(proc))) {
130         return;
131     }
132
133     // FIXME handle signal delivery at process level.
134     switch (signum)
135     {
136     case SIGKILL:
137         signal_terminate(signum);
138         break;
139     case SIGCONT:
140     case SIGSTOP:
141         __set_signal_all_threads(proc, signum);
142     default:
143         break;
144     }
145     
146     __set_signal(proc->th_active, signum);
147 }
148
149 int
150 signal_send(pid_t pid, signum_t signum)
151 {
152     if (signum >= _SIG_NUM) {
153         return EINVAL;
154     }
155
156     pid_t sender_pid = __current->pid;
157     struct proc_info* proc;
158
159     if (pid > 0) {
160         proc = get_process(pid);
161         goto send_single;
162     } else if (!pid) {
163         proc = __current;
164         goto send_grp;
165     } else if (pid < 0) {
166         proc = get_process(-pid);
167         goto send_grp;
168     } else {
169         // TODO: send to all process.
170         //  But I don't want to support it yet.
171         return EINVAL;
172     }
173
174 send_grp: ;
175     struct proc_info *pos, *n;
176     llist_for_each(pos, n, &proc->grp_member, grp_member)
177     {
178         proc_setsignal(pos, signum);
179     }
180
181 send_single:
182     if (proc_terminated(proc)) {
183         return EINVAL;
184     }
185
186     proc_setsignal(proc, signum);
187
188     return 0;
189 }
190
191 void
192 signal_dup_context(struct sigctx* dest_ctx) 
193 {
194     struct sigctx* old_ctx = &current_thread->sigctx;
195     memcpy(dest_ctx, old_ctx, sizeof(struct sigctx));
196 }
197
198 void
199 signal_dup_registry(struct sigregistry* dest_reg)
200 {
201     struct sigregistry* oldreg = __current->sigreg;
202     for (int i = 0; i < _SIG_NUM; i++) {
203         struct sigact* oldact = oldreg->signals[i];
204         if (!oldact) {
205             continue;
206         }
207         
208         struct sigact* newact = valloc(sizeof(struct sigact));
209         memcpy(newact, oldact, sizeof(struct sigact));
210
211         dest_reg->signals[i] = newact;
212     }
213 }
214
215 void
216 signal_reset_context(struct sigctx* sigctx) {
217     memset(sigctx, 0, sizeof(struct sigctx));
218 }
219
220 void
221 signal_reset_registry(struct sigregistry* sigreg) {
222     for (int i = 0; i < _SIG_NUM; i++) {
223         struct sigact* act = sigreg->signals[i];
224         if (act) {
225             vfree(act);
226             sigreg->signals[i] = NULL;
227         }
228     }
229 }
230
231 void
232 signal_free_registry(struct sigregistry* sigreg) {
233     signal_reset_registry(sigreg);
234     vfree(sigreg);
235 }
236
237 static bool
238 signal_set_sigmask(struct thread* thread, int how, sigset_t* oldset, sigset_t* set)
239 {
240     struct sigctx* sh = &current_thread->sigctx;
241     *oldset = sh->sig_mask;
242
243     if (how == _SIG_BLOCK) {
244         sigset_union(sh->sig_mask, *set);
245     } else if (how == _SIG_UNBLOCK) {
246         sigset_intersect(sh->sig_mask, ~(*set));
247     } else if (how == _SIG_SETMASK) {
248         sh->sig_mask = *set;
249     } else {
250         return false;
251     }
252
253     sigset_intersect(sh->sig_mask, ~UNMASKABLE);
254     return true;
255 }
256
257 __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
258 {
259     struct sigctx* sigctx = &current_thread->sigctx;
260     struct sigact* active = active_signal(current_thread);
261
262     /* We choose signal#0 as our base case, that is sig#0 means no signal.
263         Therefore, it is an ill situation to return from such sigctx.
264     */
265     if (!active) {
266         signal_terminate(SIGSEGV);
267         schedule();
268     }
269
270     current_thread->hstate = sig_ctx->saved_hstate;
271     if (proc_terminated(__current)) {
272         __current->exit_code |= PEXITSIG;
273     } else if (sigset_test(CORE, sig_ctx->sig_num)) {
274         signal_terminate(sig_ctx->sig_num);
275     }
276
277     ptr_t ictx = (ptr_t)current_thread->hstate;
278
279     /*
280         Ensure our restored context is within kernel stack
281
282         This prevent user to forge their own context such that arbitrary code
283        can be executed as supervisor level
284     */
285     if (!within_kstack(ictx)) {
286         signal_terminate(SIGSEGV);
287     }
288
289     sigactive_pop(current_thread);
290
291     schedule();
292
293     // never reach!
294     return 0;
295 }
296
297 __DEFINE_LXSYSCALL3(
298     int, sigprocmask, int, how, const sigset_t, *set, sigset_t, *oldset)
299 {
300     // TODO maybe it is a good opportunity to introduce a process-wide 
301     //      signal mask?
302     
303     if (signal_set_sigmask(current_thread, how, oldset, set)) {
304         return 0;
305     }
306
307     syscall_result(EINVAL);
308     return -1;
309 }
310
311 __DEFINE_LXSYSCALL3(
312     int, th_sigmask, int, how, const sigset_t, *set, sigset_t, *oldset)
313 {
314     if (signal_set_sigmask(current_thread, how, oldset, set)) {
315         return 0;
316     }
317
318     return EINVAL;
319 }
320
321 __DEFINE_LXSYSCALL2(int, sys_sigaction, int, signum, struct sigaction*, action)
322 {
323     if (signum <= 0 || signum >= _SIG_NUM) {
324         return -1;
325     }
326
327     if (sigset_test(UNMASKABLE, signum)) {
328         return -1;
329     }
330
331     struct sigctx* sigctx = &current_thread->sigctx;
332     if (signum == sigctx->sig_active) {
333         return -1;
334     }
335
336     struct sigact* sa = sigact_of(__current, signum);
337
338     if (!sa) {
339         sa = vzalloc(sizeof(struct sigact));
340         set_sigact(__current, signum, sa);
341     }
342
343     sa->sa_actor = (void*)action->sa_sigaction;
344     sa->sa_handler = (void*)action->sa_handler;
345     sigset_union(sa->sa_mask, sigset(signum));
346
347     return 0;
348 }
349
350 __DEFINE_LXSYSCALL(int, pause)
351 {
352     pause_current_thread();
353     yield_current();
354
355     syscall_result(EINTR);
356     return -1;
357 }
358
359 __DEFINE_LXSYSCALL2(int, kill, pid_t, pid, int, signum)
360 {
361     return syscall_result(signal_send(pid, signum));
362 }
363
364 __DEFINE_LXSYSCALL1(int, sigpending, sigset_t, *sigset)
365 {
366     *sigset = pending_sigs(current_thread);
367     return 0;
368 }
369
370 __DEFINE_LXSYSCALL1(int, sigsuspend, sigset_t, *mask)
371 {
372     struct sigctx* sigctx = &current_thread->sigctx;
373     sigset_t tmp = current_thread->sigctx.sig_mask;
374     sigctx->sig_mask = (*mask) & ~UNMASKABLE;
375
376     pause_current_thread();
377     yield_current();
378
379     sigctx->sig_mask = tmp;
380     return -1;
381 }