blob: fa54410c666652cf40cbfc63c2cd5fda87a5689f [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 Torvalds1361b832012-02-21 13:19:22 -080025#include <asm/fpu-internal.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <asm/ptrace.h>
27#include <asm/ia32_unistd.h>
28#include <asm/user32.h>
29#include <asm/sigcontext32.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <asm/proto.h>
Roland McGrathaf65d642008-01-30 13:30:43 +010031#include <asm/vdso.h>
Hiroshi Shimamotod98f9d82008-12-17 18:52:45 -080032#include <asm/sigframe.h>
H. Peter Anvinf28f0c22012-02-19 07:38:43 -080033#include <asm/sighandling.h>
Jaswinder Singh Rajput2f06de02008-12-27 21:37:10 +053034#include <asm/sys_ia32.h>
Hiroshi Shimamotod98f9d82008-12-17 18:52:45 -080035
H. Peter Anvinf28f0c22012-02-19 07:38:43 -080036#define FIX_EFLAGS __FIX_EFLAGS
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
38int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
39{
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080040 int err = 0;
H. Peter Anvinbb6fa8b2012-03-13 22:44:41 -070041 bool ia32 = is_ia32_task();
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010042
43 if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 return -EFAULT;
45
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080046 put_user_try {
47 /* If you change siginfo_t structure, please make sure that
48 this code is fixed accordingly.
49 It should never copy any pad contained in the structure
50 to avoid security leaks, but must copy the generic
51 3 ints plus the relevant union member. */
52 put_user_ex(from->si_signo, &to->si_signo);
53 put_user_ex(from->si_errno, &to->si_errno);
54 put_user_ex((short)from->si_code, &to->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080056 if (from->si_code < 0) {
57 put_user_ex(from->si_pid, &to->si_pid);
58 put_user_ex(from->si_uid, &to->si_uid);
59 put_user_ex(ptr_to_compat(from->si_ptr), &to->si_ptr);
60 } else {
61 /*
62 * First 32bits of unions are always present:
63 * si_pid === si_band === si_tid === si_addr(LS half)
64 */
65 put_user_ex(from->_sifields._pad[0],
66 &to->_sifields._pad[0]);
67 switch (from->si_code >> 16) {
68 case __SI_FAULT >> 16:
69 break;
70 case __SI_CHLD >> 16:
H. Peter Anvine7084fd2012-03-05 13:40:24 -080071 if (ia32) {
72 put_user_ex(from->si_utime, &to->si_utime);
73 put_user_ex(from->si_stime, &to->si_stime);
74 } else {
75 put_user_ex(from->si_utime, &to->_sifields._sigchld_x32._utime);
76 put_user_ex(from->si_stime, &to->_sifields._sigchld_x32._stime);
77 }
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080078 put_user_ex(from->si_status, &to->si_status);
79 /* FALL THROUGH */
80 default:
81 case __SI_KILL >> 16:
82 put_user_ex(from->si_uid, &to->si_uid);
83 break;
84 case __SI_POLL >> 16:
85 put_user_ex(from->si_fd, &to->si_fd);
86 break;
87 case __SI_TIMER >> 16:
88 put_user_ex(from->si_overrun, &to->si_overrun);
89 put_user_ex(ptr_to_compat(from->si_ptr),
90 &to->si_ptr);
91 break;
92 /* This is not generated by the kernel as of now. */
93 case __SI_RT >> 16:
94 case __SI_MESGQ >> 16:
95 put_user_ex(from->si_uid, &to->si_uid);
96 put_user_ex(from->si_int, &to->si_int);
97 break;
98 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 }
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800100 } put_user_catch(err);
101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 return err;
103}
104
105int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
106{
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800107 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 u32 ptr32;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100109
110 if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 return -EFAULT;
112
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800113 get_user_try {
114 get_user_ex(to->si_signo, &from->si_signo);
115 get_user_ex(to->si_errno, &from->si_errno);
116 get_user_ex(to->si_code, &from->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800118 get_user_ex(to->si_pid, &from->si_pid);
119 get_user_ex(to->si_uid, &from->si_uid);
120 get_user_ex(ptr32, &from->si_ptr);
121 to->si_ptr = compat_ptr(ptr32);
122 } get_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
124 return err;
125}
126
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100127asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200129 sigset_t blocked;
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200130 siginitset(&blocked, mask);
Al Viro68f3f162012-05-21 21:42:32 -0400131 return sigsuspend(&blocked);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132}
133
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100134asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
135 stack_ia32_t __user *uoss_ptr,
136 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100138 stack_t uss, uoss;
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800139 int ret, err = 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100140 mm_segment_t seg;
141
142 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100144
145 memset(&uss, 0, sizeof(stack_t));
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800146 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)))
147 return -EFAULT;
148
149 get_user_try {
150 get_user_ex(ptr, &uss_ptr->ss_sp);
151 get_user_ex(uss.ss_flags, &uss_ptr->ss_flags);
152 get_user_ex(uss.ss_size, &uss_ptr->ss_size);
153 } get_user_catch(err);
154
155 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 return -EFAULT;
157 uss.ss_sp = compat_ptr(ptr);
158 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100159 seg = get_fs();
160 set_fs(KERNEL_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100161 ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100162 set_fs(seg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 if (ret >= 0 && uoss_ptr) {
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800164 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)))
165 return -EFAULT;
166
167 put_user_try {
168 put_user_ex(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp);
169 put_user_ex(uoss.ss_flags, &uoss_ptr->ss_flags);
170 put_user_ex(uoss.ss_size, &uoss_ptr->ss_size);
171 } put_user_catch(err);
172
173 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100175 }
176 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177}
178
179/*
180 * Do a signal return; undo the signal stack.
181 */
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800182#define loadsegment_gs(v) load_gs_index(v)
183#define loadsegment_fs(v) loadsegment(fs, v)
184#define loadsegment_ds(v) loadsegment(ds, v)
185#define loadsegment_es(v) loadsegment(es, v)
186
187#define get_user_seg(seg) ({ unsigned int v; savesegment(seg, v); v; })
188#define set_user_seg(seg, v) loadsegment_##seg(v)
189
Hiroshi Shimamotob78a5b52008-11-17 15:44:50 -0800190#define COPY(x) { \
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800191 get_user_ex(regs->x, &sc->x); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192}
193
Hiroshi Shimamoto8801ead2009-02-20 19:00:13 -0800194#define GET_SEG(seg) ({ \
195 unsigned short tmp; \
196 get_user_ex(tmp, &sc->seg); \
197 tmp; \
198})
199
200#define COPY_SEG_CPL3(seg) do { \
201 regs->seg = GET_SEG(seg) | 3; \
202} while (0)
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800203
Hiroshi Shimamoto8c6e5ce02008-11-17 15:47:48 -0800204#define RELOAD_SEG(seg) { \
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800205 unsigned int pre = GET_SEG(seg); \
206 unsigned int cur = get_user_seg(seg); \
Hiroshi Shimamoto8c6e5ce02008-11-17 15:47:48 -0800207 pre |= 3; \
208 if (pre != cur) \
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800209 set_user_seg(seg, pre); \
Hiroshi Shimamoto8c6e5ce02008-11-17 15:47:48 -0800210}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100212static int ia32_restore_sigcontext(struct pt_regs *regs,
213 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800214 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100215{
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800216 unsigned int tmpflags, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700217 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100218 u32 tmp;
219
220 /* Always make any pending restarted system calls return -EINTR */
221 current_thread_info()->restart_block.fn = do_no_restart_syscall;
222
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800223 get_user_try {
224 /*
225 * Reload fs and gs if they have changed in the signal
226 * handler. This does not handle long fs/gs base changes in
227 * the handler, but does not clobber them at least in the
228 * normal case.
229 */
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800230 RELOAD_SEG(gs);
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800231 RELOAD_SEG(fs);
232 RELOAD_SEG(ds);
233 RELOAD_SEG(es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800235 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
236 COPY(dx); COPY(cx); COPY(ip);
237 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800239 COPY_SEG_CPL3(cs);
240 COPY_SEG_CPL3(ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800242 get_user_ex(tmpflags, &sc->flags);
243 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
244 /* disable syscall checks */
245 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100246
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800247 get_user_ex(tmp, &sc->fpstate);
248 buf = compat_ptr(tmp);
249 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800251 get_user_ex(*pax, &sc->ax);
252 } get_user_catch(err);
253
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255}
256
257asmlinkage long sys32_sigreturn(struct pt_regs *regs)
258{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800259 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100261 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
264 goto badframe;
265 if (__get_user(set.sig[0], &frame->sc.oldmask)
266 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100267 && __copy_from_user((((char *) &set.sig) + 4),
268 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 sizeof(frame->extramask))))
270 goto badframe;
271
272 sigdelsetmask(&set, ~_BLOCKABLE);
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200273 set_current_blocked(&set);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100274
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100275 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100277 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
279badframe:
280 signal_fault(regs, frame, "32bit sigreturn");
281 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100282}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
284asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
285{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800286 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100288 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 struct pt_regs tregs;
290
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800291 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
294 goto badframe;
295 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
296 goto badframe;
297
298 sigdelsetmask(&set, ~_BLOCKABLE);
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200299 set_current_blocked(&set);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100300
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100301 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 goto badframe;
303
304 tregs = *regs;
305 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
306 goto badframe;
307
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100308 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100311 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100313}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
315/*
316 * Set up a signal frame.
317 */
318
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100319static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700320 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100321 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800323 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800325 put_user_try {
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800326 put_user_ex(get_user_seg(gs), (unsigned int __user *)&sc->gs);
327 put_user_ex(get_user_seg(fs), (unsigned int __user *)&sc->fs);
328 put_user_ex(get_user_seg(ds), (unsigned int __user *)&sc->ds);
329 put_user_ex(get_user_seg(es), (unsigned int __user *)&sc->es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800331 put_user_ex(regs->di, &sc->di);
332 put_user_ex(regs->si, &sc->si);
333 put_user_ex(regs->bp, &sc->bp);
334 put_user_ex(regs->sp, &sc->sp);
335 put_user_ex(regs->bx, &sc->bx);
336 put_user_ex(regs->dx, &sc->dx);
337 put_user_ex(regs->cx, &sc->cx);
338 put_user_ex(regs->ax, &sc->ax);
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530339 put_user_ex(current->thread.trap_nr, &sc->trapno);
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800340 put_user_ex(current->thread.error_code, &sc->err);
341 put_user_ex(regs->ip, &sc->ip);
342 put_user_ex(regs->cs, (unsigned int __user *)&sc->cs);
343 put_user_ex(regs->flags, &sc->flags);
344 put_user_ex(regs->sp, &sc->sp_at_signal);
345 put_user_ex(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800347 put_user_ex(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800349 /* non-iBCS2 extensions.. */
350 put_user_ex(mask, &sc->oldmask);
351 put_user_ex(current->thread.cr2, &sc->cr2);
352 } put_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354 return err;
355}
356
357/*
358 * Determine which stack to use..
359 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100360static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700361 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700362 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100364 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
366 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100367 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
369 /* This is the X/Open sanctioned signal stack switching. */
370 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100371 if (sas_ss_flags(sp) == 0)
372 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 }
374
375 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800376 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100378 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100379 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700381 if (used_math()) {
382 sp = sp - sig_xstate_ia32_size;
383 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800384 if (save_i387_xstate_ia32(*fpstate) < 0)
385 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700386 }
387
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100388 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200389 /* Align the stack pointer according to the i386 ABI,
390 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100391 sp = ((sp + 4) & -16ul) - 4;
392 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393}
394
Roland McGrath0928d6e2005-06-23 00:08:37 -0700395int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100396 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800398 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100399 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700401 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100403 /* copy_to_user optimizes that into a single 8 byte store */
404 static const struct {
405 u16 poplmovl;
406 u32 val;
407 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100408 } __attribute__((packed)) code = {
409 0xb858, /* popl %eax ; movl $...,%eax */
410 __NR_ia32_sigreturn,
411 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100412 };
413
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700414 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
416 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700417 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700419 if (__put_user(sig, &frame->sig))
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 (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700423 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
425 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700426 if (__copy_to_user(frame->extramask, &set->sig[1],
427 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700428 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
Roland McGrathaf65d642008-01-30 13:30:43 +0100431 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100432 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100433 } else {
434 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700435 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100436 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
437 sigreturn);
438 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100439 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100440 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100441
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800442 put_user_try {
443 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
444
445 /*
446 * These are actually not used anymore, but left because some
447 * gdb versions depend on them as a marker.
448 */
449 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
450 } put_user_catch(err);
451
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700453 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
455 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100456 regs->sp = (unsigned long) frame;
457 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Andi Kleen536e3ee2006-09-26 10:52:41 +0200459 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100460 regs->ax = sig;
461 regs->dx = 0;
462 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200463
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700464 loadsegment(ds, __USER32_DS);
465 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100467 regs->cs = __USER32_CS;
468 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
Andi Kleen1d001df2006-09-26 10:52:26 +0200470 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
Roland McGrath0928d6e2005-06-23 00:08:37 -0700473int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100474 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800476 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100477 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700479 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100481 /* __copy_to_user optimizes that into a single 8 byte store */
482 static const struct {
483 u8 movl;
484 u32 val;
485 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800486 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100487 } __attribute__((packed)) code = {
488 0xb8,
489 __NR_ia32_rt_sigreturn,
490 0x80cd,
491 0,
492 };
493
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700494 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
496 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700497 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800499 put_user_try {
500 put_user_ex(sig, &frame->sig);
501 put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo);
502 put_user_ex(ptr_to_compat(&frame->uc), &frame->puc);
503 err |= copy_siginfo_to_user32(&frame->info, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800505 /* Create the ucontext. */
506 if (cpu_has_xsave)
507 put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
508 else
509 put_user_ex(0, &frame->uc.uc_flags);
510 put_user_ex(0, &frame->uc.uc_link);
511 put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
512 put_user_ex(sas_ss_flags(regs->sp),
513 &frame->uc.uc_stack.ss_flags);
514 put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
515 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
516 regs, set->sig[0]);
517 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800519 if (ka->sa.sa_flags & SA_RESTORER)
520 restorer = ka->sa.sa_restorer;
521 else
522 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
523 rt_sigreturn);
524 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800526 /*
527 * Not actually used anymore, but left because some gdb
528 * versions need it.
529 */
530 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
531 } put_user_catch(err);
532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700534 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
536 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100537 regs->sp = (unsigned long) frame;
538 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500540 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100541 regs->ax = sig;
542 regs->dx = (unsigned long) &frame->info;
543 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500544
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700545 loadsegment(ds, __USER32_DS);
546 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100547
548 regs->cs = __USER32_CS;
549 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
Andi Kleen1d001df2006-09-26 10:52:26 +0200551 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552}