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