1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/tasks/signal.hpp"
4
5#include "mos/platform/platform.hpp"
6#include "mos/syslog/printk.hpp"
7#include "mos/tasks/process.hpp"
8#include "mos/tasks/schedule.hpp"
9#include "mos/tasks/thread.hpp"
10
11#include <errno.h>
12#include <limits.h>
13#include <mos/lib/structures/list.hpp>
14#include <mos/lib/sync/spinlock.hpp>
15#include <mos/tasks/signal_types.h>
16#include <mos_stdlib.hpp>
17#include <mos_string.hpp>
18
19static int sigset_add(sigset_t *sigset, int sig)
20{
21 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
22
23 const int signo = sig - 1;
24 char *const ptr = (char *) sigset;
25 ptr[signo / CHAR_BIT] |= (1 << (signo % CHAR_BIT));
26 return 0;
27}
28
29static int sigset_del(sigset_t *sigset, int sig)
30{
31 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
32
33 const int signo = sig - 1;
34 char *const ptr = (char *) sigset;
35 ptr[signo / CHAR_BIT] &= ~(1 << (signo % CHAR_BIT));
36 return 0;
37}
38
39static int sigset_test(const sigset_t *sigset, int sig)
40{
41 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
42
43 const int signo = sig - 1;
44 const char *ptr = (const char *) sigset;
45 return (ptr[signo / CHAR_BIT] & (1 << (signo % CHAR_BIT))) != 0;
46}
47
48[[noreturn]] static void signal_do_coredump(signal_t signal)
49{
50 process_exit(proc: std::move(current_process), exit_code: 0, signal);
51 MOS_UNREACHABLE();
52}
53
54[[noreturn]] static void signal_do_terminate(signal_t signal)
55{
56 if (current_thread == current_process->main_thread)
57 process_exit(proc: std::move(current_process), exit_code: 0, signal);
58 else
59 thread_exit(t: std::move(current_thread));
60 MOS_UNREACHABLE();
61}
62
63static void signal_do_ignore(signal_t signal)
64{
65 pr_dinfo2(signal, "thread %pt ignoring signal %d", current_thread, signal);
66}
67
68static bool is_fatal_signal(signal_t signal)
69{
70 switch (signal)
71 {
72 case SIGILL:
73 case SIGTRAP:
74 case SIGABRT:
75 case SIGKILL:
76 case SIGSEGV: return true;
77
78 case SIGINT:
79 case SIGTERM:
80 case SIGCHLD:
81 case SIGPIPE: return false;
82
83 default: MOS_UNREACHABLE_X("handle this signal %d", signal); break;
84 }
85}
86
87long signal_send_to_thread(Thread *target, signal_t signal)
88{
89 if (target->mode == THREAD_MODE_KERNEL && !is_fatal_signal(signal))
90 {
91 pr_emerg("signal_send_to_thread(%pt, %d): cannot send non-fatal signal to kernel thread", target, signal);
92 return -EINVAL;
93 }
94
95 spinlock_acquire(&target->signal_info.lock);
96
97 bool has_pending = false; // true if the signal is already pending
98 list_foreach(sigpending_t, pending, target->signal_info.pending)
99 {
100 has_pending |= pending->signal == signal;
101 if (has_pending)
102 break;
103 }
104
105 if (!has_pending)
106 {
107 sigpending_t *sigdesc = mos::create<sigpending_t>();
108 linked_list_init(list_node(sigdesc));
109 sigdesc->signal = signal;
110 list_node_append(head: &target->signal_info.pending, list_node(sigdesc));
111 }
112
113 spinlock_release(&target->signal_info.lock);
114
115 if (target != current_thread)
116 scheduler_wake_thread(thread: target);
117
118 return 0;
119}
120
121long signal_send_to_process(Process *target, signal_t signal)
122{
123 if (target->pid == 1 && signal == SIGKILL)
124 {
125 pr_emerg("signal_send_to_process(%pp, %d): cannot send SIGKILL to init", target, signal);
126 return -EINVAL;
127 }
128
129 if (target->pid == 2)
130 {
131 pr_emerg("signal_send_to_process(%pp, %d): cannot send signal to kthreadd", target, signal);
132 return -EINVAL;
133 }
134
135 Thread *target_thread = NULL;
136 for (const auto &thread : target->thread_list)
137 {
138 if (thread->state == THREAD_STATE_RUNNING || thread->state == THREAD_STATE_READY || thread->state == THREAD_STATE_CREATED)
139 {
140 target_thread = thread;
141 break;
142 }
143 }
144
145 if (!target_thread)
146 {
147 for (const auto &thread : target->thread_list)
148 {
149 if (thread->state == THREAD_STATE_BLOCKED)
150 {
151 target_thread = thread;
152 break;
153 }
154 }
155 }
156
157 if (!target_thread)
158 {
159 pr_emerg("signal_send_to_process(%pp, %d): no thread to send signal to", target, signal);
160 return -EINVAL;
161 }
162
163 signal_send_to_thread(target: target_thread, signal);
164
165 return 0;
166}
167
168static signal_t signal_get_next_pending(void)
169{
170 signal_t signal = 0;
171
172 MOS_ASSERT(spinlock_is_locked(&current_thread->signal_info.lock));
173 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
174 {
175 if (sigset_test(sigset: &current_thread->signal_info.mask, sig: pending->signal))
176 {
177 // if a fatal signal is pending but also masked, kill the thread
178 if (is_fatal_signal(signal: pending->signal))
179 {
180 pr_emerg("thread %pt received fatal signal %d but it was masked, terminating", current_thread, pending->signal);
181 signal_do_terminate(signal: pending->signal);
182 }
183 continue; // signal is masked, skip it
184 }
185
186 list_remove(pending);
187 signal = pending->signal;
188 delete pending;
189 break;
190 }
191
192 return signal;
193}
194
195static void do_signal_exit_to_user_prepare(platform_regs_t *regs, signal_t next_signal, const sigaction_t *action)
196{
197 if (action->handler == SIG_DFL)
198 {
199 if (current_process->pid == 1 && !is_fatal_signal(signal: next_signal))
200 goto done; // init only receives signals it wants
201
202 switch (next_signal)
203 {
204 case SIGINT: signal_do_terminate(signal: next_signal); break;
205 case SIGILL: signal_do_coredump(signal: next_signal); break;
206 case SIGTRAP: signal_do_coredump(signal: next_signal); break;
207 case SIGABRT: signal_do_coredump(signal: next_signal); break;
208 case SIGKILL: signal_do_terminate(signal: next_signal); break;
209 case SIGSEGV: signal_do_coredump(signal: next_signal); break;
210 case SIGTERM: signal_do_terminate(signal: next_signal); break;
211 case SIGCHLD: signal_do_ignore(signal: next_signal); break;
212 case SIGPIPE: signal_do_terminate(signal: next_signal); break;
213
214 default: MOS_UNREACHABLE_X("handle this signal %d", next_signal); break;
215 }
216
217 // the default handler returns
218 done:
219 return;
220 }
221
222 if (action->handler == SIG_IGN)
223 {
224 signal_do_ignore(signal: next_signal);
225 return;
226 }
227
228 const bool was_masked = sigset_test(sigset: &current_thread->signal_info.mask, sig: next_signal);
229 if (!was_masked)
230 sigset_add(sigset: &current_thread->signal_info.mask, sig: next_signal);
231
232 const sigreturn_data_t data = {
233 .signal = next_signal,
234 .was_masked = was_masked,
235 };
236
237 platform_jump_to_signal_handler(regs, sigreturn_data: &data, sa: action); // save previous register states onto user stack
238}
239
240void signal_exit_to_user_prepare(platform_regs_t *regs)
241{
242 MOS_ASSERT(current_thread);
243
244 spinlock_acquire(&current_thread->signal_info.lock);
245 const signal_t next_signal = signal_get_next_pending();
246 spinlock_release(&current_thread->signal_info.lock);
247
248 if (!next_signal)
249 return; // no pending signal, leave asap
250
251 const sigaction_t action = current_process->signal_info.handlers[next_signal];
252
253 do_signal_exit_to_user_prepare(regs, next_signal, action: &action);
254}
255
256void signal_exit_to_user_prepare_syscall(platform_regs_t *regs, reg_t syscall_nr, reg_t syscall_ret)
257{
258 MOS_ASSERT(current_thread);
259
260 spinlock_acquire(&current_thread->signal_info.lock);
261 const signal_t next_signal = signal_get_next_pending();
262 spinlock_release(&current_thread->signal_info.lock);
263
264 const sigaction_t action = current_process->signal_info.handlers[next_signal];
265
266 reg_t real_ret = syscall_ret;
267 if (syscall_ret == (reg_t) -ERESTARTSYS)
268 {
269 MOS_ASSERT(next_signal);
270 real_ret = -EINTR;
271
272 if (action.sa_flags & SA_RESTART)
273 {
274 pr_dinfo2(signal, "thread %pt will restart syscall %lu after signal %d", current_thread, syscall_nr, next_signal);
275 platform_syscall_setup_restart_context(regs, syscall_nr);
276 goto really_prepare;
277 }
278 // else: fall through, return -EINTR
279 }
280
281 platform_syscall_store_retval(regs, result: real_ret);
282
283 if (!next_signal)
284 return; // no pending signal, leave asap
285
286really_prepare:
287 do_signal_exit_to_user_prepare(regs, next_signal, action: &action);
288}
289
290void signal_on_returned(sigreturn_data_t *data)
291{
292 if (!data->was_masked)
293 sigset_del(sigset: &current_thread->signal_info.mask, sig: data->signal);
294}
295
296bool signal_has_pending(void)
297{
298 bool has_pending = false;
299 spinlock_acquire(&current_thread->signal_info.lock);
300 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
301 {
302 if (sigset_test(sigset: &current_thread->signal_info.mask, sig: pending->signal))
303 continue; // signal is masked, skip it
304 has_pending = true;
305 break;
306 }
307
308 spinlock_release(&current_thread->signal_info.lock);
309 return has_pending;
310}
311