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