blob: 8dd0903da08bb20094703933e86001cb9744330c [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>
15#include <linux/signal.h>
16#include <linux/errno.h>
17#include <linux/wait.h>
18#include <linux/ptrace.h>
19#include <linux/unistd.h>
20#include <linux/stddef.h>
21#include <linux/personality.h>
22#include <linux/compat.h>
Andi Kleen9fbbd4d2007-02-13 13:26:26 +010023#include <linux/binfmts.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <asm/ucontext.h>
25#include <asm/uaccess.h>
26#include <asm/i387.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <asm/ptrace.h>
28#include <asm/ia32_unistd.h>
29#include <asm/user32.h>
30#include <asm/sigcontext32.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <asm/proto.h>
Roland McGrathaf65d642008-01-30 13:30:43 +010032#include <asm/vdso.h>
Hiroshi Shimamotod98f9d82008-12-17 18:52:45 -080033#include <asm/sigframe.h>
Jaswinder Singh Rajput2f06de02008-12-27 21:37:10 +053034#include <asm/sys_ia32.h>
Hiroshi Shimamotod98f9d82008-12-17 18:52:45 -080035
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
37
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -070038#define FIX_EFLAGS (X86_EFLAGS_AC | X86_EFLAGS_OF | \
39 X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
40 X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
41 X86_EFLAGS_CF)
42
Linus Torvalds1da177e2005-04-16 15:20:36 -070043void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
44
45int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
46{
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080047 int err = 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010048
49 if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 return -EFAULT;
51
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080052 put_user_try {
53 /* If you change siginfo_t structure, please make sure that
54 this code is fixed accordingly.
55 It should never copy any pad contained in the structure
56 to avoid security leaks, but must copy the generic
57 3 ints plus the relevant union member. */
58 put_user_ex(from->si_signo, &to->si_signo);
59 put_user_ex(from->si_errno, &to->si_errno);
60 put_user_ex((short)from->si_code, &to->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -080062 if (from->si_code < 0) {
63 put_user_ex(from->si_pid, &to->si_pid);
64 put_user_ex(from->si_uid, &to->si_uid);
65 put_user_ex(ptr_to_compat(from->si_ptr), &to->si_ptr);
66 } else {
67 /*
68 * First 32bits of unions are always present:
69 * si_pid === si_band === si_tid === si_addr(LS half)
70 */
71 put_user_ex(from->_sifields._pad[0],
72 &to->_sifields._pad[0]);
73 switch (from->si_code >> 16) {
74 case __SI_FAULT >> 16:
75 break;
76 case __SI_CHLD >> 16:
77 put_user_ex(from->si_utime, &to->si_utime);
78 put_user_ex(from->si_stime, &to->si_stime);
79 put_user_ex(from->si_status, &to->si_status);
80 /* FALL THROUGH */
81 default:
82 case __SI_KILL >> 16:
83 put_user_ex(from->si_uid, &to->si_uid);
84 break;
85 case __SI_POLL >> 16:
86 put_user_ex(from->si_fd, &to->si_fd);
87 break;
88 case __SI_TIMER >> 16:
89 put_user_ex(from->si_overrun, &to->si_overrun);
90 put_user_ex(ptr_to_compat(from->si_ptr),
91 &to->si_ptr);
92 break;
93 /* This is not generated by the kernel as of now. */
94 case __SI_RT >> 16:
95 case __SI_MESGQ >> 16:
96 put_user_ex(from->si_uid, &to->si_uid);
97 put_user_ex(from->si_int, &to->si_int);
98 break;
99 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 }
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800101 } put_user_catch(err);
102
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 return err;
104}
105
106int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
107{
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800108 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 u32 ptr32;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100110
111 if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 return -EFAULT;
113
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800114 get_user_try {
115 get_user_ex(to->si_signo, &from->si_signo);
116 get_user_ex(to->si_errno, &from->si_errno);
117 get_user_ex(to->si_code, &from->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800119 get_user_ex(to->si_pid, &from->si_pid);
120 get_user_ex(to->si_uid, &from->si_uid);
121 get_user_ex(ptr32, &from->si_ptr);
122 to->si_ptr = compat_ptr(ptr32);
123 } get_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
125 return err;
126}
127
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100128asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 mask &= _BLOCKABLE;
131 spin_lock_irq(&current->sighand->siglock);
Andi Kleen1d001df2006-09-26 10:52:26 +0200132 current->saved_sigmask = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 siginitset(&current->blocked, mask);
134 recalc_sigpending();
135 spin_unlock_irq(&current->sighand->siglock);
136
Andi Kleen1d001df2006-09-26 10:52:26 +0200137 current->state = TASK_INTERRUPTIBLE;
138 schedule();
Roland McGrath5a8da0e2008-04-30 00:53:10 -0700139 set_restore_sigmask();
Andi Kleen1d001df2006-09-26 10:52:26 +0200140 return -ERESTARTNOHAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141}
142
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100143asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
144 stack_ia32_t __user *uoss_ptr,
145 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100147 stack_t uss, uoss;
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800148 int ret, err = 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100149 mm_segment_t seg;
150
151 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100153
154 memset(&uss, 0, sizeof(stack_t));
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800155 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)))
156 return -EFAULT;
157
158 get_user_try {
159 get_user_ex(ptr, &uss_ptr->ss_sp);
160 get_user_ex(uss.ss_flags, &uss_ptr->ss_flags);
161 get_user_ex(uss.ss_size, &uss_ptr->ss_size);
162 } get_user_catch(err);
163
164 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 return -EFAULT;
166 uss.ss_sp = compat_ptr(ptr);
167 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100168 seg = get_fs();
169 set_fs(KERNEL_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100170 ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100171 set_fs(seg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 if (ret >= 0 && uoss_ptr) {
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800173 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)))
174 return -EFAULT;
175
176 put_user_try {
177 put_user_ex(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp);
178 put_user_ex(uoss.ss_flags, &uoss_ptr->ss_flags);
179 put_user_ex(uoss.ss_size, &uoss_ptr->ss_size);
180 } put_user_catch(err);
181
182 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100184 }
185 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186}
187
188/*
189 * Do a signal return; undo the signal stack.
190 */
Hiroshi Shimamotob78a5b52008-11-17 15:44:50 -0800191#define COPY(x) { \
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800192 get_user_ex(regs->x, &sc->x); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193}
194
Hiroshi Shimamoto8801ead2009-02-20 19:00:13 -0800195#define GET_SEG(seg) ({ \
196 unsigned short tmp; \
197 get_user_ex(tmp, &sc->seg); \
198 tmp; \
199})
200
201#define COPY_SEG_CPL3(seg) do { \
202 regs->seg = GET_SEG(seg) | 3; \
203} while (0)
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800204
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800205#define RELOAD_SEG(seg) { \
206 unsigned int cur, pre; \
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800207 get_user_ex(pre, &sc->seg); \
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800208 savesegment(seg, cur); \
209 pre |= 3; \
210 if (pre != cur) \
211 loadsegment(seg, pre); \
212}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100214static int ia32_restore_sigcontext(struct pt_regs *regs,
215 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800216 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100217{
218 unsigned int tmpflags, gs, oldgs, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700219 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100220 u32 tmp;
221
222 /* Always make any pending restarted system calls return -EINTR */
223 current_thread_info()->restart_block.fn = do_no_restart_syscall;
224
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800225 get_user_try {
226 /*
227 * Reload fs and gs if they have changed in the signal
228 * handler. This does not handle long fs/gs base changes in
229 * the handler, but does not clobber them at least in the
230 * normal case.
231 */
232 get_user_ex(gs, &sc->gs);
233 gs |= 3;
234 savesegment(gs, oldgs);
235 if (gs != oldgs)
236 load_gs_index(gs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100237
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800238 RELOAD_SEG(fs);
239 RELOAD_SEG(ds);
240 RELOAD_SEG(es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800242 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
243 COPY(dx); COPY(cx); COPY(ip);
244 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800246 COPY_SEG_CPL3(cs);
247 COPY_SEG_CPL3(ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800249 get_user_ex(tmpflags, &sc->flags);
250 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
251 /* disable syscall checks */
252 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100253
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800254 get_user_ex(tmp, &sc->fpstate);
255 buf = compat_ptr(tmp);
256 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800258 get_user_ex(*pax, &sc->ax);
259 } get_user_catch(err);
260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262}
263
264asmlinkage long sys32_sigreturn(struct pt_regs *regs)
265{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800266 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100268 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
270 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
271 goto badframe;
272 if (__get_user(set.sig[0], &frame->sc.oldmask)
273 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100274 && __copy_from_user((((char *) &set.sig) + 4),
275 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 sizeof(frame->extramask))))
277 goto badframe;
278
279 sigdelsetmask(&set, ~_BLOCKABLE);
280 spin_lock_irq(&current->sighand->siglock);
281 current->blocked = set;
282 recalc_sigpending();
283 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100284
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100285 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100287 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289badframe:
290 signal_fault(regs, frame, "32bit sigreturn");
291 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100292}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
295{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800296 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100298 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 struct pt_regs tregs;
300
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800301 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
303 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
304 goto badframe;
305 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
306 goto badframe;
307
308 sigdelsetmask(&set, ~_BLOCKABLE);
309 spin_lock_irq(&current->sighand->siglock);
310 current->blocked = set;
311 recalc_sigpending();
312 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100313
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100314 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 goto badframe;
316
317 tregs = *regs;
318 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
319 goto badframe;
320
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100321 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100324 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100326}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328/*
329 * Set up a signal frame.
330 */
331
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100332static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700333 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100334 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335{
336 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800338 put_user_try {
339 savesegment(gs, tmp);
340 put_user_ex(tmp, (unsigned int __user *)&sc->gs);
341 savesegment(fs, tmp);
342 put_user_ex(tmp, (unsigned int __user *)&sc->fs);
343 savesegment(ds, tmp);
344 put_user_ex(tmp, (unsigned int __user *)&sc->ds);
345 savesegment(es, tmp);
346 put_user_ex(tmp, (unsigned int __user *)&sc->es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800348 put_user_ex(regs->di, &sc->di);
349 put_user_ex(regs->si, &sc->si);
350 put_user_ex(regs->bp, &sc->bp);
351 put_user_ex(regs->sp, &sc->sp);
352 put_user_ex(regs->bx, &sc->bx);
353 put_user_ex(regs->dx, &sc->dx);
354 put_user_ex(regs->cx, &sc->cx);
355 put_user_ex(regs->ax, &sc->ax);
356 put_user_ex(current->thread.trap_no, &sc->trapno);
357 put_user_ex(current->thread.error_code, &sc->err);
358 put_user_ex(regs->ip, &sc->ip);
359 put_user_ex(regs->cs, (unsigned int __user *)&sc->cs);
360 put_user_ex(regs->flags, &sc->flags);
361 put_user_ex(regs->sp, &sc->sp_at_signal);
362 put_user_ex(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800364 put_user_ex(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800366 /* non-iBCS2 extensions.. */
367 put_user_ex(mask, &sc->oldmask);
368 put_user_ex(current->thread.cr2, &sc->cr2);
369 } put_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
371 return err;
372}
373
374/*
375 * Determine which stack to use..
376 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100377static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700378 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700379 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100381 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
383 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100384 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
386 /* This is the X/Open sanctioned signal stack switching. */
387 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100388 if (sas_ss_flags(sp) == 0)
389 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 }
391
392 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800393 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100395 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100396 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700398 if (used_math()) {
399 sp = sp - sig_xstate_ia32_size;
400 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800401 if (save_i387_xstate_ia32(*fpstate) < 0)
402 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700403 }
404
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100405 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200406 /* Align the stack pointer according to the i386 ABI,
407 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100408 sp = ((sp + 4) & -16ul) - 4;
409 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410}
411
Roland McGrath0928d6e2005-06-23 00:08:37 -0700412int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100413 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800415 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100416 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700418 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100420 /* copy_to_user optimizes that into a single 8 byte store */
421 static const struct {
422 u16 poplmovl;
423 u32 val;
424 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100425 } __attribute__((packed)) code = {
426 0xb858, /* popl %eax ; movl $...,%eax */
427 __NR_ia32_sigreturn,
428 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100429 };
430
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700431 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
433 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
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 (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700437 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700439 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700440 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700443 if (__copy_to_user(frame->extramask, &set->sig[1],
444 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700445 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
Roland McGrathaf65d642008-01-30 13:30:43 +0100448 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100449 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100450 } else {
451 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700452 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100453 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
454 sigreturn);
455 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100456 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100457 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100458
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800459 put_user_try {
460 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
461
462 /*
463 * These are actually not used anymore, but left because some
464 * gdb versions depend on them as a marker.
465 */
466 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
467 } put_user_catch(err);
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700470 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
472 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100473 regs->sp = (unsigned long) frame;
474 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475
Andi Kleen536e3ee2006-09-26 10:52:41 +0200476 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100477 regs->ax = sig;
478 regs->dx = 0;
479 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200480
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700481 loadsegment(ds, __USER32_DS);
482 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100484 regs->cs = __USER32_CS;
485 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
Andi Kleen1d001df2006-09-26 10:52:26 +0200487 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489
Roland McGrath0928d6e2005-06-23 00:08:37 -0700490int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100491 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800493 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100494 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700496 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100498 /* __copy_to_user optimizes that into a single 8 byte store */
499 static const struct {
500 u8 movl;
501 u32 val;
502 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800503 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100504 } __attribute__((packed)) code = {
505 0xb8,
506 __NR_ia32_rt_sigreturn,
507 0x80cd,
508 0,
509 };
510
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700511 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700514 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800516 put_user_try {
517 put_user_ex(sig, &frame->sig);
518 put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo);
519 put_user_ex(ptr_to_compat(&frame->uc), &frame->puc);
520 err |= copy_siginfo_to_user32(&frame->info, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800522 /* Create the ucontext. */
523 if (cpu_has_xsave)
524 put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
525 else
526 put_user_ex(0, &frame->uc.uc_flags);
527 put_user_ex(0, &frame->uc.uc_link);
528 put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
529 put_user_ex(sas_ss_flags(regs->sp),
530 &frame->uc.uc_stack.ss_flags);
531 put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
532 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
533 regs, set->sig[0]);
534 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800536 if (ka->sa.sa_flags & SA_RESTORER)
537 restorer = ka->sa.sa_restorer;
538 else
539 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
540 rt_sigreturn);
541 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800543 /*
544 * Not actually used anymore, but left because some gdb
545 * versions need it.
546 */
547 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
548 } put_user_catch(err);
549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700551 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
553 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100554 regs->sp = (unsigned long) frame;
555 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500557 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100558 regs->ax = sig;
559 regs->dx = (unsigned long) &frame->info;
560 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500561
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700562 loadsegment(ds, __USER32_DS);
563 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100564
565 regs->cs = __USER32_CS;
566 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Andi Kleen1d001df2006-09-26 10:52:26 +0200568 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}