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