blob: 25d80f3faf2ea0102cd24ef21b3999dd2b26268c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/arch/x86_64/ia32/ia32_signal.c
3 *
4 * Copyright (C) 1991, 1992 Linus Torvalds
5 *
6 * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
7 * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes
8 * 2000-12-* x86-64 compatibility mode signal handling by Andi Kleen
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10
11#include <linux/sched.h>
12#include <linux/mm.h>
13#include <linux/smp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/errno.h>
16#include <linux/wait.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/unistd.h>
18#include <linux/stddef.h>
19#include <linux/personality.h>
20#include <linux/compat.h>
Andi Kleen9fbbd4d2007-02-13 13:26:26 +010021#include <linux/binfmts.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <asm/ucontext.h>
23#include <asm/uaccess.h>
24#include <asm/i387.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <asm/ptrace.h>
26#include <asm/ia32_unistd.h>
27#include <asm/user32.h>
28#include <asm/sigcontext32.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <asm/proto.h>
Roland McGrathaf65d642008-01-30 13:30:43 +010030#include <asm/vdso.h>
Hiroshi Shimamotod98f9d82008-12-17 18:52:45 -080031#include <asm/sigframe.h>
H. Peter Anvinf28f0c22012-02-19 07:38:43 -080032#include <asm/sighandling.h>
Jaswinder Singh Rajput2f06de02008-12-27 21:37:10 +053033#include <asm/sys_ia32.h>
Hiroshi Shimamotod98f9d82008-12-17 18:52:45 -080034
H. Peter Anvinf28f0c22012-02-19 07:38:43 -080035#define FIX_EFLAGS __FIX_EFLAGS
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
38{
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080039 int err = 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010040
41 if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 return -EFAULT;
43
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080044 put_user_try {
45 /* If you change siginfo_t structure, please make sure that
46 this code is fixed accordingly.
47 It should never copy any pad contained in the structure
48 to avoid security leaks, but must copy the generic
49 3 ints plus the relevant union member. */
50 put_user_ex(from->si_signo, &to->si_signo);
51 put_user_ex(from->si_errno, &to->si_errno);
52 put_user_ex((short)from->si_code, &to->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080054 if (from->si_code < 0) {
55 put_user_ex(from->si_pid, &to->si_pid);
56 put_user_ex(from->si_uid, &to->si_uid);
57 put_user_ex(ptr_to_compat(from->si_ptr), &to->si_ptr);
58 } else {
59 /*
60 * First 32bits of unions are always present:
61 * si_pid === si_band === si_tid === si_addr(LS half)
62 */
63 put_user_ex(from->_sifields._pad[0],
64 &to->_sifields._pad[0]);
65 switch (from->si_code >> 16) {
66 case __SI_FAULT >> 16:
67 break;
68 case __SI_CHLD >> 16:
69 put_user_ex(from->si_utime, &to->si_utime);
70 put_user_ex(from->si_stime, &to->si_stime);
71 put_user_ex(from->si_status, &to->si_status);
72 /* FALL THROUGH */
73 default:
74 case __SI_KILL >> 16:
75 put_user_ex(from->si_uid, &to->si_uid);
76 break;
77 case __SI_POLL >> 16:
78 put_user_ex(from->si_fd, &to->si_fd);
79 break;
80 case __SI_TIMER >> 16:
81 put_user_ex(from->si_overrun, &to->si_overrun);
82 put_user_ex(ptr_to_compat(from->si_ptr),
83 &to->si_ptr);
84 break;
85 /* This is not generated by the kernel as of now. */
86 case __SI_RT >> 16:
87 case __SI_MESGQ >> 16:
88 put_user_ex(from->si_uid, &to->si_uid);
89 put_user_ex(from->si_int, &to->si_int);
90 break;
91 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 }
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080093 } put_user_catch(err);
94
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 return err;
96}
97
98int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
99{
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800100 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 u32 ptr32;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100102
103 if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 return -EFAULT;
105
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800106 get_user_try {
107 get_user_ex(to->si_signo, &from->si_signo);
108 get_user_ex(to->si_errno, &from->si_errno);
109 get_user_ex(to->si_code, &from->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800111 get_user_ex(to->si_pid, &from->si_pid);
112 get_user_ex(to->si_uid, &from->si_uid);
113 get_user_ex(ptr32, &from->si_ptr);
114 to->si_ptr = compat_ptr(ptr32);
115 } get_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
117 return err;
118}
119
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100120asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121{
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200122 sigset_t blocked;
123
Andi Kleen1d001df2006-09-26 10:52:26 +0200124 current->saved_sigmask = current->blocked;
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200125
126 mask &= _BLOCKABLE;
127 siginitset(&blocked, mask);
128 set_current_blocked(&blocked);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
Andi Kleen1d001df2006-09-26 10:52:26 +0200130 current->state = TASK_INTERRUPTIBLE;
131 schedule();
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200132
Roland McGrath5a8da0e2008-04-30 00:53:10 -0700133 set_restore_sigmask();
Andi Kleen1d001df2006-09-26 10:52:26 +0200134 return -ERESTARTNOHAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135}
136
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100137asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
138 stack_ia32_t __user *uoss_ptr,
139 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100141 stack_t uss, uoss;
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800142 int ret, err = 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100143 mm_segment_t seg;
144
145 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100147
148 memset(&uss, 0, sizeof(stack_t));
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800149 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)))
150 return -EFAULT;
151
152 get_user_try {
153 get_user_ex(ptr, &uss_ptr->ss_sp);
154 get_user_ex(uss.ss_flags, &uss_ptr->ss_flags);
155 get_user_ex(uss.ss_size, &uss_ptr->ss_size);
156 } get_user_catch(err);
157
158 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return -EFAULT;
160 uss.ss_sp = compat_ptr(ptr);
161 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100162 seg = get_fs();
163 set_fs(KERNEL_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100164 ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100165 set_fs(seg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (ret >= 0 && uoss_ptr) {
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800167 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)))
168 return -EFAULT;
169
170 put_user_try {
171 put_user_ex(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp);
172 put_user_ex(uoss.ss_flags, &uoss_ptr->ss_flags);
173 put_user_ex(uoss.ss_size, &uoss_ptr->ss_size);
174 } put_user_catch(err);
175
176 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100178 }
179 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
182/*
183 * Do a signal return; undo the signal stack.
184 */
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800185#define loadsegment_gs(v) load_gs_index(v)
186#define loadsegment_fs(v) loadsegment(fs, v)
187#define loadsegment_ds(v) loadsegment(ds, v)
188#define loadsegment_es(v) loadsegment(es, v)
189
190#define get_user_seg(seg) ({ unsigned int v; savesegment(seg, v); v; })
191#define set_user_seg(seg, v) loadsegment_##seg(v)
192
Hiroshi Shimamotob78a5b52008-11-17 15:44:50 -0800193#define COPY(x) { \
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800194 get_user_ex(regs->x, &sc->x); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195}
196
Hiroshi Shimamoto8801ead2009-02-20 19:00:13 -0800197#define GET_SEG(seg) ({ \
198 unsigned short tmp; \
199 get_user_ex(tmp, &sc->seg); \
200 tmp; \
201})
202
203#define COPY_SEG_CPL3(seg) do { \
204 regs->seg = GET_SEG(seg) | 3; \
205} while (0)
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800206
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800207#define RELOAD_SEG(seg) { \
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800208 unsigned int pre = GET_SEG(seg); \
209 unsigned int cur = get_user_seg(seg); \
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800210 pre |= 3; \
211 if (pre != cur) \
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800212 set_user_seg(seg, pre); \
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800213}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100215static int ia32_restore_sigcontext(struct pt_regs *regs,
216 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800217 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100218{
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800219 unsigned int tmpflags, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700220 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100221 u32 tmp;
222
223 /* Always make any pending restarted system calls return -EINTR */
224 current_thread_info()->restart_block.fn = do_no_restart_syscall;
225
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800226 get_user_try {
227 /*
228 * Reload fs and gs if they have changed in the signal
229 * handler. This does not handle long fs/gs base changes in
230 * the handler, but does not clobber them at least in the
231 * normal case.
232 */
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800233 RELOAD_SEG(gs);
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800234 RELOAD_SEG(fs);
235 RELOAD_SEG(ds);
236 RELOAD_SEG(es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800238 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
239 COPY(dx); COPY(cx); COPY(ip);
240 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800242 COPY_SEG_CPL3(cs);
243 COPY_SEG_CPL3(ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800245 get_user_ex(tmpflags, &sc->flags);
246 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
247 /* disable syscall checks */
248 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100249
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800250 get_user_ex(tmp, &sc->fpstate);
251 buf = compat_ptr(tmp);
252 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800254 get_user_ex(*pax, &sc->ax);
255 } get_user_catch(err);
256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258}
259
260asmlinkage long sys32_sigreturn(struct pt_regs *regs)
261{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800262 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100264 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
266 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
267 goto badframe;
268 if (__get_user(set.sig[0], &frame->sc.oldmask)
269 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100270 && __copy_from_user((((char *) &set.sig) + 4),
271 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 sizeof(frame->extramask))))
273 goto badframe;
274
275 sigdelsetmask(&set, ~_BLOCKABLE);
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200276 set_current_blocked(&set);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100277
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100278 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100280 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282badframe:
283 signal_fault(regs, frame, "32bit sigreturn");
284 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100285}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
287asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
288{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800289 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100291 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 struct pt_regs tregs;
293
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800294 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
297 goto badframe;
298 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
299 goto badframe;
300
301 sigdelsetmask(&set, ~_BLOCKABLE);
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200302 set_current_blocked(&set);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100303
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100304 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 goto badframe;
306
307 tregs = *regs;
308 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
309 goto badframe;
310
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100311 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100314 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100316}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
318/*
319 * Set up a signal frame.
320 */
321
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100322static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700323 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100324 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800326 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800328 put_user_try {
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800329 put_user_ex(get_user_seg(gs), (unsigned int __user *)&sc->gs);
330 put_user_ex(get_user_seg(fs), (unsigned int __user *)&sc->fs);
331 put_user_ex(get_user_seg(ds), (unsigned int __user *)&sc->ds);
332 put_user_ex(get_user_seg(es), (unsigned int __user *)&sc->es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800334 put_user_ex(regs->di, &sc->di);
335 put_user_ex(regs->si, &sc->si);
336 put_user_ex(regs->bp, &sc->bp);
337 put_user_ex(regs->sp, &sc->sp);
338 put_user_ex(regs->bx, &sc->bx);
339 put_user_ex(regs->dx, &sc->dx);
340 put_user_ex(regs->cx, &sc->cx);
341 put_user_ex(regs->ax, &sc->ax);
342 put_user_ex(current->thread.trap_no, &sc->trapno);
343 put_user_ex(current->thread.error_code, &sc->err);
344 put_user_ex(regs->ip, &sc->ip);
345 put_user_ex(regs->cs, (unsigned int __user *)&sc->cs);
346 put_user_ex(regs->flags, &sc->flags);
347 put_user_ex(regs->sp, &sc->sp_at_signal);
348 put_user_ex(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800350 put_user_ex(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800352 /* non-iBCS2 extensions.. */
353 put_user_ex(mask, &sc->oldmask);
354 put_user_ex(current->thread.cr2, &sc->cr2);
355 } put_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
357 return err;
358}
359
360/*
361 * Determine which stack to use..
362 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100363static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700364 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700365 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100367 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
369 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100370 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372 /* This is the X/Open sanctioned signal stack switching. */
373 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100374 if (sas_ss_flags(sp) == 0)
375 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
377
378 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800379 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100381 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100382 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700384 if (used_math()) {
385 sp = sp - sig_xstate_ia32_size;
386 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800387 if (save_i387_xstate_ia32(*fpstate) < 0)
388 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700389 }
390
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100391 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200392 /* Align the stack pointer according to the i386 ABI,
393 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100394 sp = ((sp + 4) & -16ul) - 4;
395 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396}
397
Roland McGrath0928d6e2005-06-23 00:08:37 -0700398int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100399 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800401 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100402 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700404 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100406 /* copy_to_user optimizes that into a single 8 byte store */
407 static const struct {
408 u16 poplmovl;
409 u32 val;
410 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100411 } __attribute__((packed)) code = {
412 0xb858, /* popl %eax ; movl $...,%eax */
413 __NR_ia32_sigreturn,
414 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100415 };
416
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700417 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700420 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700422 if (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700423 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700425 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700426 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
428 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700429 if (__copy_to_user(frame->extramask, &set->sig[1],
430 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700431 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
Roland McGrathaf65d642008-01-30 13:30:43 +0100434 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100435 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100436 } else {
437 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700438 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100439 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
440 sigreturn);
441 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100442 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100443 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100444
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800445 put_user_try {
446 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
447
448 /*
449 * These are actually not used anymore, but left because some
450 * gdb versions depend on them as a marker.
451 */
452 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
453 } put_user_catch(err);
454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700456 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100459 regs->sp = (unsigned long) frame;
460 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Andi Kleen536e3ee2006-09-26 10:52:41 +0200462 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100463 regs->ax = sig;
464 regs->dx = 0;
465 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200466
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700467 loadsegment(ds, __USER32_DS);
468 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100470 regs->cs = __USER32_CS;
471 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Andi Kleen1d001df2006-09-26 10:52:26 +0200473 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474}
475
Roland McGrath0928d6e2005-06-23 00:08:37 -0700476int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100477 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800479 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100480 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700482 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100484 /* __copy_to_user optimizes that into a single 8 byte store */
485 static const struct {
486 u8 movl;
487 u32 val;
488 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800489 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100490 } __attribute__((packed)) code = {
491 0xb8,
492 __NR_ia32_rt_sigreturn,
493 0x80cd,
494 0,
495 };
496
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700497 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700500 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800502 put_user_try {
503 put_user_ex(sig, &frame->sig);
504 put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo);
505 put_user_ex(ptr_to_compat(&frame->uc), &frame->puc);
506 err |= copy_siginfo_to_user32(&frame->info, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800508 /* Create the ucontext. */
509 if (cpu_has_xsave)
510 put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
511 else
512 put_user_ex(0, &frame->uc.uc_flags);
513 put_user_ex(0, &frame->uc.uc_link);
514 put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
515 put_user_ex(sas_ss_flags(regs->sp),
516 &frame->uc.uc_stack.ss_flags);
517 put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
518 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
519 regs, set->sig[0]);
520 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800522 if (ka->sa.sa_flags & SA_RESTORER)
523 restorer = ka->sa.sa_restorer;
524 else
525 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
526 rt_sigreturn);
527 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800529 /*
530 * Not actually used anymore, but left because some gdb
531 * versions need it.
532 */
533 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
534 } put_user_catch(err);
535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700537 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100540 regs->sp = (unsigned long) frame;
541 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500543 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100544 regs->ax = sig;
545 regs->dx = (unsigned long) &frame->info;
546 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500547
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700548 loadsegment(ds, __USER32_DS);
549 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100550
551 regs->cs = __USER32_CS;
552 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Andi Kleen1d001df2006-09-26 10:52:26 +0200554 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555}