blob: 334a4aa2e75bd318ddaf642ee8c94a57aca4d5f4 [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
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800177struct sigframe_ia32
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178{
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
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800188struct rt_sigframe_ia32
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
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
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800204#define COPY_SEG_CPL3(seg) { \
205 unsigned short tmp; \
206 err |= __get_user(tmp, &sc->seg); \
207 regs->seg = tmp | 3; \
208}
209
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800210#define RELOAD_SEG(seg) { \
211 unsigned int cur, pre; \
212 err |= __get_user(pre, &sc->seg); \
213 savesegment(seg, cur); \
214 pre |= 3; \
215 if (pre != cur) \
216 loadsegment(seg, pre); \
217}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100219static int ia32_restore_sigcontext(struct pt_regs *regs,
220 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800221 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100222{
223 unsigned int tmpflags, gs, oldgs, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700224 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100225 u32 tmp;
226
227 /* Always make any pending restarted system calls return -EINTR */
228 current_thread_info()->restart_block.fn = do_no_restart_syscall;
229
230#if DEBUG_SIG
231 printk(KERN_DEBUG "SIG restore_sigcontext: "
232 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100233 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100234#endif
235
236 /*
237 * Reload fs and gs if they have changed in the signal
238 * handler. This does not handle long fs/gs base changes in
239 * the handler, but does not clobber them at least in the
240 * normal case.
241 */
242 err |= __get_user(gs, &sc->gs);
243 gs |= 3;
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700244 savesegment(gs, oldgs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100245 if (gs != oldgs)
246 load_gs_index(gs);
247
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800248 RELOAD_SEG(fs);
249 RELOAD_SEG(ds);
250 RELOAD_SEG(es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
252 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
253 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100254 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800256 COPY_SEG_CPL3(cs);
257 COPY_SEG_CPL3(ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
H. Peter Anvin742fa542008-01-30 13:30:56 +0100259 err |= __get_user(tmpflags, &sc->flags);
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -0700260 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100261 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100262 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100263
264 err |= __get_user(tmp, &sc->fpstate);
265 buf = compat_ptr(tmp);
Suresh Siddhaab513702008-07-29 10:29:22 -0700266 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800268 err |= __get_user(*pax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
272asmlinkage long sys32_sigreturn(struct pt_regs *regs)
273{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800274 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100276 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
278 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
279 goto badframe;
280 if (__get_user(set.sig[0], &frame->sc.oldmask)
281 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100282 && __copy_from_user((((char *) &set.sig) + 4),
283 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 sizeof(frame->extramask))))
285 goto badframe;
286
287 sigdelsetmask(&set, ~_BLOCKABLE);
288 spin_lock_irq(&current->sighand->siglock);
289 current->blocked = set;
290 recalc_sigpending();
291 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100292
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100293 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100295 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297badframe:
298 signal_fault(regs, frame, "32bit sigreturn");
299 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100300}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
302asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
303{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800304 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100306 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 struct pt_regs tregs;
308
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800309 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
312 goto badframe;
313 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
314 goto badframe;
315
316 sigdelsetmask(&set, ~_BLOCKABLE);
317 spin_lock_irq(&current->sighand->siglock);
318 current->blocked = set;
319 recalc_sigpending();
320 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100321
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100322 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 goto badframe;
324
325 tregs = *regs;
326 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
327 goto badframe;
328
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100329 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
331badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100332 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100334}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336/*
337 * Set up a signal frame.
338 */
339
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100340static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700341 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100342 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343{
344 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700346 savesegment(gs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700348 savesegment(fs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700350 savesegment(ds, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700352 savesegment(es, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
354
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700355 err |= __put_user(regs->di, &sc->di);
356 err |= __put_user(regs->si, &sc->si);
357 err |= __put_user(regs->bp, &sc->bp);
358 err |= __put_user(regs->sp, &sc->sp);
359 err |= __put_user(regs->bx, &sc->bx);
360 err |= __put_user(regs->dx, &sc->dx);
361 err |= __put_user(regs->cx, &sc->cx);
362 err |= __put_user(regs->ax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 err |= __put_user(current->thread.trap_no, &sc->trapno);
364 err |= __put_user(current->thread.error_code, &sc->err);
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700365 err |= __put_user(regs->ip, &sc->ip);
Hiroshi Shimamoto64977602008-11-17 15:49:14 -0800366 err |= __put_user(regs->cs, (unsigned int __user *)&sc->cs);
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700367 err |= __put_user(regs->flags, &sc->flags);
368 err |= __put_user(regs->sp, &sc->sp_at_signal);
Hiroshi Shimamoto64977602008-11-17 15:49:14 -0800369 err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800371 err |= __put_user(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373 /* non-iBCS2 extensions.. */
374 err |= __put_user(mask, &sc->oldmask);
375 err |= __put_user(current->thread.cr2, &sc->cr2);
376
377 return err;
378}
379
380/*
381 * Determine which stack to use..
382 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100383static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700384 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700385 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100387 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
389 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100390 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392 /* This is the X/Open sanctioned signal stack switching. */
393 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100394 if (sas_ss_flags(sp) == 0)
395 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 }
397
398 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800399 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100401 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100402 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700404 if (used_math()) {
405 sp = sp - sig_xstate_ia32_size;
406 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800407 if (save_i387_xstate_ia32(*fpstate) < 0)
408 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700409 }
410
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100411 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200412 /* Align the stack pointer according to the i386 ABI,
413 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100414 sp = ((sp + 4) & -16ul) - 4;
415 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
417
Roland McGrath0928d6e2005-06-23 00:08:37 -0700418int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100419 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800421 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100422 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700424 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100426 /* copy_to_user optimizes that into a single 8 byte store */
427 static const struct {
428 u16 poplmovl;
429 u32 val;
430 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100431 } __attribute__((packed)) code = {
432 0xb858, /* popl %eax ; movl $...,%eax */
433 __NR_ia32_sigreturn,
434 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100435 };
436
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700437 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
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 (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700443 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700445 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700446 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
448 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700449 if (__copy_to_user(frame->extramask, &set->sig[1],
450 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700451 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Roland McGrathaf65d642008-01-30 13:30:43 +0100454 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100455 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100456 } else {
457 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700458 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100459 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
460 sigreturn);
461 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100462 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100463 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100464 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
465
466 /*
467 * These are actually not used anymore, but left because some
468 * gdb versions depend on them as a marker.
469 */
Hiroshi Shimamotod0b48ca2008-12-16 14:03:36 -0800470 err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700472 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
474 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100475 regs->sp = (unsigned long) frame;
476 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
Andi Kleen536e3ee2006-09-26 10:52:41 +0200478 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100479 regs->ax = sig;
480 regs->dx = 0;
481 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200482
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700483 loadsegment(ds, __USER32_DS);
484 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100486 regs->cs = __USER32_CS;
487 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100490 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100491 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492#endif
493
Andi Kleen1d001df2006-09-26 10:52:26 +0200494 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495}
496
Roland McGrath0928d6e2005-06-23 00:08:37 -0700497int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100498 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800500 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100501 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700503 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100505 /* __copy_to_user optimizes that into a single 8 byte store */
506 static const struct {
507 u8 movl;
508 u32 val;
509 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800510 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100511 } __attribute__((packed)) code = {
512 0xb8,
513 __NR_ia32_rt_sigreturn,
514 0x80cd,
515 0,
516 };
517
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700518 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519
520 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700521 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
Hiroshi Shimamoto812b1212008-07-16 19:21:31 -0700523 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
525 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
526 err |= copy_siginfo_to_user32(&frame->info, info);
527 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700528 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
530 /* Create the ucontext. */
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700531 if (cpu_has_xsave)
532 err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
533 else
534 err |= __put_user(0, &frame->uc.uc_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 err |= __put_user(0, &frame->uc.uc_link);
536 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100537 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 &frame->uc.uc_stack.ss_flags);
539 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700540 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100541 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
543 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700544 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100546 if (ka->sa.sa_flags & SA_RESTORER)
547 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100548 else
549 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
550 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100551 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100553 /*
554 * Not actually used anymore, but left because some gdb
555 * versions need it.
556 */
Hiroshi Shimamotod0b48ca2008-12-16 14:03:36 -0800557 err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700559 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100562 regs->sp = (unsigned long) frame;
563 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500565 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100566 regs->ax = sig;
567 regs->dx = (unsigned long) &frame->info;
568 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500569
Albert Cahalan8e3de532006-12-07 02:14:06 +0100570 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100571 regs->ax = sig;
572 regs->dx = (unsigned long) &frame->info;
573 regs->cx = (unsigned long) &frame->uc;
Albert Cahalan8e3de532006-12-07 02:14:06 +0100574
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700575 loadsegment(ds, __USER32_DS);
576 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100577
578 regs->cs = __USER32_CS;
579 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100582 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100583 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584#endif
585
Andi Kleen1d001df2006-09-26 10:52:26 +0200586 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587}