blob: 0b3f2354f6aaa168e7800cd19fbcbdb55fd28f97 [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;
Will Drewrya0727e82012-04-12 16:48:00 -050070 case __SI_SYS >> 16:
71 put_user_ex(from->si_syscall, &to->si_syscall);
72 put_user_ex(from->si_arch, &to->si_arch);
73 break;
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080074 case __SI_CHLD >> 16:
H. Peter Anvine7084fd2012-03-05 13:40:24 -080075 if (ia32) {
76 put_user_ex(from->si_utime, &to->si_utime);
77 put_user_ex(from->si_stime, &to->si_stime);
78 } else {
79 put_user_ex(from->si_utime, &to->_sifields._sigchld_x32._utime);
80 put_user_ex(from->si_stime, &to->_sifields._sigchld_x32._stime);
81 }
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080082 put_user_ex(from->si_status, &to->si_status);
83 /* FALL THROUGH */
84 default:
85 case __SI_KILL >> 16:
86 put_user_ex(from->si_uid, &to->si_uid);
87 break;
88 case __SI_POLL >> 16:
89 put_user_ex(from->si_fd, &to->si_fd);
90 break;
91 case __SI_TIMER >> 16:
92 put_user_ex(from->si_overrun, &to->si_overrun);
93 put_user_ex(ptr_to_compat(from->si_ptr),
94 &to->si_ptr);
95 break;
96 /* This is not generated by the kernel as of now. */
97 case __SI_RT >> 16:
98 case __SI_MESGQ >> 16:
99 put_user_ex(from->si_uid, &to->si_uid);
100 put_user_ex(from->si_int, &to->si_int);
101 break;
102 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 }
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800104 } put_user_catch(err);
105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 return err;
107}
108
109int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
110{
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800111 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 u32 ptr32;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100113
114 if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 return -EFAULT;
116
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800117 get_user_try {
118 get_user_ex(to->si_signo, &from->si_signo);
119 get_user_ex(to->si_errno, &from->si_errno);
120 get_user_ex(to->si_code, &from->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800122 get_user_ex(to->si_pid, &from->si_pid);
123 get_user_ex(to->si_uid, &from->si_uid);
124 get_user_ex(ptr32, &from->si_ptr);
125 to->si_ptr = compat_ptr(ptr32);
126 } get_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 return err;
129}
130
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100131asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132{
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200133 sigset_t blocked;
134
Andi Kleen1d001df2006-09-26 10:52:26 +0200135 current->saved_sigmask = current->blocked;
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200136
137 mask &= _BLOCKABLE;
138 siginitset(&blocked, mask);
139 set_current_blocked(&blocked);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
Andi Kleen1d001df2006-09-26 10:52:26 +0200141 current->state = TASK_INTERRUPTIBLE;
142 schedule();
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200143
Roland McGrath5a8da0e2008-04-30 00:53:10 -0700144 set_restore_sigmask();
Andi Kleen1d001df2006-09-26 10:52:26 +0200145 return -ERESTARTNOHAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100148asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
149 stack_ia32_t __user *uoss_ptr,
150 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100152 stack_t uss, uoss;
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800153 int ret, err = 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100154 mm_segment_t seg;
155
156 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100158
159 memset(&uss, 0, sizeof(stack_t));
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800160 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)))
161 return -EFAULT;
162
163 get_user_try {
164 get_user_ex(ptr, &uss_ptr->ss_sp);
165 get_user_ex(uss.ss_flags, &uss_ptr->ss_flags);
166 get_user_ex(uss.ss_size, &uss_ptr->ss_size);
167 } get_user_catch(err);
168
169 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 return -EFAULT;
171 uss.ss_sp = compat_ptr(ptr);
172 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100173 seg = get_fs();
174 set_fs(KERNEL_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100175 ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100176 set_fs(seg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 if (ret >= 0 && uoss_ptr) {
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800178 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)))
179 return -EFAULT;
180
181 put_user_try {
182 put_user_ex(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp);
183 put_user_ex(uoss.ss_flags, &uoss_ptr->ss_flags);
184 put_user_ex(uoss.ss_size, &uoss_ptr->ss_size);
185 } put_user_catch(err);
186
187 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100189 }
190 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191}
192
193/*
194 * Do a signal return; undo the signal stack.
195 */
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800196#define loadsegment_gs(v) load_gs_index(v)
197#define loadsegment_fs(v) loadsegment(fs, v)
198#define loadsegment_ds(v) loadsegment(ds, v)
199#define loadsegment_es(v) loadsegment(es, v)
200
201#define get_user_seg(seg) ({ unsigned int v; savesegment(seg, v); v; })
202#define set_user_seg(seg, v) loadsegment_##seg(v)
203
Hiroshi Shimamotob78a5b52008-11-17 15:44:50 -0800204#define COPY(x) { \
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800205 get_user_ex(regs->x, &sc->x); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206}
207
Hiroshi Shimamoto8801ead2009-02-20 19:00:13 -0800208#define GET_SEG(seg) ({ \
209 unsigned short tmp; \
210 get_user_ex(tmp, &sc->seg); \
211 tmp; \
212})
213
214#define COPY_SEG_CPL3(seg) do { \
215 regs->seg = GET_SEG(seg) | 3; \
216} while (0)
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800217
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800218#define RELOAD_SEG(seg) { \
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800219 unsigned int pre = GET_SEG(seg); \
220 unsigned int cur = get_user_seg(seg); \
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800221 pre |= 3; \
222 if (pre != cur) \
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800223 set_user_seg(seg, pre); \
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800224}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100226static int ia32_restore_sigcontext(struct pt_regs *regs,
227 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800228 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100229{
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800230 unsigned int tmpflags, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700231 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100232 u32 tmp;
233
234 /* Always make any pending restarted system calls return -EINTR */
235 current_thread_info()->restart_block.fn = do_no_restart_syscall;
236
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800237 get_user_try {
238 /*
239 * Reload fs and gs if they have changed in the signal
240 * handler. This does not handle long fs/gs base changes in
241 * the handler, but does not clobber them at least in the
242 * normal case.
243 */
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800244 RELOAD_SEG(gs);
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800245 RELOAD_SEG(fs);
246 RELOAD_SEG(ds);
247 RELOAD_SEG(es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800249 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
250 COPY(dx); COPY(cx); COPY(ip);
251 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800253 COPY_SEG_CPL3(cs);
254 COPY_SEG_CPL3(ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800256 get_user_ex(tmpflags, &sc->flags);
257 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
258 /* disable syscall checks */
259 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100260
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800261 get_user_ex(tmp, &sc->fpstate);
262 buf = compat_ptr(tmp);
263 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800265 get_user_ex(*pax, &sc->ax);
266 } get_user_catch(err);
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
271asmlinkage long sys32_sigreturn(struct pt_regs *regs)
272{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800273 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100275 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
278 goto badframe;
279 if (__get_user(set.sig[0], &frame->sc.oldmask)
280 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100281 && __copy_from_user((((char *) &set.sig) + 4),
282 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 sizeof(frame->extramask))))
284 goto badframe;
285
286 sigdelsetmask(&set, ~_BLOCKABLE);
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200287 set_current_blocked(&set);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100288
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100289 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100291 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293badframe:
294 signal_fault(regs, frame, "32bit sigreturn");
295 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100296}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297
298asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
299{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800300 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100302 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 struct pt_regs tregs;
304
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800305 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
307 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
308 goto badframe;
309 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
310 goto badframe;
311
312 sigdelsetmask(&set, ~_BLOCKABLE);
Oleg Nesterov905f29e2011-07-10 21:27:24 +0200313 set_current_blocked(&set);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100314
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100315 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 goto badframe;
317
318 tregs = *regs;
319 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
320 goto badframe;
321
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100322 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
324badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100325 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100327}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
329/*
330 * Set up a signal frame.
331 */
332
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100333static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700334 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100335 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800337 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800339 put_user_try {
Hiroshi Shimamotoa967bb32009-02-20 19:00:54 -0800340 put_user_ex(get_user_seg(gs), (unsigned int __user *)&sc->gs);
341 put_user_ex(get_user_seg(fs), (unsigned int __user *)&sc->fs);
342 put_user_ex(get_user_seg(ds), (unsigned int __user *)&sc->ds);
343 put_user_ex(get_user_seg(es), (unsigned int __user *)&sc->es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800345 put_user_ex(regs->di, &sc->di);
346 put_user_ex(regs->si, &sc->si);
347 put_user_ex(regs->bp, &sc->bp);
348 put_user_ex(regs->sp, &sc->sp);
349 put_user_ex(regs->bx, &sc->bx);
350 put_user_ex(regs->dx, &sc->dx);
351 put_user_ex(regs->cx, &sc->cx);
352 put_user_ex(regs->ax, &sc->ax);
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530353 put_user_ex(current->thread.trap_nr, &sc->trapno);
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800354 put_user_ex(current->thread.error_code, &sc->err);
355 put_user_ex(regs->ip, &sc->ip);
356 put_user_ex(regs->cs, (unsigned int __user *)&sc->cs);
357 put_user_ex(regs->flags, &sc->flags);
358 put_user_ex(regs->sp, &sc->sp_at_signal);
359 put_user_ex(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800361 put_user_ex(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800363 /* non-iBCS2 extensions.. */
364 put_user_ex(mask, &sc->oldmask);
365 put_user_ex(current->thread.cr2, &sc->cr2);
366 } put_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
368 return err;
369}
370
371/*
372 * Determine which stack to use..
373 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100374static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700375 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700376 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100378 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
380 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100381 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
383 /* This is the X/Open sanctioned signal stack switching. */
384 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100385 if (sas_ss_flags(sp) == 0)
386 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 }
388
389 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800390 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100392 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100393 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700395 if (used_math()) {
396 sp = sp - sig_xstate_ia32_size;
397 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800398 if (save_i387_xstate_ia32(*fpstate) < 0)
399 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700400 }
401
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100402 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200403 /* Align the stack pointer according to the i386 ABI,
404 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100405 sp = ((sp + 4) & -16ul) - 4;
406 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
Roland McGrath0928d6e2005-06-23 00:08:37 -0700409int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100410 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800412 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100413 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700415 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100417 /* copy_to_user optimizes that into a single 8 byte store */
418 static const struct {
419 u16 poplmovl;
420 u32 val;
421 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100422 } __attribute__((packed)) code = {
423 0xb858, /* popl %eax ; movl $...,%eax */
424 __NR_ia32_sigreturn,
425 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100426 };
427
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700428 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
430 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700431 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700433 if (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700434 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700436 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700437 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700440 if (__copy_to_user(frame->extramask, &set->sig[1],
441 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700442 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Roland McGrathaf65d642008-01-30 13:30:43 +0100445 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100446 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100447 } else {
448 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700449 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100450 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
451 sigreturn);
452 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100453 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100454 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100455
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800456 put_user_try {
457 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
458
459 /*
460 * These are actually not used anymore, but left because some
461 * gdb versions depend on them as a marker.
462 */
463 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
464 } put_user_catch(err);
465
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700467 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468
469 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100470 regs->sp = (unsigned long) frame;
471 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Andi Kleen536e3ee2006-09-26 10:52:41 +0200473 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100474 regs->ax = sig;
475 regs->dx = 0;
476 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200477
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700478 loadsegment(ds, __USER32_DS);
479 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100481 regs->cs = __USER32_CS;
482 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Andi Kleen1d001df2006-09-26 10:52:26 +0200484 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485}
486
Roland McGrath0928d6e2005-06-23 00:08:37 -0700487int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100488 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800490 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100491 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700493 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100495 /* __copy_to_user optimizes that into a single 8 byte store */
496 static const struct {
497 u8 movl;
498 u32 val;
499 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800500 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100501 } __attribute__((packed)) code = {
502 0xb8,
503 __NR_ia32_rt_sigreturn,
504 0x80cd,
505 0,
506 };
507
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700508 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700511 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800513 put_user_try {
514 put_user_ex(sig, &frame->sig);
515 put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo);
516 put_user_ex(ptr_to_compat(&frame->uc), &frame->puc);
517 err |= copy_siginfo_to_user32(&frame->info, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800519 /* Create the ucontext. */
520 if (cpu_has_xsave)
521 put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
522 else
523 put_user_ex(0, &frame->uc.uc_flags);
524 put_user_ex(0, &frame->uc.uc_link);
525 put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
526 put_user_ex(sas_ss_flags(regs->sp),
527 &frame->uc.uc_stack.ss_flags);
528 put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
529 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
530 regs, set->sig[0]);
531 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800533 if (ka->sa.sa_flags & SA_RESTORER)
534 restorer = ka->sa.sa_restorer;
535 else
536 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
537 rt_sigreturn);
538 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800540 /*
541 * Not actually used anymore, but left because some gdb
542 * versions need it.
543 */
544 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
545 } put_user_catch(err);
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700548 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
550 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100551 regs->sp = (unsigned long) frame;
552 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500554 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100555 regs->ax = sig;
556 regs->dx = (unsigned long) &frame->info;
557 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500558
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700559 loadsegment(ds, __USER32_DS);
560 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100561
562 regs->cs = __USER32_CS;
563 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Andi Kleen1d001df2006-09-26 10:52:26 +0200565 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}