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