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