diff options
author | Ley Foon Tan <lftan@altera.com> | 2014-11-06 02:19:59 -0500 |
---|---|---|
committer | Ley Foon Tan <lftan@altera.com> | 2014-12-07 23:55:59 -0500 |
commit | b53e906d255d7bc3539c2729afb8a18c309cd41e (patch) | |
tree | 2cd954b17aad5bdf540e23023e82d6ca2871c361 /arch/nios2 | |
parent | 1000197d801329804d30094aef5dba0265204d17 (diff) |
nios2: Signal handling support
This patch adds support for signal handling.
Signed-off-by: Ley Foon Tan <lftan@altera.com>
Diffstat (limited to 'arch/nios2')
-rw-r--r-- | arch/nios2/include/asm/signal.h | 22 | ||||
-rw-r--r-- | arch/nios2/include/asm/ucontext.h | 32 | ||||
-rw-r--r-- | arch/nios2/include/uapi/asm/sigcontext.h | 28 | ||||
-rw-r--r-- | arch/nios2/include/uapi/asm/signal.h | 23 | ||||
-rw-r--r-- | arch/nios2/kernel/signal.c | 323 |
5 files changed, 428 insertions, 0 deletions
diff --git a/arch/nios2/include/asm/signal.h b/arch/nios2/include/asm/signal.h new file mode 100644 index 000000000000..bbcf11eecb01 --- /dev/null +++ b/arch/nios2/include/asm/signal.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * Copyright Altera Corporation (C) 2013. All rights reserved | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | * | ||
16 | */ | ||
17 | #ifndef _NIOS2_SIGNAL_H | ||
18 | #define _NIOS2_SIGNAL_H | ||
19 | |||
20 | #include <uapi/asm/signal.h> | ||
21 | |||
22 | #endif /* _NIOS2_SIGNAL_H */ | ||
diff --git a/arch/nios2/include/asm/ucontext.h b/arch/nios2/include/asm/ucontext.h new file mode 100644 index 000000000000..2c87614b0f6e --- /dev/null +++ b/arch/nios2/include/asm/ucontext.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> | ||
3 | * Copyright (C) 2004 Microtronix Datacom Ltd | ||
4 | * | ||
5 | * This file is subject to the terms and conditions of the GNU General Public | ||
6 | * License. See the file "COPYING" in the main directory of this archive | ||
7 | * for more details. | ||
8 | */ | ||
9 | |||
10 | #ifndef _ASM_NIOS2_UCONTEXT_H | ||
11 | #define _ASM_NIOS2_UCONTEXT_H | ||
12 | |||
13 | typedef int greg_t; | ||
14 | #define NGREG 32 | ||
15 | typedef greg_t gregset_t[NGREG]; | ||
16 | |||
17 | struct mcontext { | ||
18 | int version; | ||
19 | gregset_t gregs; | ||
20 | }; | ||
21 | |||
22 | #define MCONTEXT_VERSION 2 | ||
23 | |||
24 | struct ucontext { | ||
25 | unsigned long uc_flags; | ||
26 | struct ucontext *uc_link; | ||
27 | stack_t uc_stack; | ||
28 | struct mcontext uc_mcontext; | ||
29 | sigset_t uc_sigmask; /* mask last for extensibility */ | ||
30 | }; | ||
31 | |||
32 | #endif | ||
diff --git a/arch/nios2/include/uapi/asm/sigcontext.h b/arch/nios2/include/uapi/asm/sigcontext.h new file mode 100644 index 000000000000..7b8bb41867d4 --- /dev/null +++ b/arch/nios2/include/uapi/asm/sigcontext.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, Microtronix Datacom Ltd. | ||
3 | * | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
14 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
15 | * details. | ||
16 | */ | ||
17 | |||
18 | #ifndef _ASM_NIOS2_SIGCONTEXT_H | ||
19 | #define _ASM_NIOS2_SIGCONTEXT_H | ||
20 | |||
21 | #include <asm/ptrace.h> | ||
22 | |||
23 | struct sigcontext { | ||
24 | struct pt_regs regs; | ||
25 | unsigned long sc_mask; /* old sigmask */ | ||
26 | }; | ||
27 | |||
28 | #endif | ||
diff --git a/arch/nios2/include/uapi/asm/signal.h b/arch/nios2/include/uapi/asm/signal.h new file mode 100644 index 000000000000..f29ee6314481 --- /dev/null +++ b/arch/nios2/include/uapi/asm/signal.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright Altera Corporation (C) 2013. All rights reserved | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | * | ||
16 | */ | ||
17 | #ifndef _ASM_NIOS2_SIGNAL_H | ||
18 | #define _ASM_NIOS2_SIGNAL_H | ||
19 | |||
20 | #define SA_RESTORER 0x04000000 | ||
21 | #include <asm-generic/signal.h> | ||
22 | |||
23 | #endif /* _ASM_NIOS2_SIGNAL_H */ | ||
diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c new file mode 100644 index 000000000000..f9d27883a714 --- /dev/null +++ b/arch/nios2/kernel/signal.c | |||
@@ -0,0 +1,323 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013-2014 Altera Corporation | ||
3 | * Copyright (C) 2011-2012 Tobias Klauser <tklauser@distanz.ch> | ||
4 | * Copyright (C) 2004 Microtronix Datacom Ltd | ||
5 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file COPYING in the main directory of this archive | ||
9 | * for more details. | ||
10 | */ | ||
11 | |||
12 | #include <linux/signal.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/ptrace.h> | ||
15 | #include <linux/uaccess.h> | ||
16 | #include <linux/unistd.h> | ||
17 | #include <linux/personality.h> | ||
18 | #include <linux/tracehook.h> | ||
19 | |||
20 | #include <asm/ucontext.h> | ||
21 | #include <asm/cacheflush.h> | ||
22 | |||
23 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
24 | |||
25 | /* | ||
26 | * Do a signal return; undo the signal stack. | ||
27 | * | ||
28 | * Keep the return code on the stack quadword aligned! | ||
29 | * That makes the cache flush below easier. | ||
30 | */ | ||
31 | |||
32 | struct rt_sigframe { | ||
33 | struct siginfo info; | ||
34 | struct ucontext uc; | ||
35 | }; | ||
36 | |||
37 | static inline int rt_restore_ucontext(struct pt_regs *regs, | ||
38 | struct switch_stack *sw, | ||
39 | struct ucontext *uc, int *pr2) | ||
40 | { | ||
41 | int temp; | ||
42 | greg_t *gregs = uc->uc_mcontext.gregs; | ||
43 | int err; | ||
44 | |||
45 | /* Always make any pending restarted system calls return -EINTR */ | ||
46 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
47 | |||
48 | err = __get_user(temp, &uc->uc_mcontext.version); | ||
49 | if (temp != MCONTEXT_VERSION) | ||
50 | goto badframe; | ||
51 | /* restore passed registers */ | ||
52 | err |= __get_user(regs->r1, &gregs[0]); | ||
53 | err |= __get_user(regs->r2, &gregs[1]); | ||
54 | err |= __get_user(regs->r3, &gregs[2]); | ||
55 | err |= __get_user(regs->r4, &gregs[3]); | ||
56 | err |= __get_user(regs->r5, &gregs[4]); | ||
57 | err |= __get_user(regs->r6, &gregs[5]); | ||
58 | err |= __get_user(regs->r7, &gregs[6]); | ||
59 | err |= __get_user(regs->r8, &gregs[7]); | ||
60 | err |= __get_user(regs->r9, &gregs[8]); | ||
61 | err |= __get_user(regs->r10, &gregs[9]); | ||
62 | err |= __get_user(regs->r11, &gregs[10]); | ||
63 | err |= __get_user(regs->r12, &gregs[11]); | ||
64 | err |= __get_user(regs->r13, &gregs[12]); | ||
65 | err |= __get_user(regs->r14, &gregs[13]); | ||
66 | err |= __get_user(regs->r15, &gregs[14]); | ||
67 | err |= __get_user(sw->r16, &gregs[15]); | ||
68 | err |= __get_user(sw->r17, &gregs[16]); | ||
69 | err |= __get_user(sw->r18, &gregs[17]); | ||
70 | err |= __get_user(sw->r19, &gregs[18]); | ||
71 | err |= __get_user(sw->r20, &gregs[19]); | ||
72 | err |= __get_user(sw->r21, &gregs[20]); | ||
73 | err |= __get_user(sw->r22, &gregs[21]); | ||
74 | err |= __get_user(sw->r23, &gregs[22]); | ||
75 | /* gregs[23] is handled below */ | ||
76 | err |= __get_user(sw->fp, &gregs[24]); /* Verify, should this be | ||
77 | settable */ | ||
78 | err |= __get_user(sw->gp, &gregs[25]); /* Verify, should this be | ||
79 | settable */ | ||
80 | |||
81 | err |= __get_user(temp, &gregs[26]); /* Not really necessary no user | ||
82 | settable bits */ | ||
83 | err |= __get_user(regs->ea, &gregs[27]); | ||
84 | |||
85 | err |= __get_user(regs->ra, &gregs[23]); | ||
86 | err |= __get_user(regs->sp, &gregs[28]); | ||
87 | |||
88 | regs->orig_r2 = -1; /* disable syscall checks */ | ||
89 | |||
90 | err |= restore_altstack(&uc->uc_stack); | ||
91 | if (err) | ||
92 | goto badframe; | ||
93 | |||
94 | *pr2 = regs->r2; | ||
95 | return err; | ||
96 | |||
97 | badframe: | ||
98 | return 1; | ||
99 | } | ||
100 | |||
101 | asmlinkage int do_rt_sigreturn(struct switch_stack *sw) | ||
102 | { | ||
103 | struct pt_regs *regs = (struct pt_regs *)(sw + 1); | ||
104 | /* Verify, can we follow the stack back */ | ||
105 | struct rt_sigframe *frame = (struct rt_sigframe *) regs->sp; | ||
106 | sigset_t set; | ||
107 | int rval; | ||
108 | |||
109 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
110 | goto badframe; | ||
111 | |||
112 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
113 | goto badframe; | ||
114 | |||
115 | set_current_blocked(&set); | ||
116 | |||
117 | if (rt_restore_ucontext(regs, sw, &frame->uc, &rval)) | ||
118 | goto badframe; | ||
119 | |||
120 | return rval; | ||
121 | |||
122 | badframe: | ||
123 | force_sig(SIGSEGV, current); | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs) | ||
128 | { | ||
129 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | ||
130 | greg_t *gregs = uc->uc_mcontext.gregs; | ||
131 | int err = 0; | ||
132 | |||
133 | err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); | ||
134 | err |= __put_user(regs->r1, &gregs[0]); | ||
135 | err |= __put_user(regs->r2, &gregs[1]); | ||
136 | err |= __put_user(regs->r3, &gregs[2]); | ||
137 | err |= __put_user(regs->r4, &gregs[3]); | ||
138 | err |= __put_user(regs->r5, &gregs[4]); | ||
139 | err |= __put_user(regs->r6, &gregs[5]); | ||
140 | err |= __put_user(regs->r7, &gregs[6]); | ||
141 | err |= __put_user(regs->r8, &gregs[7]); | ||
142 | err |= __put_user(regs->r9, &gregs[8]); | ||
143 | err |= __put_user(regs->r10, &gregs[9]); | ||
144 | err |= __put_user(regs->r11, &gregs[10]); | ||
145 | err |= __put_user(regs->r12, &gregs[11]); | ||
146 | err |= __put_user(regs->r13, &gregs[12]); | ||
147 | err |= __put_user(regs->r14, &gregs[13]); | ||
148 | err |= __put_user(regs->r15, &gregs[14]); | ||
149 | err |= __put_user(sw->r16, &gregs[15]); | ||
150 | err |= __put_user(sw->r17, &gregs[16]); | ||
151 | err |= __put_user(sw->r18, &gregs[17]); | ||
152 | err |= __put_user(sw->r19, &gregs[18]); | ||
153 | err |= __put_user(sw->r20, &gregs[19]); | ||
154 | err |= __put_user(sw->r21, &gregs[20]); | ||
155 | err |= __put_user(sw->r22, &gregs[21]); | ||
156 | err |= __put_user(sw->r23, &gregs[22]); | ||
157 | err |= __put_user(regs->ra, &gregs[23]); | ||
158 | err |= __put_user(sw->fp, &gregs[24]); | ||
159 | err |= __put_user(sw->gp, &gregs[25]); | ||
160 | err |= __put_user(regs->ea, &gregs[27]); | ||
161 | err |= __put_user(regs->sp, &gregs[28]); | ||
162 | return err; | ||
163 | } | ||
164 | |||
165 | static inline void *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, | ||
166 | size_t frame_size) | ||
167 | { | ||
168 | unsigned long usp; | ||
169 | |||
170 | /* Default to using normal stack. */ | ||
171 | usp = regs->sp; | ||
172 | |||
173 | /* This is the X/Open sanctioned signal stack switching. */ | ||
174 | usp = sigsp(usp, ksig); | ||
175 | |||
176 | /* Verify, is it 32 or 64 bit aligned */ | ||
177 | return (void *)((usp - frame_size) & -8UL); | ||
178 | } | ||
179 | |||
180 | static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, | ||
181 | struct pt_regs *regs) | ||
182 | { | ||
183 | struct rt_sigframe *frame; | ||
184 | int err = 0; | ||
185 | |||
186 | frame = get_sigframe(ksig, regs, sizeof(*frame)); | ||
187 | |||
188 | if (ksig->ka.sa.sa_flags & SA_SIGINFO) | ||
189 | err |= copy_siginfo_to_user(&frame->info, &ksig->info); | ||
190 | |||
191 | /* Create the ucontext. */ | ||
192 | err |= __put_user(0, &frame->uc.uc_flags); | ||
193 | err |= __put_user(0, &frame->uc.uc_link); | ||
194 | err |= __save_altstack(&frame->uc.uc_stack, regs->sp); | ||
195 | err |= rt_setup_ucontext(&frame->uc, regs); | ||
196 | err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
197 | |||
198 | if (err) | ||
199 | goto give_sigsegv; | ||
200 | |||
201 | /* Set up to return from userspace; jump to fixed address sigreturn | ||
202 | trampoline on kuser page. */ | ||
203 | regs->ra = (unsigned long) (0x1040); | ||
204 | |||
205 | /* Set up registers for signal handler */ | ||
206 | regs->sp = (unsigned long) frame; | ||
207 | regs->r4 = (unsigned long) ksig->sig; | ||
208 | regs->r5 = (unsigned long) &frame->info; | ||
209 | regs->r6 = (unsigned long) &frame->uc; | ||
210 | regs->ea = (unsigned long) ksig->ka.sa.sa_handler; | ||
211 | return 0; | ||
212 | |||
213 | give_sigsegv: | ||
214 | force_sigsegv(ksig->sig, current); | ||
215 | return -EFAULT; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * OK, we're invoking a handler | ||
220 | */ | ||
221 | static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) | ||
222 | { | ||
223 | int ret; | ||
224 | sigset_t *oldset = sigmask_to_save(); | ||
225 | |||
226 | /* set up the stack frame */ | ||
227 | ret = setup_rt_frame(ksig, oldset, regs); | ||
228 | |||
229 | signal_setup_done(ret, ksig, 0); | ||
230 | } | ||
231 | |||
232 | static int do_signal(struct pt_regs *regs) | ||
233 | { | ||
234 | unsigned int retval = 0, continue_addr = 0, restart_addr = 0; | ||
235 | int restart = 0; | ||
236 | struct ksignal ksig; | ||
237 | |||
238 | current->thread.kregs = regs; | ||
239 | |||
240 | /* | ||
241 | * If we were from a system call, check for system call restarting... | ||
242 | */ | ||
243 | if (regs->orig_r2 >= 0) { | ||
244 | continue_addr = regs->ea; | ||
245 | restart_addr = continue_addr - 4; | ||
246 | retval = regs->r2; | ||
247 | |||
248 | /* | ||
249 | * Prepare for system call restart. We do this here so that a | ||
250 | * debugger will see the already changed PC. | ||
251 | */ | ||
252 | switch (retval) { | ||
253 | case ERESTART_RESTARTBLOCK: | ||
254 | restart = -2; | ||
255 | case ERESTARTNOHAND: | ||
256 | case ERESTARTSYS: | ||
257 | case ERESTARTNOINTR: | ||
258 | restart++; | ||
259 | regs->r2 = regs->orig_r2; | ||
260 | regs->r7 = regs->orig_r7; | ||
261 | regs->ea = restart_addr; | ||
262 | break; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | if (get_signal(&ksig)) { | ||
267 | /* handler */ | ||
268 | if (unlikely(restart && regs->ea == restart_addr)) { | ||
269 | if (retval == ERESTARTNOHAND || | ||
270 | retval == ERESTART_RESTARTBLOCK || | ||
271 | (retval == ERESTARTSYS | ||
272 | && !(ksig.ka.sa.sa_flags & SA_RESTART))) { | ||
273 | regs->r2 = EINTR; | ||
274 | regs->r7 = 1; | ||
275 | regs->ea = continue_addr; | ||
276 | } | ||
277 | } | ||
278 | handle_signal(&ksig, regs); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * No handler present | ||
284 | */ | ||
285 | if (unlikely(restart) && regs->ea == restart_addr) { | ||
286 | regs->ea = continue_addr; | ||
287 | regs->r2 = __NR_restart_syscall; | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * If there's no signal to deliver, we just put the saved sigmask back. | ||
292 | */ | ||
293 | restore_saved_sigmask(); | ||
294 | |||
295 | return restart; | ||
296 | } | ||
297 | |||
298 | asmlinkage int do_notify_resume(struct pt_regs *regs) | ||
299 | { | ||
300 | /* | ||
301 | * We want the common case to go fast, which is why we may in certain | ||
302 | * cases get here from kernel mode. Just return without doing anything | ||
303 | * if so. | ||
304 | */ | ||
305 | if (!user_mode(regs)) | ||
306 | return 0; | ||
307 | |||
308 | if (test_thread_flag(TIF_SIGPENDING)) { | ||
309 | int restart = do_signal(regs); | ||
310 | |||
311 | if (unlikely(restart)) { | ||
312 | /* | ||
313 | * Restart without handlers. | ||
314 | * Deal with it without leaving | ||
315 | * the kernel space. | ||
316 | */ | ||
317 | return restart; | ||
318 | } | ||
319 | } else if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) | ||
320 | tracehook_notify_resume(regs); | ||
321 | |||
322 | return 0; | ||
323 | } | ||