diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/alpha/kernel/signal.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/alpha/kernel/signal.c')
-rw-r--r-- | arch/alpha/kernel/signal.c | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c new file mode 100644 index 000000000000..08fe8071a7f8 --- /dev/null +++ b/arch/alpha/kernel/signal.c | |||
@@ -0,0 +1,672 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/signal.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * | ||
6 | * 1997-11-02 Modified for POSIX.1b signals by Richard Henderson | ||
7 | */ | ||
8 | |||
9 | #include <linux/sched.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/signal.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/wait.h> | ||
14 | #include <linux/ptrace.h> | ||
15 | #include <linux/unistd.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/smp.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/stddef.h> | ||
20 | #include <linux/tty.h> | ||
21 | #include <linux/binfmts.h> | ||
22 | #include <linux/bitops.h> | ||
23 | |||
24 | #include <asm/uaccess.h> | ||
25 | #include <asm/sigcontext.h> | ||
26 | #include <asm/ucontext.h> | ||
27 | |||
28 | #include "proto.h" | ||
29 | |||
30 | |||
31 | #define DEBUG_SIG 0 | ||
32 | |||
33 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
34 | |||
35 | asmlinkage void ret_from_sys_call(void); | ||
36 | static int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *, | ||
37 | unsigned long, unsigned long); | ||
38 | |||
39 | |||
40 | /* | ||
41 | * The OSF/1 sigprocmask calling sequence is different from the | ||
42 | * C sigprocmask() sequence.. | ||
43 | * | ||
44 | * how: | ||
45 | * 1 - SIG_BLOCK | ||
46 | * 2 - SIG_UNBLOCK | ||
47 | * 3 - SIG_SETMASK | ||
48 | * | ||
49 | * We change the range to -1 .. 1 in order to let gcc easily | ||
50 | * use the conditional move instructions. | ||
51 | * | ||
52 | * Note that we don't need to acquire the kernel lock for SMP | ||
53 | * operation, as all of this is local to this thread. | ||
54 | */ | ||
55 | asmlinkage unsigned long | ||
56 | do_osf_sigprocmask(int how, unsigned long newmask, struct pt_regs *regs) | ||
57 | { | ||
58 | unsigned long oldmask = -EINVAL; | ||
59 | |||
60 | if ((unsigned long)how-1 <= 2) { | ||
61 | long sign = how-2; /* -1 .. 1 */ | ||
62 | unsigned long block, unblock; | ||
63 | |||
64 | newmask &= _BLOCKABLE; | ||
65 | spin_lock_irq(¤t->sighand->siglock); | ||
66 | oldmask = current->blocked.sig[0]; | ||
67 | |||
68 | unblock = oldmask & ~newmask; | ||
69 | block = oldmask | newmask; | ||
70 | if (!sign) | ||
71 | block = unblock; | ||
72 | if (sign <= 0) | ||
73 | newmask = block; | ||
74 | if (_NSIG_WORDS > 1 && sign > 0) | ||
75 | sigemptyset(¤t->blocked); | ||
76 | current->blocked.sig[0] = newmask; | ||
77 | recalc_sigpending(); | ||
78 | spin_unlock_irq(¤t->sighand->siglock); | ||
79 | |||
80 | regs->r0 = 0; /* special no error return */ | ||
81 | } | ||
82 | return oldmask; | ||
83 | } | ||
84 | |||
85 | asmlinkage int | ||
86 | osf_sigaction(int sig, const struct osf_sigaction __user *act, | ||
87 | struct osf_sigaction __user *oact) | ||
88 | { | ||
89 | struct k_sigaction new_ka, old_ka; | ||
90 | int ret; | ||
91 | |||
92 | if (act) { | ||
93 | old_sigset_t mask; | ||
94 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
95 | __get_user(new_ka.sa.sa_handler, &act->sa_handler) || | ||
96 | __get_user(new_ka.sa.sa_flags, &act->sa_flags)) | ||
97 | return -EFAULT; | ||
98 | __get_user(mask, &act->sa_mask); | ||
99 | siginitset(&new_ka.sa.sa_mask, mask); | ||
100 | new_ka.ka_restorer = NULL; | ||
101 | } | ||
102 | |||
103 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
104 | |||
105 | if (!ret && oact) { | ||
106 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
107 | __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || | ||
108 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags)) | ||
109 | return -EFAULT; | ||
110 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); | ||
111 | } | ||
112 | |||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | asmlinkage long | ||
117 | sys_rt_sigaction(int sig, const struct sigaction __user *act, | ||
118 | struct sigaction __user *oact, | ||
119 | size_t sigsetsize, void __user *restorer) | ||
120 | { | ||
121 | struct k_sigaction new_ka, old_ka; | ||
122 | int ret; | ||
123 | |||
124 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
125 | if (sigsetsize != sizeof(sigset_t)) | ||
126 | return -EINVAL; | ||
127 | |||
128 | if (act) { | ||
129 | new_ka.ka_restorer = restorer; | ||
130 | if (copy_from_user(&new_ka.sa, act, sizeof(*act))) | ||
131 | return -EFAULT; | ||
132 | } | ||
133 | |||
134 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
135 | |||
136 | if (!ret && oact) { | ||
137 | if (copy_to_user(oact, &old_ka.sa, sizeof(*oact))) | ||
138 | return -EFAULT; | ||
139 | } | ||
140 | |||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Atomically swap in the new signal mask, and wait for a signal. | ||
146 | */ | ||
147 | asmlinkage int | ||
148 | do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw) | ||
149 | { | ||
150 | sigset_t oldset; | ||
151 | |||
152 | mask &= _BLOCKABLE; | ||
153 | spin_lock_irq(¤t->sighand->siglock); | ||
154 | oldset = current->blocked; | ||
155 | siginitset(¤t->blocked, mask); | ||
156 | recalc_sigpending(); | ||
157 | spin_unlock_irq(¤t->sighand->siglock); | ||
158 | |||
159 | /* Indicate EINTR on return from any possible signal handler, | ||
160 | which will not come back through here, but via sigreturn. */ | ||
161 | regs->r0 = EINTR; | ||
162 | regs->r19 = 1; | ||
163 | |||
164 | while (1) { | ||
165 | current->state = TASK_INTERRUPTIBLE; | ||
166 | schedule(); | ||
167 | if (do_signal(&oldset, regs, sw, 0, 0)) | ||
168 | return -EINTR; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | asmlinkage int | ||
173 | do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize, | ||
174 | struct pt_regs *regs, struct switch_stack *sw) | ||
175 | { | ||
176 | sigset_t oldset, set; | ||
177 | |||
178 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
179 | if (sigsetsize != sizeof(sigset_t)) | ||
180 | return -EINVAL; | ||
181 | if (copy_from_user(&set, uset, sizeof(set))) | ||
182 | return -EFAULT; | ||
183 | |||
184 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
185 | spin_lock_irq(¤t->sighand->siglock); | ||
186 | oldset = current->blocked; | ||
187 | current->blocked = set; | ||
188 | recalc_sigpending(); | ||
189 | spin_unlock_irq(¤t->sighand->siglock); | ||
190 | |||
191 | /* Indicate EINTR on return from any possible signal handler, | ||
192 | which will not come back through here, but via sigreturn. */ | ||
193 | regs->r0 = EINTR; | ||
194 | regs->r19 = 1; | ||
195 | |||
196 | while (1) { | ||
197 | current->state = TASK_INTERRUPTIBLE; | ||
198 | schedule(); | ||
199 | if (do_signal(&oldset, regs, sw, 0, 0)) | ||
200 | return -EINTR; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | asmlinkage int | ||
205 | sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) | ||
206 | { | ||
207 | return do_sigaltstack(uss, uoss, rdusp()); | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Do a signal return; undo the signal stack. | ||
212 | */ | ||
213 | |||
214 | #if _NSIG_WORDS > 1 | ||
215 | # error "Non SA_SIGINFO frame needs rearranging" | ||
216 | #endif | ||
217 | |||
218 | struct sigframe | ||
219 | { | ||
220 | struct sigcontext sc; | ||
221 | unsigned int retcode[3]; | ||
222 | }; | ||
223 | |||
224 | struct rt_sigframe | ||
225 | { | ||
226 | struct siginfo info; | ||
227 | struct ucontext uc; | ||
228 | unsigned int retcode[3]; | ||
229 | }; | ||
230 | |||
231 | /* If this changes, userland unwinders that Know Things about our signal | ||
232 | frame will break. Do not undertake lightly. It also implies an ABI | ||
233 | change wrt the size of siginfo_t, which may cause some pain. */ | ||
234 | extern char compile_time_assert | ||
235 | [offsetof(struct rt_sigframe, uc.uc_mcontext) == 176 ? 1 : -1]; | ||
236 | |||
237 | #define INSN_MOV_R30_R16 0x47fe0410 | ||
238 | #define INSN_LDI_R0 0x201f0000 | ||
239 | #define INSN_CALLSYS 0x00000083 | ||
240 | |||
241 | static long | ||
242 | restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | ||
243 | struct switch_stack *sw) | ||
244 | { | ||
245 | unsigned long usp; | ||
246 | long i, err = __get_user(regs->pc, &sc->sc_pc); | ||
247 | |||
248 | sw->r26 = (unsigned long) ret_from_sys_call; | ||
249 | |||
250 | err |= __get_user(regs->r0, sc->sc_regs+0); | ||
251 | err |= __get_user(regs->r1, sc->sc_regs+1); | ||
252 | err |= __get_user(regs->r2, sc->sc_regs+2); | ||
253 | err |= __get_user(regs->r3, sc->sc_regs+3); | ||
254 | err |= __get_user(regs->r4, sc->sc_regs+4); | ||
255 | err |= __get_user(regs->r5, sc->sc_regs+5); | ||
256 | err |= __get_user(regs->r6, sc->sc_regs+6); | ||
257 | err |= __get_user(regs->r7, sc->sc_regs+7); | ||
258 | err |= __get_user(regs->r8, sc->sc_regs+8); | ||
259 | err |= __get_user(sw->r9, sc->sc_regs+9); | ||
260 | err |= __get_user(sw->r10, sc->sc_regs+10); | ||
261 | err |= __get_user(sw->r11, sc->sc_regs+11); | ||
262 | err |= __get_user(sw->r12, sc->sc_regs+12); | ||
263 | err |= __get_user(sw->r13, sc->sc_regs+13); | ||
264 | err |= __get_user(sw->r14, sc->sc_regs+14); | ||
265 | err |= __get_user(sw->r15, sc->sc_regs+15); | ||
266 | err |= __get_user(regs->r16, sc->sc_regs+16); | ||
267 | err |= __get_user(regs->r17, sc->sc_regs+17); | ||
268 | err |= __get_user(regs->r18, sc->sc_regs+18); | ||
269 | err |= __get_user(regs->r19, sc->sc_regs+19); | ||
270 | err |= __get_user(regs->r20, sc->sc_regs+20); | ||
271 | err |= __get_user(regs->r21, sc->sc_regs+21); | ||
272 | err |= __get_user(regs->r22, sc->sc_regs+22); | ||
273 | err |= __get_user(regs->r23, sc->sc_regs+23); | ||
274 | err |= __get_user(regs->r24, sc->sc_regs+24); | ||
275 | err |= __get_user(regs->r25, sc->sc_regs+25); | ||
276 | err |= __get_user(regs->r26, sc->sc_regs+26); | ||
277 | err |= __get_user(regs->r27, sc->sc_regs+27); | ||
278 | err |= __get_user(regs->r28, sc->sc_regs+28); | ||
279 | err |= __get_user(regs->gp, sc->sc_regs+29); | ||
280 | err |= __get_user(usp, sc->sc_regs+30); | ||
281 | wrusp(usp); | ||
282 | |||
283 | for (i = 0; i < 31; i++) | ||
284 | err |= __get_user(sw->fp[i], sc->sc_fpregs+i); | ||
285 | err |= __get_user(sw->fp[31], &sc->sc_fpcr); | ||
286 | |||
287 | return err; | ||
288 | } | ||
289 | |||
290 | /* Note that this syscall is also used by setcontext(3) to install | ||
291 | a given sigcontext. This because it's impossible to set *all* | ||
292 | registers and transfer control from userland. */ | ||
293 | |||
294 | asmlinkage void | ||
295 | do_sigreturn(struct sigcontext __user *sc, struct pt_regs *regs, | ||
296 | struct switch_stack *sw) | ||
297 | { | ||
298 | sigset_t set; | ||
299 | |||
300 | /* Verify that it's a good sigcontext before using it */ | ||
301 | if (!access_ok(VERIFY_READ, sc, sizeof(*sc))) | ||
302 | goto give_sigsegv; | ||
303 | if (__get_user(set.sig[0], &sc->sc_mask)) | ||
304 | goto give_sigsegv; | ||
305 | |||
306 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
307 | spin_lock_irq(¤t->sighand->siglock); | ||
308 | current->blocked = set; | ||
309 | recalc_sigpending(); | ||
310 | spin_unlock_irq(¤t->sighand->siglock); | ||
311 | |||
312 | if (restore_sigcontext(sc, regs, sw)) | ||
313 | goto give_sigsegv; | ||
314 | |||
315 | /* Send SIGTRAP if we're single-stepping: */ | ||
316 | if (ptrace_cancel_bpt (current)) { | ||
317 | siginfo_t info; | ||
318 | |||
319 | info.si_signo = SIGTRAP; | ||
320 | info.si_errno = 0; | ||
321 | info.si_code = TRAP_BRKPT; | ||
322 | info.si_addr = (void __user *) regs->pc; | ||
323 | info.si_trapno = 0; | ||
324 | send_sig_info(SIGTRAP, &info, current); | ||
325 | } | ||
326 | return; | ||
327 | |||
328 | give_sigsegv: | ||
329 | force_sig(SIGSEGV, current); | ||
330 | } | ||
331 | |||
332 | asmlinkage void | ||
333 | do_rt_sigreturn(struct rt_sigframe __user *frame, struct pt_regs *regs, | ||
334 | struct switch_stack *sw) | ||
335 | { | ||
336 | sigset_t set; | ||
337 | |||
338 | /* Verify that it's a good ucontext_t before using it */ | ||
339 | if (!access_ok(VERIFY_READ, &frame->uc, sizeof(frame->uc))) | ||
340 | goto give_sigsegv; | ||
341 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
342 | goto give_sigsegv; | ||
343 | |||
344 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
345 | spin_lock_irq(¤t->sighand->siglock); | ||
346 | current->blocked = set; | ||
347 | recalc_sigpending(); | ||
348 | spin_unlock_irq(¤t->sighand->siglock); | ||
349 | |||
350 | if (restore_sigcontext(&frame->uc.uc_mcontext, regs, sw)) | ||
351 | goto give_sigsegv; | ||
352 | |||
353 | /* Send SIGTRAP if we're single-stepping: */ | ||
354 | if (ptrace_cancel_bpt (current)) { | ||
355 | siginfo_t info; | ||
356 | |||
357 | info.si_signo = SIGTRAP; | ||
358 | info.si_errno = 0; | ||
359 | info.si_code = TRAP_BRKPT; | ||
360 | info.si_addr = (void __user *) regs->pc; | ||
361 | info.si_trapno = 0; | ||
362 | send_sig_info(SIGTRAP, &info, current); | ||
363 | } | ||
364 | return; | ||
365 | |||
366 | give_sigsegv: | ||
367 | force_sig(SIGSEGV, current); | ||
368 | } | ||
369 | |||
370 | |||
371 | /* | ||
372 | * Set up a signal frame. | ||
373 | */ | ||
374 | |||
375 | static inline void __user * | ||
376 | get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) | ||
377 | { | ||
378 | if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) | ||
379 | sp = current->sas_ss_sp + current->sas_ss_size; | ||
380 | |||
381 | return (void __user *)((sp - frame_size) & -32ul); | ||
382 | } | ||
383 | |||
384 | static long | ||
385 | setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | ||
386 | struct switch_stack *sw, unsigned long mask, unsigned long sp) | ||
387 | { | ||
388 | long i, err = 0; | ||
389 | |||
390 | err |= __put_user(on_sig_stack((unsigned long)sc), &sc->sc_onstack); | ||
391 | err |= __put_user(mask, &sc->sc_mask); | ||
392 | err |= __put_user(regs->pc, &sc->sc_pc); | ||
393 | err |= __put_user(8, &sc->sc_ps); | ||
394 | |||
395 | err |= __put_user(regs->r0 , sc->sc_regs+0); | ||
396 | err |= __put_user(regs->r1 , sc->sc_regs+1); | ||
397 | err |= __put_user(regs->r2 , sc->sc_regs+2); | ||
398 | err |= __put_user(regs->r3 , sc->sc_regs+3); | ||
399 | err |= __put_user(regs->r4 , sc->sc_regs+4); | ||
400 | err |= __put_user(regs->r5 , sc->sc_regs+5); | ||
401 | err |= __put_user(regs->r6 , sc->sc_regs+6); | ||
402 | err |= __put_user(regs->r7 , sc->sc_regs+7); | ||
403 | err |= __put_user(regs->r8 , sc->sc_regs+8); | ||
404 | err |= __put_user(sw->r9 , sc->sc_regs+9); | ||
405 | err |= __put_user(sw->r10 , sc->sc_regs+10); | ||
406 | err |= __put_user(sw->r11 , sc->sc_regs+11); | ||
407 | err |= __put_user(sw->r12 , sc->sc_regs+12); | ||
408 | err |= __put_user(sw->r13 , sc->sc_regs+13); | ||
409 | err |= __put_user(sw->r14 , sc->sc_regs+14); | ||
410 | err |= __put_user(sw->r15 , sc->sc_regs+15); | ||
411 | err |= __put_user(regs->r16, sc->sc_regs+16); | ||
412 | err |= __put_user(regs->r17, sc->sc_regs+17); | ||
413 | err |= __put_user(regs->r18, sc->sc_regs+18); | ||
414 | err |= __put_user(regs->r19, sc->sc_regs+19); | ||
415 | err |= __put_user(regs->r20, sc->sc_regs+20); | ||
416 | err |= __put_user(regs->r21, sc->sc_regs+21); | ||
417 | err |= __put_user(regs->r22, sc->sc_regs+22); | ||
418 | err |= __put_user(regs->r23, sc->sc_regs+23); | ||
419 | err |= __put_user(regs->r24, sc->sc_regs+24); | ||
420 | err |= __put_user(regs->r25, sc->sc_regs+25); | ||
421 | err |= __put_user(regs->r26, sc->sc_regs+26); | ||
422 | err |= __put_user(regs->r27, sc->sc_regs+27); | ||
423 | err |= __put_user(regs->r28, sc->sc_regs+28); | ||
424 | err |= __put_user(regs->gp , sc->sc_regs+29); | ||
425 | err |= __put_user(sp, sc->sc_regs+30); | ||
426 | err |= __put_user(0, sc->sc_regs+31); | ||
427 | |||
428 | for (i = 0; i < 31; i++) | ||
429 | err |= __put_user(sw->fp[i], sc->sc_fpregs+i); | ||
430 | err |= __put_user(0, sc->sc_fpregs+31); | ||
431 | err |= __put_user(sw->fp[31], &sc->sc_fpcr); | ||
432 | |||
433 | err |= __put_user(regs->trap_a0, &sc->sc_traparg_a0); | ||
434 | err |= __put_user(regs->trap_a1, &sc->sc_traparg_a1); | ||
435 | err |= __put_user(regs->trap_a2, &sc->sc_traparg_a2); | ||
436 | |||
437 | return err; | ||
438 | } | ||
439 | |||
440 | static void | ||
441 | setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, | ||
442 | struct pt_regs *regs, struct switch_stack * sw) | ||
443 | { | ||
444 | unsigned long oldsp, r26, err = 0; | ||
445 | struct sigframe __user *frame; | ||
446 | |||
447 | oldsp = rdusp(); | ||
448 | frame = get_sigframe(ka, oldsp, sizeof(*frame)); | ||
449 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
450 | goto give_sigsegv; | ||
451 | |||
452 | err |= setup_sigcontext(&frame->sc, regs, sw, set->sig[0], oldsp); | ||
453 | if (err) | ||
454 | goto give_sigsegv; | ||
455 | |||
456 | /* Set up to return from userspace. If provided, use a stub | ||
457 | already in userspace. */ | ||
458 | if (ka->ka_restorer) { | ||
459 | r26 = (unsigned long) ka->ka_restorer; | ||
460 | } else { | ||
461 | err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); | ||
462 | err |= __put_user(INSN_LDI_R0+__NR_sigreturn, frame->retcode+1); | ||
463 | err |= __put_user(INSN_CALLSYS, frame->retcode+2); | ||
464 | imb(); | ||
465 | r26 = (unsigned long) frame->retcode; | ||
466 | } | ||
467 | |||
468 | /* Check that everything was written properly. */ | ||
469 | if (err) | ||
470 | goto give_sigsegv; | ||
471 | |||
472 | /* "Return" to the handler */ | ||
473 | regs->r26 = r26; | ||
474 | regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; | ||
475 | regs->r16 = sig; /* a0: signal number */ | ||
476 | regs->r17 = 0; /* a1: exception code */ | ||
477 | regs->r18 = (unsigned long) &frame->sc; /* a2: sigcontext pointer */ | ||
478 | wrusp((unsigned long) frame); | ||
479 | |||
480 | #if DEBUG_SIG | ||
481 | printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", | ||
482 | current->comm, current->pid, frame, regs->pc, regs->r26); | ||
483 | #endif | ||
484 | |||
485 | return; | ||
486 | |||
487 | give_sigsegv: | ||
488 | force_sigsegv(sig, current); | ||
489 | } | ||
490 | |||
491 | static void | ||
492 | setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
493 | sigset_t *set, struct pt_regs *regs, struct switch_stack * sw) | ||
494 | { | ||
495 | unsigned long oldsp, r26, err = 0; | ||
496 | struct rt_sigframe __user *frame; | ||
497 | |||
498 | oldsp = rdusp(); | ||
499 | frame = get_sigframe(ka, oldsp, sizeof(*frame)); | ||
500 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
501 | goto give_sigsegv; | ||
502 | |||
503 | err |= copy_siginfo_to_user(&frame->info, info); | ||
504 | |||
505 | /* Create the ucontext. */ | ||
506 | err |= __put_user(0, &frame->uc.uc_flags); | ||
507 | err |= __put_user(0, &frame->uc.uc_link); | ||
508 | err |= __put_user(set->sig[0], &frame->uc.uc_osf_sigmask); | ||
509 | err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | ||
510 | err |= __put_user(sas_ss_flags(oldsp), &frame->uc.uc_stack.ss_flags); | ||
511 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
512 | err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, sw, | ||
513 | set->sig[0], oldsp); | ||
514 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
515 | if (err) | ||
516 | goto give_sigsegv; | ||
517 | |||
518 | /* Set up to return from userspace. If provided, use a stub | ||
519 | already in userspace. */ | ||
520 | if (ka->ka_restorer) { | ||
521 | r26 = (unsigned long) ka->ka_restorer; | ||
522 | } else { | ||
523 | err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); | ||
524 | err |= __put_user(INSN_LDI_R0+__NR_rt_sigreturn, | ||
525 | frame->retcode+1); | ||
526 | err |= __put_user(INSN_CALLSYS, frame->retcode+2); | ||
527 | imb(); | ||
528 | r26 = (unsigned long) frame->retcode; | ||
529 | } | ||
530 | |||
531 | if (err) | ||
532 | goto give_sigsegv; | ||
533 | |||
534 | /* "Return" to the handler */ | ||
535 | regs->r26 = r26; | ||
536 | regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; | ||
537 | regs->r16 = sig; /* a0: signal number */ | ||
538 | regs->r17 = (unsigned long) &frame->info; /* a1: siginfo pointer */ | ||
539 | regs->r18 = (unsigned long) &frame->uc; /* a2: ucontext pointer */ | ||
540 | wrusp((unsigned long) frame); | ||
541 | |||
542 | #if DEBUG_SIG | ||
543 | printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", | ||
544 | current->comm, current->pid, frame, regs->pc, regs->r26); | ||
545 | #endif | ||
546 | |||
547 | return; | ||
548 | |||
549 | give_sigsegv: | ||
550 | force_sigsegv(sig, current); | ||
551 | } | ||
552 | |||
553 | |||
554 | /* | ||
555 | * OK, we're invoking a handler. | ||
556 | */ | ||
557 | static inline void | ||
558 | handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
559 | sigset_t *oldset, struct pt_regs * regs, struct switch_stack *sw) | ||
560 | { | ||
561 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
562 | setup_rt_frame(sig, ka, info, oldset, regs, sw); | ||
563 | else | ||
564 | setup_frame(sig, ka, oldset, regs, sw); | ||
565 | |||
566 | if (ka->sa.sa_flags & SA_RESETHAND) | ||
567 | ka->sa.sa_handler = SIG_DFL; | ||
568 | |||
569 | if (!(ka->sa.sa_flags & SA_NODEFER)) { | ||
570 | spin_lock_irq(¤t->sighand->siglock); | ||
571 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | ||
572 | sigaddset(¤t->blocked,sig); | ||
573 | recalc_sigpending(); | ||
574 | spin_unlock_irq(¤t->sighand->siglock); | ||
575 | } | ||
576 | } | ||
577 | |||
578 | static inline void | ||
579 | syscall_restart(unsigned long r0, unsigned long r19, | ||
580 | struct pt_regs *regs, struct k_sigaction *ka) | ||
581 | { | ||
582 | switch (regs->r0) { | ||
583 | case ERESTARTSYS: | ||
584 | if (!(ka->sa.sa_flags & SA_RESTART)) { | ||
585 | case ERESTARTNOHAND: | ||
586 | regs->r0 = EINTR; | ||
587 | break; | ||
588 | } | ||
589 | /* fallthrough */ | ||
590 | case ERESTARTNOINTR: | ||
591 | regs->r0 = r0; /* reset v0 and a3 and replay syscall */ | ||
592 | regs->r19 = r19; | ||
593 | regs->pc -= 4; | ||
594 | break; | ||
595 | case ERESTART_RESTARTBLOCK: | ||
596 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
597 | regs->r0 = EINTR; | ||
598 | break; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | |||
603 | /* | ||
604 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
605 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
606 | * mistake. | ||
607 | * | ||
608 | * Note that we go through the signals twice: once to check the signals that | ||
609 | * the kernel can handle, and then we build all the user-level signal handling | ||
610 | * stack-frames in one go after that. | ||
611 | * | ||
612 | * "r0" and "r19" are the registers we need to restore for system call | ||
613 | * restart. "r0" is also used as an indicator whether we can restart at | ||
614 | * all (if we get here from anything but a syscall return, it will be 0) | ||
615 | */ | ||
616 | static int | ||
617 | do_signal(sigset_t *oldset, struct pt_regs * regs, struct switch_stack * sw, | ||
618 | unsigned long r0, unsigned long r19) | ||
619 | { | ||
620 | siginfo_t info; | ||
621 | int signr; | ||
622 | unsigned long single_stepping = ptrace_cancel_bpt(current); | ||
623 | struct k_sigaction ka; | ||
624 | |||
625 | if (!oldset) | ||
626 | oldset = ¤t->blocked; | ||
627 | |||
628 | /* This lets the debugger run, ... */ | ||
629 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
630 | /* ... so re-check the single stepping. */ | ||
631 | single_stepping |= ptrace_cancel_bpt(current); | ||
632 | |||
633 | if (signr > 0) { | ||
634 | /* Whee! Actually deliver the signal. */ | ||
635 | if (r0) syscall_restart(r0, r19, regs, &ka); | ||
636 | handle_signal(signr, &ka, &info, oldset, regs, sw); | ||
637 | if (single_stepping) | ||
638 | ptrace_set_bpt(current); /* re-set bpt */ | ||
639 | return 1; | ||
640 | } | ||
641 | |||
642 | if (r0) { | ||
643 | switch (regs->r0) { | ||
644 | case ERESTARTNOHAND: | ||
645 | case ERESTARTSYS: | ||
646 | case ERESTARTNOINTR: | ||
647 | /* Reset v0 and a3 and replay syscall. */ | ||
648 | regs->r0 = r0; | ||
649 | regs->r19 = r19; | ||
650 | regs->pc -= 4; | ||
651 | break; | ||
652 | case ERESTART_RESTARTBLOCK: | ||
653 | /* Force v0 to the restart syscall and reply. */ | ||
654 | regs->r0 = __NR_restart_syscall; | ||
655 | regs->pc -= 4; | ||
656 | break; | ||
657 | } | ||
658 | } | ||
659 | if (single_stepping) | ||
660 | ptrace_set_bpt(current); /* re-set breakpoint */ | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | void | ||
666 | do_notify_resume(sigset_t *oldset, struct pt_regs *regs, | ||
667 | struct switch_stack *sw, unsigned long r0, | ||
668 | unsigned long r19, unsigned long thread_info_flags) | ||
669 | { | ||
670 | if (thread_info_flags & _TIF_SIGPENDING) | ||
671 | do_signal(oldset, regs, sw, r0, r19); | ||
672 | } | ||