diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2012-03-05 06:49:31 -0500 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2012-09-17 08:42:10 -0400 |
commit | 2c020ed8d148f7562c01abaf875c9bbfb9a40a83 (patch) | |
tree | 99cfa4fb57c840aad51b4d7b7793ca37b947c2f0 /arch/arm64/kernel | |
parent | 9031fefde6f2ac1d1e5e0f8f4b22db4a091229bb (diff) |
arm64: Signal handling support
This patch adds support for signal handling. The sigreturn is done via
VDSO, introduced by a previous patch. The SA_RESTORER is still defined
as it is required for 32-bit (compat) support but it is not to be used
for 64-bit applications.
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Nicolas Pitre <nico@linaro.org>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r-- | arch/arm64/kernel/signal.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c new file mode 100644 index 000000000000..8807ba2cf262 --- /dev/null +++ b/arch/arm64/kernel/signal.c | |||
@@ -0,0 +1,437 @@ | |||
1 | /* | ||
2 | * Based on arch/arm/kernel/signal.c | ||
3 | * | ||
4 | * Copyright (C) 1995-2009 Russell King | ||
5 | * Copyright (C) 2012 ARM Ltd. | ||
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 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <linux/errno.h> | ||
21 | #include <linux/signal.h> | ||
22 | #include <linux/personality.h> | ||
23 | #include <linux/freezer.h> | ||
24 | #include <linux/uaccess.h> | ||
25 | #include <linux/tracehook.h> | ||
26 | #include <linux/ratelimit.h> | ||
27 | |||
28 | #include <asm/compat.h> | ||
29 | #include <asm/debug-monitors.h> | ||
30 | #include <asm/elf.h> | ||
31 | #include <asm/cacheflush.h> | ||
32 | #include <asm/ucontext.h> | ||
33 | #include <asm/unistd.h> | ||
34 | #include <asm/fpsimd.h> | ||
35 | #include <asm/signal32.h> | ||
36 | #include <asm/vdso.h> | ||
37 | |||
38 | /* | ||
39 | * Do a signal return; undo the signal stack. These are aligned to 128-bit. | ||
40 | */ | ||
41 | struct rt_sigframe { | ||
42 | struct siginfo info; | ||
43 | struct ucontext uc; | ||
44 | }; | ||
45 | |||
46 | static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) | ||
47 | { | ||
48 | struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state; | ||
49 | int err; | ||
50 | |||
51 | /* dump the hardware registers to the fpsimd_state structure */ | ||
52 | fpsimd_save_state(fpsimd); | ||
53 | |||
54 | /* copy the FP and status/control registers */ | ||
55 | err = __copy_to_user(ctx->vregs, fpsimd->vregs, sizeof(fpsimd->vregs)); | ||
56 | __put_user_error(fpsimd->fpsr, &ctx->fpsr, err); | ||
57 | __put_user_error(fpsimd->fpcr, &ctx->fpcr, err); | ||
58 | |||
59 | /* copy the magic/size information */ | ||
60 | __put_user_error(FPSIMD_MAGIC, &ctx->head.magic, err); | ||
61 | __put_user_error(sizeof(struct fpsimd_context), &ctx->head.size, err); | ||
62 | |||
63 | return err ? -EFAULT : 0; | ||
64 | } | ||
65 | |||
66 | static int restore_fpsimd_context(struct fpsimd_context __user *ctx) | ||
67 | { | ||
68 | struct fpsimd_state fpsimd; | ||
69 | __u32 magic, size; | ||
70 | int err = 0; | ||
71 | |||
72 | /* check the magic/size information */ | ||
73 | __get_user_error(magic, &ctx->head.magic, err); | ||
74 | __get_user_error(size, &ctx->head.size, err); | ||
75 | if (err) | ||
76 | return -EFAULT; | ||
77 | if (magic != FPSIMD_MAGIC || size != sizeof(struct fpsimd_context)) | ||
78 | return -EINVAL; | ||
79 | |||
80 | /* copy the FP and status/control registers */ | ||
81 | err = __copy_from_user(fpsimd.vregs, ctx->vregs, | ||
82 | sizeof(fpsimd.vregs)); | ||
83 | __get_user_error(fpsimd.fpsr, &ctx->fpsr, err); | ||
84 | __get_user_error(fpsimd.fpcr, &ctx->fpcr, err); | ||
85 | |||
86 | /* load the hardware registers from the fpsimd_state structure */ | ||
87 | if (!err) { | ||
88 | preempt_disable(); | ||
89 | fpsimd_load_state(&fpsimd); | ||
90 | preempt_enable(); | ||
91 | } | ||
92 | |||
93 | return err ? -EFAULT : 0; | ||
94 | } | ||
95 | |||
96 | static int restore_sigframe(struct pt_regs *regs, | ||
97 | struct rt_sigframe __user *sf) | ||
98 | { | ||
99 | sigset_t set; | ||
100 | int i, err; | ||
101 | struct aux_context __user *aux = | ||
102 | (struct aux_context __user *)sf->uc.uc_mcontext.__reserved; | ||
103 | |||
104 | err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set)); | ||
105 | if (err == 0) | ||
106 | set_current_blocked(&set); | ||
107 | |||
108 | for (i = 0; i < 31; i++) | ||
109 | __get_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], | ||
110 | err); | ||
111 | __get_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); | ||
112 | __get_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); | ||
113 | __get_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); | ||
114 | |||
115 | /* | ||
116 | * Avoid sys_rt_sigreturn() restarting. | ||
117 | */ | ||
118 | regs->syscallno = ~0UL; | ||
119 | |||
120 | err |= !valid_user_regs(®s->user_regs); | ||
121 | |||
122 | if (err == 0) | ||
123 | err |= restore_fpsimd_context(&aux->fpsimd); | ||
124 | |||
125 | return err; | ||
126 | } | ||
127 | |||
128 | asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) | ||
129 | { | ||
130 | struct rt_sigframe __user *frame; | ||
131 | |||
132 | /* Always make any pending restarted system calls return -EINTR */ | ||
133 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
134 | |||
135 | /* | ||
136 | * Since we stacked the signal on a 128-bit boundary, then 'sp' should | ||
137 | * be word aligned here. | ||
138 | */ | ||
139 | if (regs->sp & 15) | ||
140 | goto badframe; | ||
141 | |||
142 | frame = (struct rt_sigframe __user *)regs->sp; | ||
143 | |||
144 | if (!access_ok(VERIFY_READ, frame, sizeof (*frame))) | ||
145 | goto badframe; | ||
146 | |||
147 | if (restore_sigframe(regs, frame)) | ||
148 | goto badframe; | ||
149 | |||
150 | if (do_sigaltstack(&frame->uc.uc_stack, | ||
151 | NULL, regs->sp) == -EFAULT) | ||
152 | goto badframe; | ||
153 | |||
154 | return regs->regs[0]; | ||
155 | |||
156 | badframe: | ||
157 | if (show_unhandled_signals) | ||
158 | pr_info_ratelimited("%s[%d]: bad frame in %s: pc=%08llx sp=%08llx\n", | ||
159 | current->comm, task_pid_nr(current), __func__, | ||
160 | regs->pc, regs->sp); | ||
161 | force_sig(SIGSEGV, current); | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, | ||
166 | unsigned long sp) | ||
167 | { | ||
168 | return do_sigaltstack(uss, uoss, sp); | ||
169 | } | ||
170 | |||
171 | static int setup_sigframe(struct rt_sigframe __user *sf, | ||
172 | struct pt_regs *regs, sigset_t *set) | ||
173 | { | ||
174 | int i, err = 0; | ||
175 | struct aux_context __user *aux = | ||
176 | (struct aux_context __user *)sf->uc.uc_mcontext.__reserved; | ||
177 | |||
178 | for (i = 0; i < 31; i++) | ||
179 | __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], | ||
180 | err); | ||
181 | __put_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); | ||
182 | __put_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); | ||
183 | __put_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); | ||
184 | |||
185 | __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err); | ||
186 | |||
187 | err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set)); | ||
188 | |||
189 | if (err == 0) | ||
190 | err |= preserve_fpsimd_context(&aux->fpsimd); | ||
191 | |||
192 | /* set the "end" magic */ | ||
193 | __put_user_error(0, &aux->end.magic, err); | ||
194 | __put_user_error(0, &aux->end.size, err); | ||
195 | |||
196 | return err; | ||
197 | } | ||
198 | |||
199 | static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, | ||
200 | int framesize) | ||
201 | { | ||
202 | unsigned long sp, sp_top; | ||
203 | void __user *frame; | ||
204 | |||
205 | sp = sp_top = regs->sp; | ||
206 | |||
207 | /* | ||
208 | * This is the X/Open sanctioned signal stack switching. | ||
209 | */ | ||
210 | if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) | ||
211 | sp = sp_top = current->sas_ss_sp + current->sas_ss_size; | ||
212 | |||
213 | /* room for stack frame (FP, LR) */ | ||
214 | sp -= 16; | ||
215 | |||
216 | sp = (sp - framesize) & ~15; | ||
217 | frame = (void __user *)sp; | ||
218 | |||
219 | /* | ||
220 | * Check that we can actually write to the signal frame. | ||
221 | */ | ||
222 | if (!access_ok(VERIFY_WRITE, frame, sp_top - sp)) | ||
223 | frame = NULL; | ||
224 | |||
225 | return frame; | ||
226 | } | ||
227 | |||
228 | static int setup_return(struct pt_regs *regs, struct k_sigaction *ka, | ||
229 | void __user *frame, int usig) | ||
230 | { | ||
231 | int err = 0; | ||
232 | __sigrestore_t sigtramp; | ||
233 | unsigned long __user *sp = (unsigned long __user *)regs->sp; | ||
234 | |||
235 | /* set up the stack frame */ | ||
236 | __put_user_error(regs->regs[29], sp - 2, err); | ||
237 | __put_user_error(regs->regs[30], sp - 1, err); | ||
238 | |||
239 | regs->regs[0] = usig; | ||
240 | regs->regs[29] = regs->sp - 16; | ||
241 | regs->sp = (unsigned long)frame; | ||
242 | regs->pc = (unsigned long)ka->sa.sa_handler; | ||
243 | |||
244 | if (ka->sa.sa_flags & SA_RESTORER) | ||
245 | sigtramp = ka->sa.sa_restorer; | ||
246 | else | ||
247 | sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp); | ||
248 | |||
249 | regs->regs[30] = (unsigned long)sigtramp; | ||
250 | |||
251 | return err; | ||
252 | } | ||
253 | |||
254 | static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, | ||
255 | sigset_t *set, struct pt_regs *regs) | ||
256 | { | ||
257 | struct rt_sigframe __user *frame; | ||
258 | stack_t stack; | ||
259 | int err = 0; | ||
260 | |||
261 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
262 | if (!frame) | ||
263 | return 1; | ||
264 | |||
265 | __put_user_error(0, &frame->uc.uc_flags, err); | ||
266 | __put_user_error(NULL, &frame->uc.uc_link, err); | ||
267 | |||
268 | memset(&stack, 0, sizeof(stack)); | ||
269 | stack.ss_sp = (void __user *)current->sas_ss_sp; | ||
270 | stack.ss_flags = sas_ss_flags(regs->sp); | ||
271 | stack.ss_size = current->sas_ss_size; | ||
272 | err |= __copy_to_user(&frame->uc.uc_stack, &stack, sizeof(stack)); | ||
273 | |||
274 | err |= setup_sigframe(frame, regs, set); | ||
275 | if (err == 0) | ||
276 | err = setup_return(regs, ka, frame, usig); | ||
277 | |||
278 | if (err == 0 && ka->sa.sa_flags & SA_SIGINFO) { | ||
279 | err |= copy_siginfo_to_user(&frame->info, info); | ||
280 | regs->regs[1] = (unsigned long)&frame->info; | ||
281 | regs->regs[2] = (unsigned long)&frame->uc; | ||
282 | } | ||
283 | |||
284 | return err; | ||
285 | } | ||
286 | |||
287 | static void setup_restart_syscall(struct pt_regs *regs) | ||
288 | { | ||
289 | if (is_compat_task()) | ||
290 | compat_setup_restart_syscall(regs); | ||
291 | else | ||
292 | regs->regs[8] = __NR_restart_syscall; | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * OK, we're invoking a handler | ||
297 | */ | ||
298 | static void handle_signal(unsigned long sig, struct k_sigaction *ka, | ||
299 | siginfo_t *info, struct pt_regs *regs) | ||
300 | { | ||
301 | struct thread_info *thread = current_thread_info(); | ||
302 | struct task_struct *tsk = current; | ||
303 | sigset_t *oldset = sigmask_to_save(); | ||
304 | int usig = sig; | ||
305 | int ret; | ||
306 | |||
307 | /* | ||
308 | * translate the signal | ||
309 | */ | ||
310 | if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) | ||
311 | usig = thread->exec_domain->signal_invmap[usig]; | ||
312 | |||
313 | /* | ||
314 | * Set up the stack frame | ||
315 | */ | ||
316 | if (is_compat_task()) { | ||
317 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
318 | ret = compat_setup_rt_frame(usig, ka, info, oldset, | ||
319 | regs); | ||
320 | else | ||
321 | ret = compat_setup_frame(usig, ka, oldset, regs); | ||
322 | } else { | ||
323 | ret = setup_rt_frame(usig, ka, info, oldset, regs); | ||
324 | } | ||
325 | |||
326 | /* | ||
327 | * Check that the resulting registers are actually sane. | ||
328 | */ | ||
329 | ret |= !valid_user_regs(®s->user_regs); | ||
330 | |||
331 | if (ret != 0) { | ||
332 | force_sigsegv(sig, tsk); | ||
333 | return; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * Fast forward the stepping logic so we step into the signal | ||
338 | * handler. | ||
339 | */ | ||
340 | user_fastforward_single_step(tsk); | ||
341 | |||
342 | signal_delivered(sig, info, ka, regs, 0); | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
347 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
348 | * mistake. | ||
349 | * | ||
350 | * Note that we go through the signals twice: once to check the signals that | ||
351 | * the kernel can handle, and then we build all the user-level signal handling | ||
352 | * stack-frames in one go after that. | ||
353 | */ | ||
354 | static void do_signal(struct pt_regs *regs) | ||
355 | { | ||
356 | unsigned long continue_addr = 0, restart_addr = 0; | ||
357 | struct k_sigaction ka; | ||
358 | siginfo_t info; | ||
359 | int signr, retval = 0; | ||
360 | int syscall = (int)regs->syscallno; | ||
361 | |||
362 | /* | ||
363 | * If we were from a system call, check for system call restarting... | ||
364 | */ | ||
365 | if (syscall >= 0) { | ||
366 | continue_addr = regs->pc; | ||
367 | restart_addr = continue_addr - (compat_thumb_mode(regs) ? 2 : 4); | ||
368 | retval = regs->regs[0]; | ||
369 | |||
370 | /* | ||
371 | * Avoid additional syscall restarting via ret_to_user. | ||
372 | */ | ||
373 | regs->syscallno = ~0UL; | ||
374 | |||
375 | /* | ||
376 | * Prepare for system call restart. We do this here so that a | ||
377 | * debugger will see the already changed PC. | ||
378 | */ | ||
379 | switch (retval) { | ||
380 | case -ERESTARTNOHAND: | ||
381 | case -ERESTARTSYS: | ||
382 | case -ERESTARTNOINTR: | ||
383 | case -ERESTART_RESTARTBLOCK: | ||
384 | regs->regs[0] = regs->orig_x0; | ||
385 | regs->pc = restart_addr; | ||
386 | break; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * Get the signal to deliver. When running under ptrace, at this point | ||
392 | * the debugger may change all of our registers. | ||
393 | */ | ||
394 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
395 | if (signr > 0) { | ||
396 | /* | ||
397 | * Depending on the signal settings, we may need to revert the | ||
398 | * decision to restart the system call, but skip this if a | ||
399 | * debugger has chosen to restart at a different PC. | ||
400 | */ | ||
401 | if (regs->pc == restart_addr && | ||
402 | (retval == -ERESTARTNOHAND || | ||
403 | retval == -ERESTART_RESTARTBLOCK || | ||
404 | (retval == -ERESTARTSYS && | ||
405 | !(ka.sa.sa_flags & SA_RESTART)))) { | ||
406 | regs->regs[0] = -EINTR; | ||
407 | regs->pc = continue_addr; | ||
408 | } | ||
409 | |||
410 | handle_signal(signr, &ka, &info, regs); | ||
411 | return; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * Handle restarting a different system call. As above, if a debugger | ||
416 | * has chosen to restart at a different PC, ignore the restart. | ||
417 | */ | ||
418 | if (syscall >= 0 && regs->pc == restart_addr) { | ||
419 | if (retval == -ERESTART_RESTARTBLOCK) | ||
420 | setup_restart_syscall(regs); | ||
421 | user_rewind_single_step(current); | ||
422 | } | ||
423 | |||
424 | restore_saved_sigmask(); | ||
425 | } | ||
426 | |||
427 | asmlinkage void do_notify_resume(struct pt_regs *regs, | ||
428 | unsigned int thread_flags) | ||
429 | { | ||
430 | if (thread_flags & _TIF_SIGPENDING) | ||
431 | do_signal(regs); | ||
432 | |||
433 | if (thread_flags & _TIF_NOTIFY_RESUME) { | ||
434 | clear_thread_flag(TIF_NOTIFY_RESUME); | ||
435 | tracehook_notify_resume(regs); | ||
436 | } | ||
437 | } | ||