blob: adc63f81cb8efcae0417db3c199d116f7828f20b [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 Shimamotod71a68d2008-11-17 15:47:06 -0800195#define COPY_SEG_CPL3(seg) { \
196 unsigned short tmp; \
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800197 get_user_ex(tmp, &sc->seg); \
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800198 regs->seg = tmp | 3; \
199}
200
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800201#define RELOAD_SEG(seg) { \
202 unsigned int cur, pre; \
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800203 get_user_ex(pre, &sc->seg); \
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800204 savesegment(seg, cur); \
205 pre |= 3; \
206 if (pre != cur) \
207 loadsegment(seg, pre); \
208}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100210static int ia32_restore_sigcontext(struct pt_regs *regs,
211 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800212 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100213{
214 unsigned int tmpflags, gs, oldgs, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700215 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100216 u32 tmp;
217
218 /* Always make any pending restarted system calls return -EINTR */
219 current_thread_info()->restart_block.fn = do_no_restart_syscall;
220
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800221 get_user_try {
222 /*
223 * Reload fs and gs if they have changed in the signal
224 * handler. This does not handle long fs/gs base changes in
225 * the handler, but does not clobber them at least in the
226 * normal case.
227 */
228 get_user_ex(gs, &sc->gs);
229 gs |= 3;
230 savesegment(gs, oldgs);
231 if (gs != oldgs)
232 load_gs_index(gs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100233
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);
276 spin_lock_irq(&current->sighand->siglock);
277 current->blocked = set;
278 recalc_sigpending();
279 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100280
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100281 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100283 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
285badframe:
286 signal_fault(regs, frame, "32bit sigreturn");
287 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100288}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
291{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800292 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100294 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 struct pt_regs tregs;
296
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800297 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
300 goto badframe;
301 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
302 goto badframe;
303
304 sigdelsetmask(&set, ~_BLOCKABLE);
305 spin_lock_irq(&current->sighand->siglock);
306 current->blocked = set;
307 recalc_sigpending();
308 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100309
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100310 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 goto badframe;
312
313 tregs = *regs;
314 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
315 goto badframe;
316
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100317 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100320 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100322}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
324/*
325 * Set up a signal frame.
326 */
327
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100328static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700329 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100330 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331{
332 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800334 put_user_try {
335 savesegment(gs, tmp);
336 put_user_ex(tmp, (unsigned int __user *)&sc->gs);
337 savesegment(fs, tmp);
338 put_user_ex(tmp, (unsigned int __user *)&sc->fs);
339 savesegment(ds, tmp);
340 put_user_ex(tmp, (unsigned int __user *)&sc->ds);
341 savesegment(es, tmp);
342 put_user_ex(tmp, (unsigned int __user *)&sc->es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800344 put_user_ex(regs->di, &sc->di);
345 put_user_ex(regs->si, &sc->si);
346 put_user_ex(regs->bp, &sc->bp);
347 put_user_ex(regs->sp, &sc->sp);
348 put_user_ex(regs->bx, &sc->bx);
349 put_user_ex(regs->dx, &sc->dx);
350 put_user_ex(regs->cx, &sc->cx);
351 put_user_ex(regs->ax, &sc->ax);
352 put_user_ex(current->thread.trap_no, &sc->trapno);
353 put_user_ex(current->thread.error_code, &sc->err);
354 put_user_ex(regs->ip, &sc->ip);
355 put_user_ex(regs->cs, (unsigned int __user *)&sc->cs);
356 put_user_ex(regs->flags, &sc->flags);
357 put_user_ex(regs->sp, &sc->sp_at_signal);
358 put_user_ex(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800360 put_user_ex(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800362 /* non-iBCS2 extensions.. */
363 put_user_ex(mask, &sc->oldmask);
364 put_user_ex(current->thread.cr2, &sc->cr2);
365 } put_user_catch(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367 return err;
368}
369
370/*
371 * Determine which stack to use..
372 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100373static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700374 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700375 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100377 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
379 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100380 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
382 /* This is the X/Open sanctioned signal stack switching. */
383 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100384 if (sas_ss_flags(sp) == 0)
385 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 }
387
388 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800389 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100391 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100392 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700394 if (used_math()) {
395 sp = sp - sig_xstate_ia32_size;
396 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800397 if (save_i387_xstate_ia32(*fpstate) < 0)
398 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700399 }
400
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100401 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200402 /* Align the stack pointer according to the i386 ABI,
403 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100404 sp = ((sp + 4) & -16ul) - 4;
405 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406}
407
Roland McGrath0928d6e2005-06-23 00:08:37 -0700408int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100409 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800411 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100412 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700414 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100416 /* copy_to_user optimizes that into a single 8 byte store */
417 static const struct {
418 u16 poplmovl;
419 u32 val;
420 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100421 } __attribute__((packed)) code = {
422 0xb858, /* popl %eax ; movl $...,%eax */
423 __NR_ia32_sigreturn,
424 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100425 };
426
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700427 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
429 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700430 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700432 if (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700433 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700435 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700436 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
438 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700439 if (__copy_to_user(frame->extramask, &set->sig[1],
440 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700441 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443
Roland McGrathaf65d642008-01-30 13:30:43 +0100444 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100445 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100446 } else {
447 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700448 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100449 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
450 sigreturn);
451 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100452 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100453 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100454
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800455 put_user_try {
456 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
457
458 /*
459 * These are actually not used anymore, but left because some
460 * gdb versions depend on them as a marker.
461 */
462 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
463 } put_user_catch(err);
464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700466 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
468 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100469 regs->sp = (unsigned long) frame;
470 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
Andi Kleen536e3ee2006-09-26 10:52:41 +0200472 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100473 regs->ax = sig;
474 regs->dx = 0;
475 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200476
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700477 loadsegment(ds, __USER32_DS);
478 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100480 regs->cs = __USER32_CS;
481 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Andi Kleen1d001df2006-09-26 10:52:26 +0200483 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
Roland McGrath0928d6e2005-06-23 00:08:37 -0700486int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100487 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800489 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100490 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700492 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100494 /* __copy_to_user optimizes that into a single 8 byte store */
495 static const struct {
496 u8 movl;
497 u32 val;
498 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800499 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100500 } __attribute__((packed)) code = {
501 0xb8,
502 __NR_ia32_rt_sigreturn,
503 0x80cd,
504 0,
505 };
506
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700507 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700510 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800512 put_user_try {
513 put_user_ex(sig, &frame->sig);
514 put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo);
515 put_user_ex(ptr_to_compat(&frame->uc), &frame->puc);
516 err |= copy_siginfo_to_user32(&frame->info, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800518 /* Create the ucontext. */
519 if (cpu_has_xsave)
520 put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
521 else
522 put_user_ex(0, &frame->uc.uc_flags);
523 put_user_ex(0, &frame->uc.uc_link);
524 put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
525 put_user_ex(sas_ss_flags(regs->sp),
526 &frame->uc.uc_stack.ss_flags);
527 put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
528 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
529 regs, set->sig[0]);
530 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800532 if (ka->sa.sa_flags & SA_RESTORER)
533 restorer = ka->sa.sa_restorer;
534 else
535 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
536 rt_sigreturn);
537 put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Hiroshi Shimamoto3b4b7572009-01-23 15:50:38 -0800539 /*
540 * Not actually used anymore, but left because some gdb
541 * versions need it.
542 */
543 put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
544 } put_user_catch(err);
545
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700547 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
549 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100550 regs->sp = (unsigned long) frame;
551 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500553 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100554 regs->ax = sig;
555 regs->dx = (unsigned long) &frame->info;
556 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500557
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700558 loadsegment(ds, __USER32_DS);
559 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100560
561 regs->cs = __USER32_CS;
562 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Andi Kleen1d001df2006-09-26 10:52:26 +0200564 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}