diff options
author | Aurelien Jacquiot <a-jacquiot@ti.com> | 2011-10-04 11:04:34 -0400 |
---|---|---|
committer | Mark Salter <msalter@redhat.com> | 2011-10-06 19:47:46 -0400 |
commit | 03a347558749caaab482f34410ae5d27e893db89 (patch) | |
tree | 4a6e2017f0afb5b82eb31d05ce2a2b539f453827 /arch | |
parent | 687b12baecae2aa3af9df05c12b90d8e9ef21fa7 (diff) |
C6X: signal management
Original port to early 2.6 kernel using TI COFF toolchain.
Brought up to date by Mark Salter <msalter@redhat.com>
Signed-off-by: Aurelien Jacquiot <a-jacquiot@ti.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/c6x/include/asm/sigcontext.h | 80 | ||||
-rw-r--r-- | arch/c6x/include/asm/signal.h | 17 | ||||
-rw-r--r-- | arch/c6x/kernel/signal.c | 377 |
3 files changed, 474 insertions, 0 deletions
diff --git a/arch/c6x/include/asm/sigcontext.h b/arch/c6x/include/asm/sigcontext.h new file mode 100644 index 000000000000..eb702f39cde7 --- /dev/null +++ b/arch/c6x/include/asm/sigcontext.h | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_SIGCONTEXT_H | ||
12 | #define _ASM_C6X_SIGCONTEXT_H | ||
13 | |||
14 | |||
15 | struct sigcontext { | ||
16 | unsigned long sc_mask; /* old sigmask */ | ||
17 | unsigned long sc_sp; /* old user stack pointer */ | ||
18 | |||
19 | unsigned long sc_a4; | ||
20 | unsigned long sc_b4; | ||
21 | unsigned long sc_a6; | ||
22 | unsigned long sc_b6; | ||
23 | unsigned long sc_a8; | ||
24 | unsigned long sc_b8; | ||
25 | |||
26 | unsigned long sc_a0; | ||
27 | unsigned long sc_a1; | ||
28 | unsigned long sc_a2; | ||
29 | unsigned long sc_a3; | ||
30 | unsigned long sc_a5; | ||
31 | unsigned long sc_a7; | ||
32 | unsigned long sc_a9; | ||
33 | |||
34 | unsigned long sc_b0; | ||
35 | unsigned long sc_b1; | ||
36 | unsigned long sc_b2; | ||
37 | unsigned long sc_b3; | ||
38 | unsigned long sc_b5; | ||
39 | unsigned long sc_b7; | ||
40 | unsigned long sc_b9; | ||
41 | |||
42 | unsigned long sc_a16; | ||
43 | unsigned long sc_a17; | ||
44 | unsigned long sc_a18; | ||
45 | unsigned long sc_a19; | ||
46 | unsigned long sc_a20; | ||
47 | unsigned long sc_a21; | ||
48 | unsigned long sc_a22; | ||
49 | unsigned long sc_a23; | ||
50 | unsigned long sc_a24; | ||
51 | unsigned long sc_a25; | ||
52 | unsigned long sc_a26; | ||
53 | unsigned long sc_a27; | ||
54 | unsigned long sc_a28; | ||
55 | unsigned long sc_a29; | ||
56 | unsigned long sc_a30; | ||
57 | unsigned long sc_a31; | ||
58 | |||
59 | unsigned long sc_b16; | ||
60 | unsigned long sc_b17; | ||
61 | unsigned long sc_b18; | ||
62 | unsigned long sc_b19; | ||
63 | unsigned long sc_b20; | ||
64 | unsigned long sc_b21; | ||
65 | unsigned long sc_b22; | ||
66 | unsigned long sc_b23; | ||
67 | unsigned long sc_b24; | ||
68 | unsigned long sc_b25; | ||
69 | unsigned long sc_b26; | ||
70 | unsigned long sc_b27; | ||
71 | unsigned long sc_b28; | ||
72 | unsigned long sc_b29; | ||
73 | unsigned long sc_b30; | ||
74 | unsigned long sc_b31; | ||
75 | |||
76 | unsigned long sc_csr; | ||
77 | unsigned long sc_pc; | ||
78 | }; | ||
79 | |||
80 | #endif /* _ASM_C6X_SIGCONTEXT_H */ | ||
diff --git a/arch/c6x/include/asm/signal.h b/arch/c6x/include/asm/signal.h new file mode 100644 index 000000000000..f1cd870596a3 --- /dev/null +++ b/arch/c6x/include/asm/signal.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef _ASM_C6X_SIGNAL_H | ||
2 | #define _ASM_C6X_SIGNAL_H | ||
3 | |||
4 | #include <asm-generic/signal.h> | ||
5 | |||
6 | #ifndef __ASSEMBLY__ | ||
7 | #include <linux/linkage.h> | ||
8 | |||
9 | struct pt_regs; | ||
10 | |||
11 | extern asmlinkage int do_rt_sigreturn(struct pt_regs *regs); | ||
12 | extern asmlinkage void do_notify_resume(struct pt_regs *regs, | ||
13 | u32 thread_info_flags, | ||
14 | int syscall); | ||
15 | #endif | ||
16 | |||
17 | #endif /* _ASM_C6X_SIGNAL_H */ | ||
diff --git a/arch/c6x/kernel/signal.c b/arch/c6x/kernel/signal.c new file mode 100644 index 000000000000..304f675826e9 --- /dev/null +++ b/arch/c6x/kernel/signal.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Updated for 2.6.34: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/uaccess.h> | ||
16 | #include <linux/syscalls.h> | ||
17 | #include <linux/tracehook.h> | ||
18 | |||
19 | #include <asm/ucontext.h> | ||
20 | #include <asm/cacheflush.h> | ||
21 | |||
22 | |||
23 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
24 | |||
25 | /* | ||
26 | * Do a signal return, undo the signal stack. | ||
27 | */ | ||
28 | |||
29 | #define RETCODE_SIZE (9 << 2) /* 9 instructions = 36 bytes */ | ||
30 | |||
31 | struct rt_sigframe { | ||
32 | struct siginfo __user *pinfo; | ||
33 | void __user *puc; | ||
34 | struct siginfo info; | ||
35 | struct ucontext uc; | ||
36 | unsigned long retcode[RETCODE_SIZE >> 2]; | ||
37 | }; | ||
38 | |||
39 | static int restore_sigcontext(struct pt_regs *regs, | ||
40 | struct sigcontext __user *sc) | ||
41 | { | ||
42 | int err = 0; | ||
43 | |||
44 | /* The access_ok check was done by caller, so use __get_user here */ | ||
45 | #define COPY(x) (err |= __get_user(regs->x, &sc->sc_##x)) | ||
46 | |||
47 | COPY(sp); COPY(a4); COPY(b4); COPY(a6); COPY(b6); COPY(a8); COPY(b8); | ||
48 | COPY(a0); COPY(a1); COPY(a2); COPY(a3); COPY(a5); COPY(a7); COPY(a9); | ||
49 | COPY(b0); COPY(b1); COPY(b2); COPY(b3); COPY(b5); COPY(b7); COPY(b9); | ||
50 | |||
51 | COPY(a16); COPY(a17); COPY(a18); COPY(a19); | ||
52 | COPY(a20); COPY(a21); COPY(a22); COPY(a23); | ||
53 | COPY(a24); COPY(a25); COPY(a26); COPY(a27); | ||
54 | COPY(a28); COPY(a29); COPY(a30); COPY(a31); | ||
55 | COPY(b16); COPY(b17); COPY(b18); COPY(b19); | ||
56 | COPY(b20); COPY(b21); COPY(b22); COPY(b23); | ||
57 | COPY(b24); COPY(b25); COPY(b26); COPY(b27); | ||
58 | COPY(b28); COPY(b29); COPY(b30); COPY(b31); | ||
59 | |||
60 | COPY(csr); COPY(pc); | ||
61 | |||
62 | #undef COPY | ||
63 | |||
64 | return err; | ||
65 | } | ||
66 | |||
67 | asmlinkage int do_rt_sigreturn(struct pt_regs *regs) | ||
68 | { | ||
69 | struct rt_sigframe __user *frame; | ||
70 | sigset_t set; | ||
71 | |||
72 | /* | ||
73 | * Since we stacked the signal on a dword boundary, | ||
74 | * 'sp' should be dword aligned here. If it's | ||
75 | * not, then the user is trying to mess with us. | ||
76 | */ | ||
77 | if (regs->sp & 7) | ||
78 | goto badframe; | ||
79 | |||
80 | frame = (struct rt_sigframe __user *) ((unsigned long) regs->sp + 8); | ||
81 | |||
82 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
83 | goto badframe; | ||
84 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
85 | goto badframe; | ||
86 | |||
87 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
88 | spin_lock_irq(¤t->sighand->siglock); | ||
89 | current->blocked = set; | ||
90 | recalc_sigpending(); | ||
91 | spin_unlock_irq(¤t->sighand->siglock); | ||
92 | |||
93 | if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) | ||
94 | goto badframe; | ||
95 | |||
96 | return regs->a4; | ||
97 | |||
98 | badframe: | ||
99 | force_sig(SIGSEGV, current); | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | ||
104 | unsigned long mask) | ||
105 | { | ||
106 | int err = 0; | ||
107 | |||
108 | err |= __put_user(mask, &sc->sc_mask); | ||
109 | |||
110 | /* The access_ok check was done by caller, so use __put_user here */ | ||
111 | #define COPY(x) (err |= __put_user(regs->x, &sc->sc_##x)) | ||
112 | |||
113 | COPY(sp); COPY(a4); COPY(b4); COPY(a6); COPY(b6); COPY(a8); COPY(b8); | ||
114 | COPY(a0); COPY(a1); COPY(a2); COPY(a3); COPY(a5); COPY(a7); COPY(a9); | ||
115 | COPY(b0); COPY(b1); COPY(b2); COPY(b3); COPY(b5); COPY(b7); COPY(b9); | ||
116 | |||
117 | COPY(a16); COPY(a17); COPY(a18); COPY(a19); | ||
118 | COPY(a20); COPY(a21); COPY(a22); COPY(a23); | ||
119 | COPY(a24); COPY(a25); COPY(a26); COPY(a27); | ||
120 | COPY(a28); COPY(a29); COPY(a30); COPY(a31); | ||
121 | COPY(b16); COPY(b17); COPY(b18); COPY(b19); | ||
122 | COPY(b20); COPY(b21); COPY(b22); COPY(b23); | ||
123 | COPY(b24); COPY(b25); COPY(b26); COPY(b27); | ||
124 | COPY(b28); COPY(b29); COPY(b30); COPY(b31); | ||
125 | |||
126 | COPY(csr); COPY(pc); | ||
127 | |||
128 | #undef COPY | ||
129 | |||
130 | return err; | ||
131 | } | ||
132 | |||
133 | static inline void __user *get_sigframe(struct k_sigaction *ka, | ||
134 | struct pt_regs *regs, | ||
135 | unsigned long framesize) | ||
136 | { | ||
137 | unsigned long sp = regs->sp; | ||
138 | |||
139 | /* | ||
140 | * This is the X/Open sanctioned signal stack switching. | ||
141 | */ | ||
142 | if ((ka->sa.sa_flags & SA_ONSTACK) && sas_ss_flags(sp) == 0) | ||
143 | sp = current->sas_ss_sp + current->sas_ss_size; | ||
144 | |||
145 | /* | ||
146 | * No matter what happens, 'sp' must be dword | ||
147 | * aligned. Otherwise, nasty things will happen | ||
148 | */ | ||
149 | return (void __user *)((sp - framesize) & ~7); | ||
150 | } | ||
151 | |||
152 | static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, | ||
153 | sigset_t *set, struct pt_regs *regs) | ||
154 | { | ||
155 | struct rt_sigframe __user *frame; | ||
156 | unsigned long __user *retcode; | ||
157 | int err = 0; | ||
158 | |||
159 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
160 | |||
161 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
162 | goto segv_and_exit; | ||
163 | |||
164 | err |= __put_user(&frame->info, &frame->pinfo); | ||
165 | err |= __put_user(&frame->uc, &frame->puc); | ||
166 | err |= copy_siginfo_to_user(&frame->info, info); | ||
167 | |||
168 | /* Clear all the bits of the ucontext we don't use. */ | ||
169 | err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); | ||
170 | |||
171 | err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); | ||
172 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
173 | |||
174 | /* Set up to return from userspace */ | ||
175 | retcode = (unsigned long __user *) &frame->retcode; | ||
176 | |||
177 | /* The access_ok check was done above, so use __put_user here */ | ||
178 | #define COPY(x) (err |= __put_user(x, retcode++)) | ||
179 | |||
180 | COPY(0x0000002AUL | (__NR_rt_sigreturn << 7)); | ||
181 | /* MVK __NR_rt_sigreturn,B0 */ | ||
182 | COPY(0x10000000UL); /* SWE */ | ||
183 | COPY(0x00006000UL); /* NOP 4 */ | ||
184 | COPY(0x00006000UL); /* NOP 4 */ | ||
185 | COPY(0x00006000UL); /* NOP 4 */ | ||
186 | COPY(0x00006000UL); /* NOP 4 */ | ||
187 | COPY(0x00006000UL); /* NOP 4 */ | ||
188 | COPY(0x00006000UL); /* NOP 4 */ | ||
189 | COPY(0x00006000UL); /* NOP 4 */ | ||
190 | |||
191 | #undef COPY | ||
192 | |||
193 | if (err) | ||
194 | goto segv_and_exit; | ||
195 | |||
196 | flush_icache_range((unsigned long) &frame->retcode, | ||
197 | (unsigned long) &frame->retcode + RETCODE_SIZE); | ||
198 | |||
199 | retcode = (unsigned long __user *) &frame->retcode; | ||
200 | |||
201 | /* Change user context to branch to signal handler */ | ||
202 | regs->sp = (unsigned long) frame - 8; | ||
203 | regs->b3 = (unsigned long) retcode; | ||
204 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
205 | |||
206 | /* Give the signal number to the handler */ | ||
207 | regs->a4 = signr; | ||
208 | |||
209 | /* | ||
210 | * For realtime signals we must also set the second and third | ||
211 | * arguments for the signal handler. | ||
212 | * -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06 | ||
213 | */ | ||
214 | regs->b4 = (unsigned long)&frame->info; | ||
215 | regs->a6 = (unsigned long)&frame->uc; | ||
216 | |||
217 | return 0; | ||
218 | |||
219 | segv_and_exit: | ||
220 | force_sigsegv(signr, current); | ||
221 | return -EFAULT; | ||
222 | } | ||
223 | |||
224 | static inline void | ||
225 | handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) | ||
226 | { | ||
227 | switch (regs->a4) { | ||
228 | case -ERESTARTNOHAND: | ||
229 | if (!has_handler) | ||
230 | goto do_restart; | ||
231 | regs->a4 = -EINTR; | ||
232 | break; | ||
233 | |||
234 | case -ERESTARTSYS: | ||
235 | if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { | ||
236 | regs->a4 = -EINTR; | ||
237 | break; | ||
238 | } | ||
239 | /* fallthrough */ | ||
240 | case -ERESTARTNOINTR: | ||
241 | do_restart: | ||
242 | regs->a4 = regs->orig_a4; | ||
243 | regs->pc -= 4; | ||
244 | break; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | /* | ||
249 | * handle the actual delivery of a signal to userspace | ||
250 | */ | ||
251 | static int handle_signal(int sig, | ||
252 | siginfo_t *info, struct k_sigaction *ka, | ||
253 | sigset_t *oldset, struct pt_regs *regs, | ||
254 | int syscall) | ||
255 | { | ||
256 | int ret; | ||
257 | |||
258 | /* Are we from a system call? */ | ||
259 | if (syscall) { | ||
260 | /* If so, check system call restarting.. */ | ||
261 | switch (regs->a4) { | ||
262 | case -ERESTART_RESTARTBLOCK: | ||
263 | case -ERESTARTNOHAND: | ||
264 | regs->a4 = -EINTR; | ||
265 | break; | ||
266 | |||
267 | case -ERESTARTSYS: | ||
268 | if (!(ka->sa.sa_flags & SA_RESTART)) { | ||
269 | regs->a4 = -EINTR; | ||
270 | break; | ||
271 | } | ||
272 | |||
273 | /* fallthrough */ | ||
274 | case -ERESTARTNOINTR: | ||
275 | regs->a4 = regs->orig_a4; | ||
276 | regs->pc -= 4; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* Set up the stack frame */ | ||
281 | ret = setup_rt_frame(sig, ka, info, oldset, regs); | ||
282 | if (ret == 0) { | ||
283 | spin_lock_irq(¤t->sighand->siglock); | ||
284 | sigorsets(¤t->blocked, ¤t->blocked, | ||
285 | &ka->sa.sa_mask); | ||
286 | if (!(ka->sa.sa_flags & SA_NODEFER)) | ||
287 | sigaddset(¤t->blocked, sig); | ||
288 | recalc_sigpending(); | ||
289 | spin_unlock_irq(¤t->sighand->siglock); | ||
290 | } | ||
291 | |||
292 | return ret; | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * handle a potential signal | ||
297 | */ | ||
298 | static void do_signal(struct pt_regs *regs, int syscall) | ||
299 | { | ||
300 | struct k_sigaction ka; | ||
301 | siginfo_t info; | ||
302 | sigset_t *oldset; | ||
303 | int signr; | ||
304 | |||
305 | /* we want the common case to go fast, which is why we may in certain | ||
306 | * cases get here from kernel mode */ | ||
307 | if (!user_mode(regs)) | ||
308 | return; | ||
309 | |||
310 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
311 | oldset = ¤t->saved_sigmask; | ||
312 | else | ||
313 | oldset = ¤t->blocked; | ||
314 | |||
315 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
316 | if (signr > 0) { | ||
317 | if (handle_signal(signr, &info, &ka, oldset, | ||
318 | regs, syscall) == 0) { | ||
319 | /* a signal was successfully delivered; the saved | ||
320 | * sigmask will have been stored in the signal frame, | ||
321 | * and will be restored by sigreturn, so we can simply | ||
322 | * clear the TIF_RESTORE_SIGMASK flag */ | ||
323 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
324 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
325 | |||
326 | tracehook_signal_handler(signr, &info, &ka, regs, 0); | ||
327 | } | ||
328 | |||
329 | return; | ||
330 | } | ||
331 | |||
332 | /* did we come from a system call? */ | ||
333 | if (syscall) { | ||
334 | /* restart the system call - no handlers present */ | ||
335 | switch (regs->a4) { | ||
336 | case -ERESTARTNOHAND: | ||
337 | case -ERESTARTSYS: | ||
338 | case -ERESTARTNOINTR: | ||
339 | regs->a4 = regs->orig_a4; | ||
340 | regs->pc -= 4; | ||
341 | break; | ||
342 | |||
343 | case -ERESTART_RESTARTBLOCK: | ||
344 | regs->a4 = regs->orig_a4; | ||
345 | regs->b0 = __NR_restart_syscall; | ||
346 | regs->pc -= 4; | ||
347 | break; | ||
348 | } | ||
349 | } | ||
350 | |||
351 | /* if there's no signal to deliver, we just put the saved sigmask | ||
352 | * back */ | ||
353 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
354 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
355 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * notification of userspace execution resumption | ||
361 | * - triggered by current->work.notify_resume | ||
362 | */ | ||
363 | asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags, | ||
364 | int syscall) | ||
365 | { | ||
366 | /* deal with pending signal delivery */ | ||
367 | if (thread_info_flags & ((1 << TIF_SIGPENDING) | | ||
368 | (1 << TIF_RESTORE_SIGMASK))) | ||
369 | do_signal(regs, syscall); | ||
370 | |||
371 | if (thread_info_flags & (1 << TIF_NOTIFY_RESUME)) { | ||
372 | clear_thread_flag(TIF_NOTIFY_RESUME); | ||
373 | tracehook_notify_resume(regs); | ||
374 | if (current->replacement_session_keyring) | ||
375 | key_replace_session_keyring(); | ||
376 | } | ||
377 | } | ||