diff options
author | Chris Zankel <czankel@tensilica.com> | 2005-06-24 01:01:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-24 03:05:21 -0400 |
commit | 5a0015d62668e64c8b6e02e360fbbea121bfd5e6 (patch) | |
tree | ed879f8cbe0efee21ad861f38c4024bdcf25df9b /arch/xtensa/kernel/signal.c | |
parent | 4bedea94545165364618d403d03b61d797acba0b (diff) |
[PATCH] xtensa: Architecture support for Tensilica Xtensa Part 3
The attached patches provides part 3 of an architecture implementation for the
Tensilica Xtensa CPU series.
Signed-off-by: Chris Zankel <chris@zankel.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/xtensa/kernel/signal.c')
-rw-r--r-- | arch/xtensa/kernel/signal.c | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c new file mode 100644 index 000000000000..df6e1e17b096 --- /dev/null +++ b/arch/xtensa/kernel/signal.c | |||
@@ -0,0 +1,713 @@ | |||
1 | // TODO coprocessor stuff | ||
2 | /* | ||
3 | * linux/arch/xtensa/kernel/signal.c | ||
4 | * | ||
5 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
6 | * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson | ||
7 | * | ||
8 | * Joe Taylor <joe@tensilica.com> | ||
9 | * Chris Zankel <chris@zankel.net> | ||
10 | * | ||
11 | * | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <xtensa/config/core.h> | ||
16 | #include <xtensa/hal.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/smp.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/signal.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <linux/ptrace.h> | ||
26 | #include <linux/unistd.h> | ||
27 | #include <linux/stddef.h> | ||
28 | #include <linux/personality.h> | ||
29 | #include <asm/ucontext.h> | ||
30 | #include <asm/uaccess.h> | ||
31 | #include <asm/pgtable.h> | ||
32 | #include <asm/cacheflush.h> | ||
33 | |||
34 | #define DEBUG_SIG 0 | ||
35 | |||
36 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
37 | |||
38 | asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, | ||
39 | struct rusage * ru); | ||
40 | asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); | ||
41 | |||
42 | extern struct task_struct *coproc_owners[]; | ||
43 | |||
44 | |||
45 | /* | ||
46 | * Atomically swap in the new signal mask, and wait for a signal. | ||
47 | */ | ||
48 | |||
49 | int sys_sigsuspend(struct pt_regs *regs) | ||
50 | { | ||
51 | old_sigset_t mask = (old_sigset_t) regs->areg[3]; | ||
52 | sigset_t saveset; | ||
53 | |||
54 | mask &= _BLOCKABLE; | ||
55 | spin_lock_irq(¤t->sighand->siglock); | ||
56 | saveset = current->blocked; | ||
57 | siginitset(¤t->blocked, mask); | ||
58 | recalc_sigpending(); | ||
59 | spin_unlock_irq(¤t->sighand->siglock); | ||
60 | |||
61 | regs->areg[2] = -EINTR; | ||
62 | while (1) { | ||
63 | current->state = TASK_INTERRUPTIBLE; | ||
64 | schedule(); | ||
65 | if (do_signal(regs, &saveset)) | ||
66 | return -EINTR; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | asmlinkage int | ||
71 | sys_rt_sigsuspend(struct pt_regs *regs) | ||
72 | { | ||
73 | sigset_t *unewset = (sigset_t *) regs->areg[4]; | ||
74 | size_t sigsetsize = (size_t) regs->areg[3]; | ||
75 | sigset_t saveset, newset; | ||
76 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
77 | if (sigsetsize != sizeof(sigset_t)) | ||
78 | return -EINVAL; | ||
79 | |||
80 | if (copy_from_user(&newset, unewset, sizeof(newset))) | ||
81 | return -EFAULT; | ||
82 | sigdelsetmask(&newset, ~_BLOCKABLE); | ||
83 | spin_lock_irq(¤t->sighand->siglock); | ||
84 | saveset = current->blocked; | ||
85 | current->blocked = newset; | ||
86 | recalc_sigpending(); | ||
87 | spin_unlock_irq(¤t->sighand->siglock); | ||
88 | |||
89 | regs->areg[2] = -EINTR; | ||
90 | while (1) { | ||
91 | current->state = TASK_INTERRUPTIBLE; | ||
92 | schedule(); | ||
93 | if (do_signal(regs, &saveset)) | ||
94 | return -EINTR; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | asmlinkage int | ||
99 | sys_sigaction(int sig, const struct old_sigaction *act, | ||
100 | struct old_sigaction *oact) | ||
101 | { | ||
102 | struct k_sigaction new_ka, old_ka; | ||
103 | int ret; | ||
104 | |||
105 | if (act) { | ||
106 | old_sigset_t mask; | ||
107 | if (verify_area(VERIFY_READ, act, sizeof(*act)) || | ||
108 | __get_user(new_ka.sa.sa_handler, &act->sa_handler) || | ||
109 | __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) | ||
110 | return -EFAULT; | ||
111 | __get_user(new_ka.sa.sa_flags, &act->sa_flags); | ||
112 | __get_user(mask, &act->sa_mask); | ||
113 | siginitset(&new_ka.sa.sa_mask, mask); | ||
114 | } | ||
115 | |||
116 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
117 | |||
118 | if (!ret && oact) { | ||
119 | if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
120 | __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || | ||
121 | __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) | ||
122 | return -EFAULT; | ||
123 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags); | ||
124 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); | ||
125 | } | ||
126 | |||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | asmlinkage int | ||
131 | sys_sigaltstack(struct pt_regs *regs) | ||
132 | { | ||
133 | const stack_t *uss = (stack_t *) regs->areg[4]; | ||
134 | stack_t *uoss = (stack_t *) regs->areg[3]; | ||
135 | |||
136 | if (regs->depc > 64) | ||
137 | panic ("Double exception sys_sigreturn\n"); | ||
138 | |||
139 | |||
140 | return do_sigaltstack(uss, uoss, regs->areg[1]); | ||
141 | } | ||
142 | |||
143 | |||
144 | /* | ||
145 | * Do a signal return; undo the signal stack. | ||
146 | */ | ||
147 | |||
148 | struct sigframe | ||
149 | { | ||
150 | struct sigcontext sc; | ||
151 | struct _cpstate cpstate; | ||
152 | unsigned long extramask[_NSIG_WORDS-1]; | ||
153 | unsigned char retcode[6]; | ||
154 | unsigned int reserved[4]; /* Reserved area for chaining */ | ||
155 | unsigned int window[4]; /* Window of 4 registers for initial context */ | ||
156 | }; | ||
157 | |||
158 | struct rt_sigframe | ||
159 | { | ||
160 | struct siginfo info; | ||
161 | struct ucontext uc; | ||
162 | struct _cpstate cpstate; | ||
163 | unsigned char retcode[6]; | ||
164 | unsigned int reserved[4]; /* Reserved area for chaining */ | ||
165 | unsigned int window[4]; /* Window of 4 registers for initial context */ | ||
166 | }; | ||
167 | |||
168 | extern void release_all_cp (struct task_struct *); | ||
169 | |||
170 | |||
171 | // FIXME restore_cpextra | ||
172 | static inline int | ||
173 | restore_cpextra (struct _cpstate *buf) | ||
174 | { | ||
175 | #if 0 | ||
176 | /* The signal handler may have used coprocessors in which | ||
177 | * case they are still enabled. We disable them to force a | ||
178 | * reloading of the original task's CP state by the lazy | ||
179 | * context-switching mechanisms of CP exception handling. | ||
180 | * Also, we essentially discard any coprocessor state that the | ||
181 | * signal handler created. */ | ||
182 | |||
183 | struct task_struct *tsk = current; | ||
184 | release_all_cp(tsk); | ||
185 | return __copy_from_user(tsk->thread.cpextra, buf, TOTAL_CPEXTRA_SIZE); | ||
186 | #endif | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* Note: We don't copy double exception 'tregs', we have to finish double exc. first before we return to signal handler! This dbl.exc.handler might cause another double exception, but I think we are fine as the situation is the same as if we had returned to the signal handerl and got an interrupt immediately... | ||
191 | */ | ||
192 | |||
193 | |||
194 | static int | ||
195 | restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) | ||
196 | { | ||
197 | struct thread_struct *thread; | ||
198 | unsigned int err = 0; | ||
199 | unsigned long ps; | ||
200 | struct _cpstate *buf; | ||
201 | |||
202 | #define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) | ||
203 | COPY(pc); | ||
204 | COPY(depc); | ||
205 | COPY(wmask); | ||
206 | COPY(lbeg); | ||
207 | COPY(lend); | ||
208 | COPY(lcount); | ||
209 | COPY(sar); | ||
210 | COPY(windowbase); | ||
211 | COPY(windowstart); | ||
212 | #undef COPY | ||
213 | |||
214 | /* For PS, restore only PS.CALLINC. | ||
215 | * Assume that all other bits are either the same as for the signal | ||
216 | * handler, or the user mode value doesn't matter (e.g. PS.OWB). | ||
217 | */ | ||
218 | err |= __get_user(ps, &sc->sc_ps); | ||
219 | regs->ps = (regs->ps & ~XCHAL_PS_CALLINC_MASK) | ||
220 | | (ps & XCHAL_PS_CALLINC_MASK); | ||
221 | |||
222 | /* Additional corruption checks */ | ||
223 | |||
224 | if ((regs->windowbase >= (XCHAL_NUM_AREGS/4)) | ||
225 | || ((regs->windowstart & ~((1<<(XCHAL_NUM_AREGS/4)) - 1)) != 0) ) | ||
226 | err = 1; | ||
227 | if ((regs->lcount > 0) | ||
228 | && ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) ) | ||
229 | err = 1; | ||
230 | |||
231 | /* Restore extended register state. | ||
232 | * See struct thread_struct in processor.h. | ||
233 | */ | ||
234 | thread = ¤t->thread; | ||
235 | |||
236 | err |= __copy_from_user (regs->areg, sc->sc_areg, XCHAL_NUM_AREGS*4); | ||
237 | err |= __get_user(buf, &sc->sc_cpstate); | ||
238 | if (buf) { | ||
239 | if (verify_area(VERIFY_READ, buf, sizeof(*buf))) | ||
240 | goto badframe; | ||
241 | err |= restore_cpextra(buf); | ||
242 | } | ||
243 | |||
244 | regs->syscall = -1; /* disable syscall checks */ | ||
245 | return err; | ||
246 | |||
247 | badframe: | ||
248 | return 1; | ||
249 | } | ||
250 | |||
251 | static inline void | ||
252 | flush_my_cpstate(struct task_struct *tsk) | ||
253 | { | ||
254 | unsigned long flags; | ||
255 | local_irq_save(flags); | ||
256 | |||
257 | #if 0 // FIXME | ||
258 | for (i = 0; i < XCHAL_CP_NUM; i++) { | ||
259 | if (tsk == coproc_owners[i]) { | ||
260 | xthal_validate_cp(i); | ||
261 | xthal_save_cpregs(tsk->thread.cpregs_ptr[i], i); | ||
262 | |||
263 | /* Invalidate and "disown" the cp to allow | ||
264 | * callers the chance to reset cp state in the | ||
265 | * task_struct. */ | ||
266 | |||
267 | xthal_invalidate_cp(i); | ||
268 | coproc_owners[i] = 0; | ||
269 | } | ||
270 | } | ||
271 | #endif | ||
272 | local_irq_restore(flags); | ||
273 | } | ||
274 | |||
275 | /* Return codes: | ||
276 | 0: nothing saved | ||
277 | 1: stuff to save, successful | ||
278 | -1: stuff to save, error happened | ||
279 | */ | ||
280 | static int | ||
281 | save_cpextra (struct _cpstate *buf) | ||
282 | { | ||
283 | #if (XCHAL_EXTRA_SA_SIZE == 0) && (XCHAL_CP_NUM == 0) | ||
284 | return 0; | ||
285 | #else | ||
286 | |||
287 | /* FIXME: If a task has never used a coprocessor, there is | ||
288 | * no need to save and restore anything. Tracking this | ||
289 | * information would allow us to optimize this section. | ||
290 | * Perhaps we can use current->used_math or (current->flags & | ||
291 | * PF_USEDFPU) or define a new field in the thread | ||
292 | * structure. */ | ||
293 | |||
294 | /* We flush any live, task-owned cp state to the task_struct, | ||
295 | * then copy it all to the sigframe. Then we clear all | ||
296 | * cp/extra state in the task_struct, effectively | ||
297 | * clearing/resetting all cp/extra state for the signal | ||
298 | * handler (cp-exception handling will load these new values | ||
299 | * into the cp/extra registers.) This step is important for | ||
300 | * things like a floating-point cp, where the OS must reset | ||
301 | * the FCR to the default rounding mode. */ | ||
302 | |||
303 | int err = 0; | ||
304 | struct task_struct *tsk = current; | ||
305 | |||
306 | flush_my_cpstate(tsk); | ||
307 | /* Note that we just copy everything: 'extra' and 'cp' state together.*/ | ||
308 | err |= __copy_to_user(buf, tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE); | ||
309 | memset(tsk->thread.cp_save, 0, XTENSA_CP_EXTRA_SIZE); | ||
310 | |||
311 | #if (XTENSA_CP_EXTRA_SIZE == 0) | ||
312 | #error Sanity check on memset above, cpextra_size should not be zero. | ||
313 | #endif | ||
314 | |||
315 | return err ? -1 : 1; | ||
316 | #endif | ||
317 | } | ||
318 | |||
319 | static int | ||
320 | setup_sigcontext(struct sigcontext *sc, struct _cpstate *cpstate, | ||
321 | struct pt_regs *regs, unsigned long mask) | ||
322 | { | ||
323 | struct thread_struct *thread; | ||
324 | int err = 0; | ||
325 | |||
326 | //printk("setup_sigcontext\n"); | ||
327 | #define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) | ||
328 | COPY(pc); | ||
329 | COPY(ps); | ||
330 | COPY(depc); | ||
331 | COPY(wmask); | ||
332 | COPY(lbeg); | ||
333 | COPY(lend); | ||
334 | COPY(lcount); | ||
335 | COPY(sar); | ||
336 | COPY(windowbase); | ||
337 | COPY(windowstart); | ||
338 | #undef COPY | ||
339 | |||
340 | /* Save extended register state. | ||
341 | * See struct thread_struct in processor.h. | ||
342 | */ | ||
343 | thread = ¤t->thread; | ||
344 | err |= __copy_to_user (sc->sc_areg, regs->areg, XCHAL_NUM_AREGS * 4); | ||
345 | err |= save_cpextra(cpstate); | ||
346 | err |= __put_user(err ? NULL : cpstate, &sc->sc_cpstate); | ||
347 | /* non-iBCS2 extensions.. */ | ||
348 | err |= __put_user(mask, &sc->oldmask); | ||
349 | |||
350 | return err; | ||
351 | } | ||
352 | |||
353 | asmlinkage int sys_sigreturn(struct pt_regs *regs) | ||
354 | { | ||
355 | struct sigframe *frame = (struct sigframe *)regs->areg[1]; | ||
356 | sigset_t set; | ||
357 | if (regs->depc > 64) | ||
358 | panic ("Double exception sys_sigreturn\n"); | ||
359 | |||
360 | if (verify_area(VERIFY_READ, frame, sizeof(*frame))) | ||
361 | goto badframe; | ||
362 | |||
363 | if (__get_user(set.sig[0], &frame->sc.oldmask) | ||
364 | || (_NSIG_WORDS > 1 | ||
365 | && __copy_from_user(&set.sig[1], &frame->extramask, | ||
366 | sizeof(frame->extramask)))) | ||
367 | goto badframe; | ||
368 | |||
369 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
370 | |||
371 | spin_lock_irq(¤t->sighand->siglock); | ||
372 | current->blocked = set; | ||
373 | recalc_sigpending(); | ||
374 | spin_unlock_irq(¤t->sighand->siglock); | ||
375 | |||
376 | if (restore_sigcontext(regs, &frame->sc)) | ||
377 | goto badframe; | ||
378 | return regs->areg[2]; | ||
379 | |||
380 | badframe: | ||
381 | force_sig(SIGSEGV, current); | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) | ||
386 | { | ||
387 | struct rt_sigframe *frame = (struct rt_sigframe *)regs->areg[1]; | ||
388 | sigset_t set; | ||
389 | stack_t st; | ||
390 | int ret; | ||
391 | if (regs->depc > 64) | ||
392 | { | ||
393 | printk("!!!!!!! DEPC !!!!!!!\n"); | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | if (verify_area(VERIFY_READ, frame, sizeof(*frame))) | ||
398 | goto badframe; | ||
399 | |||
400 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
401 | goto badframe; | ||
402 | |||
403 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
404 | spin_lock_irq(¤t->sighand->siglock); | ||
405 | current->blocked = set; | ||
406 | recalc_sigpending(); | ||
407 | spin_unlock_irq(¤t->sighand->siglock); | ||
408 | |||
409 | if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) | ||
410 | goto badframe; | ||
411 | ret = regs->areg[2]; | ||
412 | |||
413 | if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) | ||
414 | goto badframe; | ||
415 | /* It is more difficult to avoid calling this function than to | ||
416 | call it and ignore errors. */ | ||
417 | do_sigaltstack(&st, NULL, regs->areg[1]); | ||
418 | |||
419 | return ret; | ||
420 | |||
421 | badframe: | ||
422 | force_sig(SIGSEGV, current); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * Set up a signal frame. | ||
428 | */ | ||
429 | |||
430 | /* | ||
431 | * Determine which stack to use.. | ||
432 | */ | ||
433 | static inline void * | ||
434 | get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) | ||
435 | { | ||
436 | if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) | ||
437 | sp = current->sas_ss_sp + current->sas_ss_size; | ||
438 | |||
439 | return (void *)((sp - frame_size) & -16ul); | ||
440 | } | ||
441 | |||
442 | #define USE_SIGRETURN 0 | ||
443 | #define USE_RT_SIGRETURN 1 | ||
444 | |||
445 | static int | ||
446 | gen_return_code(unsigned char *codemem, unsigned int use_rt_sigreturn) | ||
447 | { | ||
448 | unsigned int retcall; | ||
449 | int err = 0; | ||
450 | |||
451 | #if 0 | ||
452 | /* Ignoring SA_RESTORER for now; it's supposed to be obsolete, | ||
453 | * and the xtensa glibc doesn't use it. | ||
454 | */ | ||
455 | if (ka->sa.sa_flags & SA_RESTORER) { | ||
456 | regs->pr = (unsigned long) ka->sa.sa_restorer; | ||
457 | } else | ||
458 | #endif /* 0 */ | ||
459 | { | ||
460 | |||
461 | #if (__NR_sigreturn > 255) || (__NR_rt_sigreturn > 255) | ||
462 | |||
463 | /* The 12-bit immediate is really split up within the 24-bit MOVI | ||
464 | * instruction. As long as the above system call numbers fit within | ||
465 | * 8-bits, the following code works fine. See the Xtensa ISA for | ||
466 | * details. | ||
467 | */ | ||
468 | |||
469 | #error Generating the MOVI instruction below breaks! | ||
470 | #endif | ||
471 | |||
472 | retcall = use_rt_sigreturn ? __NR_rt_sigreturn : __NR_sigreturn; | ||
473 | |||
474 | #ifdef __XTENSA_EB__ /* Big Endian version */ | ||
475 | /* Generate instruction: MOVI a2, retcall */ | ||
476 | err |= __put_user(0x22, &codemem[0]); | ||
477 | err |= __put_user(0x0a, &codemem[1]); | ||
478 | err |= __put_user(retcall, &codemem[2]); | ||
479 | /* Generate instruction: SYSCALL */ | ||
480 | err |= __put_user(0x00, &codemem[3]); | ||
481 | err |= __put_user(0x05, &codemem[4]); | ||
482 | err |= __put_user(0x00, &codemem[5]); | ||
483 | |||
484 | #elif defined __XTENSA_EL__ /* Little Endian version */ | ||
485 | /* Generate instruction: MOVI a2, retcall */ | ||
486 | err |= __put_user(0x22, &codemem[0]); | ||
487 | err |= __put_user(0xa0, &codemem[1]); | ||
488 | err |= __put_user(retcall, &codemem[2]); | ||
489 | /* Generate instruction: SYSCALL */ | ||
490 | err |= __put_user(0x00, &codemem[3]); | ||
491 | err |= __put_user(0x50, &codemem[4]); | ||
492 | err |= __put_user(0x00, &codemem[5]); | ||
493 | #else | ||
494 | #error Must use compiler for Xtensa processors. | ||
495 | #endif | ||
496 | } | ||
497 | |||
498 | /* Flush generated code out of the data cache */ | ||
499 | |||
500 | if (err == 0) | ||
501 | __flush_invalidate_cache_range((unsigned long)codemem, 6UL); | ||
502 | |||
503 | return err; | ||
504 | } | ||
505 | |||
506 | static void | ||
507 | set_thread_state(struct pt_regs *regs, void *stack, unsigned char *retaddr, | ||
508 | void *handler, unsigned long arg1, void *arg2, void *arg3) | ||
509 | { | ||
510 | /* Set up registers for signal handler */ | ||
511 | start_thread(regs, (unsigned long) handler, (unsigned long) stack); | ||
512 | |||
513 | /* Set up a stack frame for a call4 | ||
514 | * Note: PS.CALLINC is set to one by start_thread | ||
515 | */ | ||
516 | regs->areg[4] = (((unsigned long) retaddr) & 0x3fffffff) | 0x40000000; | ||
517 | regs->areg[6] = arg1; | ||
518 | regs->areg[7] = (unsigned long) arg2; | ||
519 | regs->areg[8] = (unsigned long) arg3; | ||
520 | } | ||
521 | |||
522 | static void setup_frame(int sig, struct k_sigaction *ka, | ||
523 | sigset_t *set, struct pt_regs *regs) | ||
524 | { | ||
525 | struct sigframe *frame; | ||
526 | int err = 0; | ||
527 | int signal; | ||
528 | |||
529 | frame = get_sigframe(ka, regs->areg[1], sizeof(*frame)); | ||
530 | if (regs->depc > 64) | ||
531 | { | ||
532 | printk("!!!!!!! DEPC !!!!!!!\n"); | ||
533 | return; | ||
534 | } | ||
535 | |||
536 | |||
537 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
538 | goto give_sigsegv; | ||
539 | |||
540 | signal = current_thread_info()->exec_domain | ||
541 | && current_thread_info()->exec_domain->signal_invmap | ||
542 | && sig < 32 | ||
543 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
544 | : sig; | ||
545 | |||
546 | err |= setup_sigcontext(&frame->sc, &frame->cpstate, regs, set->sig[0]); | ||
547 | |||
548 | if (_NSIG_WORDS > 1) { | ||
549 | err |= __copy_to_user(frame->extramask, &set->sig[1], | ||
550 | sizeof(frame->extramask)); | ||
551 | } | ||
552 | |||
553 | /* Create sys_sigreturn syscall in stack frame */ | ||
554 | err |= gen_return_code(frame->retcode, USE_SIGRETURN); | ||
555 | |||
556 | if (err) | ||
557 | goto give_sigsegv; | ||
558 | |||
559 | /* Create signal handler execution context. | ||
560 | * Return context not modified until this point. | ||
561 | */ | ||
562 | set_thread_state(regs, frame, frame->retcode, | ||
563 | ka->sa.sa_handler, signal, &frame->sc, NULL); | ||
564 | |||
565 | /* Set access mode to USER_DS. Nomenclature is outdated, but | ||
566 | * functionality is used in uaccess.h | ||
567 | */ | ||
568 | set_fs(USER_DS); | ||
569 | |||
570 | |||
571 | #if DEBUG_SIG | ||
572 | printk("SIG deliver (%s:%d): signal=%d sp=%p pc=%08x\n", | ||
573 | current->comm, current->pid, signal, frame, regs->pc); | ||
574 | #endif | ||
575 | |||
576 | return; | ||
577 | |||
578 | give_sigsegv: | ||
579 | if (sig == SIGSEGV) | ||
580 | ka->sa.sa_handler = SIG_DFL; | ||
581 | force_sig(SIGSEGV, current); | ||
582 | } | ||
583 | |||
584 | static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
585 | sigset_t *set, struct pt_regs *regs) | ||
586 | { | ||
587 | struct rt_sigframe *frame; | ||
588 | int err = 0; | ||
589 | int signal; | ||
590 | |||
591 | frame = get_sigframe(ka, regs->areg[1], sizeof(*frame)); | ||
592 | if (regs->depc > 64) | ||
593 | panic ("Double exception sys_sigreturn\n"); | ||
594 | |||
595 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
596 | goto give_sigsegv; | ||
597 | |||
598 | signal = current_thread_info()->exec_domain | ||
599 | && current_thread_info()->exec_domain->signal_invmap | ||
600 | && sig < 32 | ||
601 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
602 | : sig; | ||
603 | |||
604 | err |= copy_siginfo_to_user(&frame->info, info); | ||
605 | |||
606 | /* Create the ucontext. */ | ||
607 | err |= __put_user(0, &frame->uc.uc_flags); | ||
608 | err |= __put_user(0, &frame->uc.uc_link); | ||
609 | err |= __put_user((void *)current->sas_ss_sp, | ||
610 | &frame->uc.uc_stack.ss_sp); | ||
611 | err |= __put_user(sas_ss_flags(regs->areg[1]), | ||
612 | &frame->uc.uc_stack.ss_flags); | ||
613 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
614 | err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->cpstate, | ||
615 | regs, set->sig[0]); | ||
616 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
617 | |||
618 | /* Create sys_rt_sigreturn syscall in stack frame */ | ||
619 | err |= gen_return_code(frame->retcode, USE_RT_SIGRETURN); | ||
620 | |||
621 | if (err) | ||
622 | goto give_sigsegv; | ||
623 | |||
624 | /* Create signal handler execution context. | ||
625 | * Return context not modified until this point. | ||
626 | */ | ||
627 | set_thread_state(regs, frame, frame->retcode, | ||
628 | ka->sa.sa_handler, signal, &frame->info, &frame->uc); | ||
629 | |||
630 | /* Set access mode to USER_DS. Nomenclature is outdated, but | ||
631 | * functionality is used in uaccess.h | ||
632 | */ | ||
633 | set_fs(USER_DS); | ||
634 | |||
635 | #if DEBUG_SIG | ||
636 | printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n", | ||
637 | current->comm, current->pid, signal, frame, regs->pc); | ||
638 | #endif | ||
639 | |||
640 | return; | ||
641 | |||
642 | give_sigsegv: | ||
643 | if (sig == SIGSEGV) | ||
644 | ka->sa.sa_handler = SIG_DFL; | ||
645 | force_sig(SIGSEGV, current); | ||
646 | } | ||
647 | |||
648 | |||
649 | |||
650 | /* | ||
651 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
652 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
653 | * mistake. | ||
654 | * | ||
655 | * Note that we go through the signals twice: once to check the signals that | ||
656 | * the kernel can handle, and then we build all the user-level signal handling | ||
657 | * stack-frames in one go after that. | ||
658 | */ | ||
659 | int do_signal(struct pt_regs *regs, sigset_t *oldset) | ||
660 | { | ||
661 | siginfo_t info; | ||
662 | int signr; | ||
663 | struct k_sigaction ka; | ||
664 | |||
665 | if (!oldset) | ||
666 | oldset = ¤t->blocked; | ||
667 | |||
668 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
669 | |||
670 | /* Are we from a system call? */ | ||
671 | if (regs->syscall >= 0) { | ||
672 | /* If so, check system call restarting.. */ | ||
673 | switch (regs->areg[2]) { | ||
674 | case ERESTARTNOHAND: | ||
675 | case ERESTART_RESTARTBLOCK: | ||
676 | regs->areg[2] = -EINTR; | ||
677 | break; | ||
678 | |||
679 | case ERESTARTSYS: | ||
680 | if (!(ka.sa.sa_flags & SA_RESTART)) { | ||
681 | regs->areg[2] = -EINTR; | ||
682 | break; | ||
683 | } | ||
684 | /* fallthrough */ | ||
685 | case ERESTARTNOINTR: | ||
686 | regs->areg[2] = regs->syscall; | ||
687 | regs->pc -= 3; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | if (signr == 0) | ||
692 | return 0; /* no signals delivered */ | ||
693 | |||
694 | /* Whee! Actually deliver the signal. */ | ||
695 | |||
696 | /* Set up the stack frame */ | ||
697 | if (ka.sa.sa_flags & SA_SIGINFO) | ||
698 | setup_rt_frame(signr, &ka, &info, oldset, regs); | ||
699 | else | ||
700 | setup_frame(signr, &ka, oldset, regs); | ||
701 | |||
702 | if (ka.sa.sa_flags & SA_ONESHOT) | ||
703 | ka.sa.sa_handler = SIG_DFL; | ||
704 | |||
705 | if (!(ka.sa.sa_flags & SA_NODEFER)) { | ||
706 | spin_lock_irq(¤t->sighand->siglock); | ||
707 | sigorsets(¤t->blocked, ¤t->blocked, &ka.sa.sa_mask); | ||
708 | sigaddset(¤t->blocked, signr); | ||
709 | recalc_sigpending(); | ||
710 | spin_unlock_irq(¤t->sighand->siglock); | ||
711 | } | ||
712 | return 1; | ||
713 | } | ||