diff options
Diffstat (limited to 'arch/um/os-Linux/signal.c')
-rw-r--r-- | arch/um/os-Linux/signal.c | 102 |
1 files changed, 59 insertions, 43 deletions
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index e9800b0b5689..0fb0cc8d4757 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c | |||
@@ -9,11 +9,47 @@ | |||
9 | #include <errno.h> | 9 | #include <errno.h> |
10 | #include <signal.h> | 10 | #include <signal.h> |
11 | #include <strings.h> | 11 | #include <strings.h> |
12 | #include "as-layout.h" | ||
13 | #include "kern_util.h" | ||
12 | #include "os.h" | 14 | #include "os.h" |
13 | #include "sysdep/barrier.h" | 15 | #include "sysdep/barrier.h" |
14 | #include "sysdep/sigcontext.h" | 16 | #include "sysdep/sigcontext.h" |
15 | #include "user.h" | 17 | #include "user.h" |
16 | 18 | ||
19 | /* Copied from linux/compiler-gcc.h since we can't include it directly */ | ||
20 | #define barrier() __asm__ __volatile__("": : :"memory") | ||
21 | |||
22 | void (*sig_info[NSIG])(int, struct uml_pt_regs *) = { | ||
23 | [SIGTRAP] = relay_signal, | ||
24 | [SIGFPE] = relay_signal, | ||
25 | [SIGILL] = relay_signal, | ||
26 | [SIGWINCH] = winch, | ||
27 | [SIGBUS] = bus_handler, | ||
28 | [SIGSEGV] = segv_handler, | ||
29 | [SIGIO] = sigio_handler, | ||
30 | [SIGVTALRM] = timer_handler }; | ||
31 | |||
32 | static void sig_handler_common(int sig, struct sigcontext *sc) | ||
33 | { | ||
34 | struct uml_pt_regs r; | ||
35 | int save_errno = errno; | ||
36 | |||
37 | r.is_user = 0; | ||
38 | if (sig == SIGSEGV) { | ||
39 | /* For segfaults, we want the data from the sigcontext. */ | ||
40 | copy_sc(&r, sc); | ||
41 | GET_FAULTINFO_FROM_SC(r.faultinfo, sc); | ||
42 | } | ||
43 | |||
44 | /* enable signals if sig isn't IRQ signal */ | ||
45 | if ((sig != SIGIO) && (sig != SIGWINCH) && (sig != SIGVTALRM)) | ||
46 | unblock_signals(); | ||
47 | |||
48 | (*sig_info[sig])(sig, &r); | ||
49 | |||
50 | errno = save_errno; | ||
51 | } | ||
52 | |||
17 | /* | 53 | /* |
18 | * These are the asynchronous signals. SIGPROF is excluded because we want to | 54 | * These are the asynchronous signals. SIGPROF is excluded because we want to |
19 | * be able to profile all of UML, not just the non-critical sections. If | 55 | * be able to profile all of UML, not just the non-critical sections. If |
@@ -26,13 +62,8 @@ | |||
26 | #define SIGVTALRM_BIT 1 | 62 | #define SIGVTALRM_BIT 1 |
27 | #define SIGVTALRM_MASK (1 << SIGVTALRM_BIT) | 63 | #define SIGVTALRM_MASK (1 << SIGVTALRM_BIT) |
28 | 64 | ||
29 | /* | 65 | static int signals_enabled; |
30 | * These are used by both the signal handlers and | 66 | static unsigned int signals_pending; |
31 | * block/unblock_signals. I don't want modifications cached in a | ||
32 | * register - they must go straight to memory. | ||
33 | */ | ||
34 | static volatile int signals_enabled = 1; | ||
35 | static volatile int pending = 0; | ||
36 | 67 | ||
37 | void sig_handler(int sig, struct sigcontext *sc) | 68 | void sig_handler(int sig, struct sigcontext *sc) |
38 | { | 69 | { |
@@ -40,13 +71,13 @@ void sig_handler(int sig, struct sigcontext *sc) | |||
40 | 71 | ||
41 | enabled = signals_enabled; | 72 | enabled = signals_enabled; |
42 | if (!enabled && (sig == SIGIO)) { | 73 | if (!enabled && (sig == SIGIO)) { |
43 | pending |= SIGIO_MASK; | 74 | signals_pending |= SIGIO_MASK; |
44 | return; | 75 | return; |
45 | } | 76 | } |
46 | 77 | ||
47 | block_signals(); | 78 | block_signals(); |
48 | 79 | ||
49 | sig_handler_common_skas(sig, sc); | 80 | sig_handler_common(sig, sc); |
50 | 81 | ||
51 | set_signals(enabled); | 82 | set_signals(enabled); |
52 | } | 83 | } |
@@ -68,7 +99,7 @@ void alarm_handler(int sig, struct sigcontext *sc) | |||
68 | 99 | ||
69 | enabled = signals_enabled; | 100 | enabled = signals_enabled; |
70 | if (!signals_enabled) { | 101 | if (!signals_enabled) { |
71 | pending |= SIGVTALRM_MASK; | 102 | signals_pending |= SIGVTALRM_MASK; |
72 | return; | 103 | return; |
73 | } | 104 | } |
74 | 105 | ||
@@ -94,16 +125,6 @@ void set_sigstack(void *sig_stack, int size) | |||
94 | panic("enabling signal stack failed, errno = %d\n", errno); | 125 | panic("enabling signal stack failed, errno = %d\n", errno); |
95 | } | 126 | } |
96 | 127 | ||
97 | void remove_sigstack(void) | ||
98 | { | ||
99 | stack_t stack = ((stack_t) { .ss_flags = SS_DISABLE, | ||
100 | .ss_sp = NULL, | ||
101 | .ss_size = 0 }); | ||
102 | |||
103 | if (sigaltstack(&stack, NULL) != 0) | ||
104 | panic("disabling signal stack failed, errno = %d\n", errno); | ||
105 | } | ||
106 | |||
107 | void (*handlers[_NSIG])(int sig, struct sigcontext *sc); | 128 | void (*handlers[_NSIG])(int sig, struct sigcontext *sc); |
108 | 129 | ||
109 | void handle_signal(int sig, struct sigcontext *sc) | 130 | void handle_signal(int sig, struct sigcontext *sc) |
@@ -166,6 +187,9 @@ void set_handler(int sig, void (*handler)(int), int flags, ...) | |||
166 | sigaddset(&action.sa_mask, mask); | 187 | sigaddset(&action.sa_mask, mask); |
167 | va_end(ap); | 188 | va_end(ap); |
168 | 189 | ||
190 | if (sig == SIGSEGV) | ||
191 | flags |= SA_NODEFER; | ||
192 | |||
169 | action.sa_flags = flags; | 193 | action.sa_flags = flags; |
170 | action.sa_restorer = NULL; | 194 | action.sa_restorer = NULL; |
171 | if (sigaction(sig, &action, NULL) < 0) | 195 | if (sigaction(sig, &action, NULL) < 0) |
@@ -179,12 +203,14 @@ void set_handler(int sig, void (*handler)(int), int flags, ...) | |||
179 | 203 | ||
180 | int change_sig(int signal, int on) | 204 | int change_sig(int signal, int on) |
181 | { | 205 | { |
182 | sigset_t sigset, old; | 206 | sigset_t sigset; |
183 | 207 | ||
184 | sigemptyset(&sigset); | 208 | sigemptyset(&sigset); |
185 | sigaddset(&sigset, signal); | 209 | sigaddset(&sigset, signal); |
186 | sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old); | 210 | if (sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, NULL) < 0) |
187 | return !sigismember(&old, signal); | 211 | return -errno; |
212 | |||
213 | return 0; | ||
188 | } | 214 | } |
189 | 215 | ||
190 | void block_signals(void) | 216 | void block_signals(void) |
@@ -196,7 +222,7 @@ void block_signals(void) | |||
196 | * This might matter if gcc figures out how to inline this and | 222 | * This might matter if gcc figures out how to inline this and |
197 | * decides to shuffle this code into the caller. | 223 | * decides to shuffle this code into the caller. |
198 | */ | 224 | */ |
199 | mb(); | 225 | barrier(); |
200 | } | 226 | } |
201 | 227 | ||
202 | void unblock_signals(void) | 228 | void unblock_signals(void) |
@@ -209,36 +235,26 @@ void unblock_signals(void) | |||
209 | /* | 235 | /* |
210 | * We loop because the IRQ handler returns with interrupts off. So, | 236 | * We loop because the IRQ handler returns with interrupts off. So, |
211 | * interrupts may have arrived and we need to re-enable them and | 237 | * interrupts may have arrived and we need to re-enable them and |
212 | * recheck pending. | 238 | * recheck signals_pending. |
213 | */ | 239 | */ |
214 | while(1) { | 240 | while(1) { |
215 | /* | 241 | /* |
216 | * Save and reset save_pending after enabling signals. This | 242 | * Save and reset save_pending after enabling signals. This |
217 | * way, pending won't be changed while we're reading it. | 243 | * way, signals_pending won't be changed while we're reading it. |
218 | */ | 244 | */ |
219 | signals_enabled = 1; | 245 | signals_enabled = 1; |
220 | 246 | ||
221 | /* | 247 | /* |
222 | * Setting signals_enabled and reading pending must | 248 | * Setting signals_enabled and reading signals_pending must |
223 | * happen in this order. | 249 | * happen in this order. |
224 | */ | 250 | */ |
225 | mb(); | 251 | barrier(); |
226 | 252 | ||
227 | save_pending = pending; | 253 | save_pending = signals_pending; |
228 | if (save_pending == 0) { | 254 | if (save_pending == 0) |
229 | /* | ||
230 | * This must return with signals enabled, so | ||
231 | * this barrier ensures that writes are | ||
232 | * flushed out before the return. This might | ||
233 | * matter if gcc figures out how to inline | ||
234 | * this (unlikely, given its size) and decides | ||
235 | * to shuffle this code into the caller. | ||
236 | */ | ||
237 | mb(); | ||
238 | return; | 255 | return; |
239 | } | ||
240 | 256 | ||
241 | pending = 0; | 257 | signals_pending = 0; |
242 | 258 | ||
243 | /* | 259 | /* |
244 | * We have pending interrupts, so disable signals, as the | 260 | * We have pending interrupts, so disable signals, as the |
@@ -254,7 +270,7 @@ void unblock_signals(void) | |||
254 | * back here. | 270 | * back here. |
255 | */ | 271 | */ |
256 | if (save_pending & SIGIO_MASK) | 272 | if (save_pending & SIGIO_MASK) |
257 | sig_handler_common_skas(SIGIO, NULL); | 273 | sig_handler_common(SIGIO, NULL); |
258 | 274 | ||
259 | if (save_pending & SIGVTALRM_MASK) | 275 | if (save_pending & SIGVTALRM_MASK) |
260 | real_alarm_handler(NULL); | 276 | real_alarm_handler(NULL); |