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