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