blob: 610a17774ea2828b527c2221bf2e2c3291bbe521 [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>
27#include <asm/ia32.h>
28#include <asm/ptrace.h>
29#include <asm/ia32_unistd.h>
30#include <asm/user32.h>
31#include <asm/sigcontext32.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <asm/proto.h>
Roland McGrathaf65d642008-01-30 13:30:43 +010033#include <asm/vdso.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
35#define DEBUG_SIG 0
36
37#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
38
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -070039#define FIX_EFLAGS (X86_EFLAGS_AC | X86_EFLAGS_OF | \
40 X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
41 X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
42 X86_EFLAGS_CF)
43
Linus Torvalds1da177e2005-04-16 15:20:36 -070044asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
45void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
46
47int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
48{
49 int err;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010050
51 if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 return -EFAULT;
53
54 /* If you change siginfo_t structure, please make sure that
55 this code is fixed accordingly.
56 It should never copy any pad contained in the structure
57 to avoid security leaks, but must copy the generic
58 3 ints plus the relevant union member. */
59 err = __put_user(from->si_signo, &to->si_signo);
60 err |= __put_user(from->si_errno, &to->si_errno);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010061 err |= __put_user((short)from->si_code, &to->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
63 if (from->si_code < 0) {
64 err |= __put_user(from->si_pid, &to->si_pid);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010065 err |= __put_user(from->si_uid, &to->si_uid);
66 err |= __put_user(ptr_to_compat(from->si_ptr), &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 } else {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010068 /*
69 * First 32bits of unions are always present:
70 * si_pid === si_band === si_tid === si_addr(LS half)
71 */
72 err |= __put_user(from->_sifields._pad[0],
73 &to->_sifields._pad[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 switch (from->si_code >> 16) {
75 case __SI_FAULT >> 16:
76 break;
77 case __SI_CHLD >> 16:
78 err |= __put_user(from->si_utime, &to->si_utime);
79 err |= __put_user(from->si_stime, &to->si_stime);
80 err |= __put_user(from->si_status, &to->si_status);
81 /* FALL THROUGH */
82 default:
83 case __SI_KILL >> 16:
84 err |= __put_user(from->si_uid, &to->si_uid);
85 break;
86 case __SI_POLL >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010087 err |= __put_user(from->si_fd, &to->si_fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 break;
89 case __SI_TIMER >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010090 err |= __put_user(from->si_overrun, &to->si_overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 err |= __put_user(ptr_to_compat(from->si_ptr),
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010092 &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 break;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010094 /* This is not generated by the kernel as of now. */
95 case __SI_RT >> 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 case __SI_MESGQ >> 16:
97 err |= __put_user(from->si_uid, &to->si_uid);
98 err |= __put_user(from->si_int, &to->si_int);
99 break;
100 }
101 }
102 return err;
103}
104
105int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
106{
107 int err;
108 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
113 err = __get_user(to->si_signo, &from->si_signo);
114 err |= __get_user(to->si_errno, &from->si_errno);
115 err |= __get_user(to->si_code, &from->si_code);
116
117 err |= __get_user(to->si_pid, &from->si_pid);
118 err |= __get_user(to->si_uid, &from->si_uid);
119 err |= __get_user(ptr32, &from->si_ptr);
120 to->si_ptr = compat_ptr(ptr32);
121
122 return err;
123}
124
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100125asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 mask &= _BLOCKABLE;
128 spin_lock_irq(&current->sighand->siglock);
Andi Kleen1d001df2006-09-26 10:52:26 +0200129 current->saved_sigmask = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 siginitset(&current->blocked, mask);
131 recalc_sigpending();
132 spin_unlock_irq(&current->sighand->siglock);
133
Andi Kleen1d001df2006-09-26 10:52:26 +0200134 current->state = TASK_INTERRUPTIBLE;
135 schedule();
Roland McGrath5a8da0e2008-04-30 00:53:10 -0700136 set_restore_sigmask();
Andi Kleen1d001df2006-09-26 10:52:26 +0200137 return -ERESTARTNOHAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138}
139
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100140asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
141 stack_ia32_t __user *uoss_ptr,
142 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100144 stack_t uss, uoss;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 int ret;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100146 mm_segment_t seg;
147
148 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100150
151 memset(&uss, 0, sizeof(stack_t));
152 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 __get_user(ptr, &uss_ptr->ss_sp) ||
154 __get_user(uss.ss_flags, &uss_ptr->ss_flags) ||
155 __get_user(uss.ss_size, &uss_ptr->ss_size))
156 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) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100164 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 __put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) ||
166 __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) ||
167 __put_user(uoss.ss_size, &uoss_ptr->ss_size))
168 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100169 }
170 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171}
172
173/*
174 * Do a signal return; undo the signal stack.
175 */
176
177struct sigframe
178{
179 u32 pretcode;
180 int sig;
181 struct sigcontext_ia32 sc;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700182 struct _fpstate_ia32 fpstate_unused; /* look at kernel/sigframe.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 unsigned int extramask[_COMPAT_NSIG_WORDS-1];
184 char retcode[8];
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700185 /* fp state follows here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186};
187
188struct rt_sigframe
189{
190 u32 pretcode;
191 int sig;
192 u32 pinfo;
193 u32 puc;
194 compat_siginfo_t info;
195 struct ucontext_ia32 uc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 char retcode[8];
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700197 /* fp state follows here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198};
199
Hiroshi Shimamotob78a5b52008-11-17 15:44:50 -0800200#define COPY(x) { \
201 err |= __get_user(regs->x, &sc->x); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202}
203
204#define RELOAD_SEG(seg,mask) \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100205 { unsigned int cur; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 unsigned short pre; \
207 err |= __get_user(pre, &sc->seg); \
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700208 savesegment(seg, cur); \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100209 pre |= mask; \
210 if (pre != cur) loadsegment(seg, pre); }
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,
214 unsigned int *peax)
215{
216 unsigned int tmpflags, gs, oldgs, 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
223#if DEBUG_SIG
224 printk(KERN_DEBUG "SIG restore_sigcontext: "
225 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100226 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100227#endif
228
229 /*
230 * Reload fs and gs if they have changed in the signal
231 * handler. This does not handle long fs/gs base changes in
232 * the handler, but does not clobber them at least in the
233 * normal case.
234 */
235 err |= __get_user(gs, &sc->gs);
236 gs |= 3;
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700237 savesegment(gs, oldgs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100238 if (gs != oldgs)
239 load_gs_index(gs);
240
241 RELOAD_SEG(fs, 3);
242 RELOAD_SEG(ds, 3);
243 RELOAD_SEG(es, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
245 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
246 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100247 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100249 err |= __get_user(regs->cs, &sc->cs);
250 regs->cs |= 3;
251 err |= __get_user(regs->ss, &sc->ss);
252 regs->ss |= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
H. Peter Anvin742fa542008-01-30 13:30:56 +0100254 err |= __get_user(tmpflags, &sc->flags);
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -0700255 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100256 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100257 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100258
259 err |= __get_user(tmp, &sc->fpstate);
260 buf = compat_ptr(tmp);
Suresh Siddhaab513702008-07-29 10:29:22 -0700261 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
H. Peter Anvin742fa542008-01-30 13:30:56 +0100263 err |= __get_user(tmp, &sc->ax);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100264 *peax = tmp;
265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267}
268
269asmlinkage long sys32_sigreturn(struct pt_regs *regs)
270{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100271 struct sigframe __user *frame = (struct sigframe __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100273 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
275 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
276 goto badframe;
277 if (__get_user(set.sig[0], &frame->sc.oldmask)
278 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100279 && __copy_from_user((((char *) &set.sig) + 4),
280 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 sizeof(frame->extramask))))
282 goto badframe;
283
284 sigdelsetmask(&set, ~_BLOCKABLE);
285 spin_lock_irq(&current->sighand->siglock);
286 current->blocked = set;
287 recalc_sigpending();
288 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100289
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100290 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100292 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294badframe:
295 signal_fault(regs, frame, "32bit sigreturn");
296 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100297}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
300{
301 struct rt_sigframe __user *frame;
302 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100303 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 struct pt_regs tregs;
305
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100306 frame = (struct rt_sigframe __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
308 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
309 goto badframe;
310 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
311 goto badframe;
312
313 sigdelsetmask(&set, ~_BLOCKABLE);
314 spin_lock_irq(&current->sighand->siglock);
315 current->blocked = set;
316 recalc_sigpending();
317 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100318
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100319 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 goto badframe;
321
322 tregs = *regs;
323 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
324 goto badframe;
325
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100326 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100329 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100331}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333/*
334 * Set up a signal frame.
335 */
336
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100337static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700338 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100339 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
341 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700343 savesegment(gs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700345 savesegment(fs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700347 savesegment(ds, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700349 savesegment(es, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
351
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700352 err |= __put_user(regs->di, &sc->di);
353 err |= __put_user(regs->si, &sc->si);
354 err |= __put_user(regs->bp, &sc->bp);
355 err |= __put_user(regs->sp, &sc->sp);
356 err |= __put_user(regs->bx, &sc->bx);
357 err |= __put_user(regs->dx, &sc->dx);
358 err |= __put_user(regs->cx, &sc->cx);
359 err |= __put_user(regs->ax, &sc->ax);
360 err |= __put_user(regs->cs, &sc->cs);
361 err |= __put_user(regs->ss, &sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 err |= __put_user(current->thread.trap_no, &sc->trapno);
363 err |= __put_user(current->thread.error_code, &sc->err);
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700364 err |= __put_user(regs->ip, &sc->ip);
365 err |= __put_user(regs->flags, &sc->flags);
366 err |= __put_user(regs->sp, &sc->sp_at_signal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800368 err |= __put_user(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
370 /* non-iBCS2 extensions.. */
371 err |= __put_user(mask, &sc->oldmask);
372 err |= __put_user(current->thread.cr2, &sc->cr2);
373
374 return err;
375}
376
377/*
378 * Determine which stack to use..
379 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100380static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700381 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700382 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100384 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
386 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100387 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
389 /* This is the X/Open sanctioned signal stack switching. */
390 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100391 if (sas_ss_flags(sp) == 0)
392 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 }
394
395 /* This is the legacy signal stack switching. */
396 else if ((regs->ss & 0xffff) != __USER_DS &&
397 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100398 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100399 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700401 if (used_math()) {
402 sp = sp - sig_xstate_ia32_size;
403 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800404 if (save_i387_xstate_ia32(*fpstate) < 0)
405 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700406 }
407
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100408 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200409 /* Align the stack pointer according to the i386 ABI,
410 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100411 sp = ((sp + 4) & -16ul) - 4;
412 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413}
414
Roland McGrath0928d6e2005-06-23 00:08:37 -0700415int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100416 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
418 struct sigframe __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100419 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700421 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100423 /* copy_to_user optimizes that into a single 8 byte store */
424 static const struct {
425 u16 poplmovl;
426 u32 val;
427 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100428 } __attribute__((packed)) code = {
429 0xb858, /* popl %eax ; movl $...,%eax */
430 __NR_ia32_sigreturn,
431 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100432 };
433
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700434 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
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 (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700440 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700442 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700443 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
445 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700446 if (__copy_to_user(frame->extramask, &set->sig[1],
447 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700448 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
Roland McGrathaf65d642008-01-30 13:30:43 +0100451 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100452 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100453 } else {
454 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700455 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100456 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
457 sigreturn);
458 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100459 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100460 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100461 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
462
463 /*
464 * These are actually not used anymore, but left because some
465 * gdb versions depend on them as a marker.
466 */
467 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700469 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100472 regs->sp = (unsigned long) frame;
473 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474
Andi Kleen536e3ee2006-09-26 10:52:41 +0200475 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100476 regs->ax = sig;
477 regs->dx = 0;
478 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200479
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700480 loadsegment(ds, __USER32_DS);
481 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100483 regs->cs = __USER32_CS;
484 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100487 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100488 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489#endif
490
Andi Kleen1d001df2006-09-26 10:52:26 +0200491 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492}
493
Roland McGrath0928d6e2005-06-23 00:08:37 -0700494int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100495 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
497 struct rt_sigframe __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100498 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700500 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100502 /* __copy_to_user optimizes that into a single 8 byte store */
503 static const struct {
504 u8 movl;
505 u32 val;
506 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800507 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100508 } __attribute__((packed)) code = {
509 0xb8,
510 __NR_ia32_rt_sigreturn,
511 0x80cd,
512 0,
513 };
514
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700515 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
517 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700518 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519
Hiroshi Shimamoto812b1212008-07-16 19:21:31 -0700520 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
522 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
523 err |= copy_siginfo_to_user32(&frame->info, info);
524 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700525 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
527 /* Create the ucontext. */
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700528 if (cpu_has_xsave)
529 err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
530 else
531 err |= __put_user(0, &frame->uc.uc_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 err |= __put_user(0, &frame->uc.uc_link);
533 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100534 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 &frame->uc.uc_stack.ss_flags);
536 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700537 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100538 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
540 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700541 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100543 if (ka->sa.sa_flags & SA_RESTORER)
544 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100545 else
546 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
547 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100548 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100550 /*
551 * Not actually used anymore, but left because some gdb
552 * versions need it.
553 */
554 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700556 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
558 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100559 regs->sp = (unsigned long) frame;
560 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500562 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100563 regs->ax = sig;
564 regs->dx = (unsigned long) &frame->info;
565 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500566
Albert Cahalan8e3de532006-12-07 02:14:06 +0100567 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100568 regs->ax = sig;
569 regs->dx = (unsigned long) &frame->info;
570 regs->cx = (unsigned long) &frame->uc;
Albert Cahalan8e3de532006-12-07 02:14:06 +0100571
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700572 loadsegment(ds, __USER32_DS);
573 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100574
575 regs->cs = __USER32_CS;
576 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100579 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100580 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581#endif
582
Andi Kleen1d001df2006-09-26 10:52:26 +0200583 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584}