blob: 3b3878a63bc2e120351de75b7c7ab5791b657c96 [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
Hiroshi Shimamotod98f9d82008-12-17 18:52:45 -080035#include <asm/sigframe.h>
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#define DEBUG_SIG 0
38
39#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
40
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -070041#define FIX_EFLAGS (X86_EFLAGS_AC | X86_EFLAGS_OF | \
42 X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
43 X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
44 X86_EFLAGS_CF)
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
47void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
48
49int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
50{
51 int err;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010052
53 if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 return -EFAULT;
55
56 /* If you change siginfo_t structure, please make sure that
57 this code is fixed accordingly.
58 It should never copy any pad contained in the structure
59 to avoid security leaks, but must copy the generic
60 3 ints plus the relevant union member. */
61 err = __put_user(from->si_signo, &to->si_signo);
62 err |= __put_user(from->si_errno, &to->si_errno);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010063 err |= __put_user((short)from->si_code, &to->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65 if (from->si_code < 0) {
66 err |= __put_user(from->si_pid, &to->si_pid);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010067 err |= __put_user(from->si_uid, &to->si_uid);
68 err |= __put_user(ptr_to_compat(from->si_ptr), &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 } else {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010070 /*
71 * First 32bits of unions are always present:
72 * si_pid === si_band === si_tid === si_addr(LS half)
73 */
74 err |= __put_user(from->_sifields._pad[0],
75 &to->_sifields._pad[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 switch (from->si_code >> 16) {
77 case __SI_FAULT >> 16:
78 break;
79 case __SI_CHLD >> 16:
80 err |= __put_user(from->si_utime, &to->si_utime);
81 err |= __put_user(from->si_stime, &to->si_stime);
82 err |= __put_user(from->si_status, &to->si_status);
83 /* FALL THROUGH */
84 default:
85 case __SI_KILL >> 16:
86 err |= __put_user(from->si_uid, &to->si_uid);
87 break;
88 case __SI_POLL >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010089 err |= __put_user(from->si_fd, &to->si_fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 break;
91 case __SI_TIMER >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010092 err |= __put_user(from->si_overrun, &to->si_overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 err |= __put_user(ptr_to_compat(from->si_ptr),
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010094 &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 break;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010096 /* This is not generated by the kernel as of now. */
97 case __SI_RT >> 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 case __SI_MESGQ >> 16:
99 err |= __put_user(from->si_uid, &to->si_uid);
100 err |= __put_user(from->si_int, &to->si_int);
101 break;
102 }
103 }
104 return err;
105}
106
107int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
108{
109 int err;
110 u32 ptr32;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100111
112 if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 return -EFAULT;
114
115 err = __get_user(to->si_signo, &from->si_signo);
116 err |= __get_user(to->si_errno, &from->si_errno);
117 err |= __get_user(to->si_code, &from->si_code);
118
119 err |= __get_user(to->si_pid, &from->si_pid);
120 err |= __get_user(to->si_uid, &from->si_uid);
121 err |= __get_user(ptr32, &from->si_ptr);
122 to->si_ptr = compat_ptr(ptr32);
123
124 return err;
125}
126
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100127asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 mask &= _BLOCKABLE;
130 spin_lock_irq(&current->sighand->siglock);
Andi Kleen1d001df2006-09-26 10:52:26 +0200131 current->saved_sigmask = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 siginitset(&current->blocked, mask);
133 recalc_sigpending();
134 spin_unlock_irq(&current->sighand->siglock);
135
Andi Kleen1d001df2006-09-26 10:52:26 +0200136 current->state = TASK_INTERRUPTIBLE;
137 schedule();
Roland McGrath5a8da0e2008-04-30 00:53:10 -0700138 set_restore_sigmask();
Andi Kleen1d001df2006-09-26 10:52:26 +0200139 return -ERESTARTNOHAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}
141
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100142asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
143 stack_ia32_t __user *uoss_ptr,
144 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100146 stack_t uss, uoss;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 int ret;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100148 mm_segment_t seg;
149
150 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100152
153 memset(&uss, 0, sizeof(stack_t));
154 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 __get_user(ptr, &uss_ptr->ss_sp) ||
156 __get_user(uss.ss_flags, &uss_ptr->ss_flags) ||
157 __get_user(uss.ss_size, &uss_ptr->ss_size))
158 return -EFAULT;
159 uss.ss_sp = compat_ptr(ptr);
160 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100161 seg = get_fs();
162 set_fs(KERNEL_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100163 ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100164 set_fs(seg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 if (ret >= 0 && uoss_ptr) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100166 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 __put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) ||
168 __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) ||
169 __put_user(uoss.ss_size, &uoss_ptr->ss_size))
170 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100171 }
172 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
175/*
176 * Do a signal return; undo the signal stack.
177 */
Hiroshi Shimamotob78a5b52008-11-17 15:44:50 -0800178#define COPY(x) { \
179 err |= __get_user(regs->x, &sc->x); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800182#define COPY_SEG_CPL3(seg) { \
183 unsigned short tmp; \
184 err |= __get_user(tmp, &sc->seg); \
185 regs->seg = tmp | 3; \
186}
187
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800188#define RELOAD_SEG(seg) { \
189 unsigned int cur, pre; \
190 err |= __get_user(pre, &sc->seg); \
191 savesegment(seg, cur); \
192 pre |= 3; \
193 if (pre != cur) \
194 loadsegment(seg, pre); \
195}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100197static int ia32_restore_sigcontext(struct pt_regs *regs,
198 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800199 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100200{
201 unsigned int tmpflags, gs, oldgs, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700202 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100203 u32 tmp;
204
205 /* Always make any pending restarted system calls return -EINTR */
206 current_thread_info()->restart_block.fn = do_no_restart_syscall;
207
208#if DEBUG_SIG
209 printk(KERN_DEBUG "SIG restore_sigcontext: "
210 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100211 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100212#endif
213
214 /*
215 * Reload fs and gs if they have changed in the signal
216 * handler. This does not handle long fs/gs base changes in
217 * the handler, but does not clobber them at least in the
218 * normal case.
219 */
220 err |= __get_user(gs, &sc->gs);
221 gs |= 3;
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700222 savesegment(gs, oldgs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100223 if (gs != oldgs)
224 load_gs_index(gs);
225
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800226 RELOAD_SEG(fs);
227 RELOAD_SEG(ds);
228 RELOAD_SEG(es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
230 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
231 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100232 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800234 COPY_SEG_CPL3(cs);
235 COPY_SEG_CPL3(ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
H. Peter Anvin742fa542008-01-30 13:30:56 +0100237 err |= __get_user(tmpflags, &sc->flags);
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -0700238 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100239 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100240 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100241
242 err |= __get_user(tmp, &sc->fpstate);
243 buf = compat_ptr(tmp);
Suresh Siddhaab513702008-07-29 10:29:22 -0700244 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800246 err |= __get_user(*pax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
250asmlinkage long sys32_sigreturn(struct pt_regs *regs)
251{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800252 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100254 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
256 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
257 goto badframe;
258 if (__get_user(set.sig[0], &frame->sc.oldmask)
259 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100260 && __copy_from_user((((char *) &set.sig) + 4),
261 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 sizeof(frame->extramask))))
263 goto badframe;
264
265 sigdelsetmask(&set, ~_BLOCKABLE);
266 spin_lock_irq(&current->sighand->siglock);
267 current->blocked = set;
268 recalc_sigpending();
269 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100270
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100271 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100273 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
275badframe:
276 signal_fault(regs, frame, "32bit sigreturn");
277 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100278}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
280asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
281{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800282 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100284 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 struct pt_regs tregs;
286
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800287 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
290 goto badframe;
291 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
292 goto badframe;
293
294 sigdelsetmask(&set, ~_BLOCKABLE);
295 spin_lock_irq(&current->sighand->siglock);
296 current->blocked = set;
297 recalc_sigpending();
298 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100299
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100300 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 goto badframe;
302
303 tregs = *regs;
304 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
305 goto badframe;
306
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100307 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100310 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100312}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
314/*
315 * Set up a signal frame.
316 */
317
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100318static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700319 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100320 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321{
322 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700324 savesegment(gs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700326 savesegment(fs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700328 savesegment(ds, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700330 savesegment(es, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
332
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700333 err |= __put_user(regs->di, &sc->di);
334 err |= __put_user(regs->si, &sc->si);
335 err |= __put_user(regs->bp, &sc->bp);
336 err |= __put_user(regs->sp, &sc->sp);
337 err |= __put_user(regs->bx, &sc->bx);
338 err |= __put_user(regs->dx, &sc->dx);
339 err |= __put_user(regs->cx, &sc->cx);
340 err |= __put_user(regs->ax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 err |= __put_user(current->thread.trap_no, &sc->trapno);
342 err |= __put_user(current->thread.error_code, &sc->err);
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700343 err |= __put_user(regs->ip, &sc->ip);
Hiroshi Shimamoto64977602008-11-17 15:49:14 -0800344 err |= __put_user(regs->cs, (unsigned int __user *)&sc->cs);
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700345 err |= __put_user(regs->flags, &sc->flags);
346 err |= __put_user(regs->sp, &sc->sp_at_signal);
Hiroshi Shimamoto64977602008-11-17 15:49:14 -0800347 err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800349 err |= __put_user(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 /* non-iBCS2 extensions.. */
352 err |= __put_user(mask, &sc->oldmask);
353 err |= __put_user(current->thread.cr2, &sc->cr2);
354
355 return err;
356}
357
358/*
359 * Determine which stack to use..
360 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100361static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700362 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700363 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100365 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100368 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
370 /* This is the X/Open sanctioned signal stack switching. */
371 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100372 if (sas_ss_flags(sp) == 0)
373 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 }
375
376 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800377 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100379 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100380 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700382 if (used_math()) {
383 sp = sp - sig_xstate_ia32_size;
384 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800385 if (save_i387_xstate_ia32(*fpstate) < 0)
386 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700387 }
388
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100389 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200390 /* Align the stack pointer according to the i386 ABI,
391 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100392 sp = ((sp + 4) & -16ul) - 4;
393 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394}
395
Roland McGrath0928d6e2005-06-23 00:08:37 -0700396int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100397 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800399 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100400 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700402 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100404 /* copy_to_user optimizes that into a single 8 byte store */
405 static const struct {
406 u16 poplmovl;
407 u32 val;
408 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100409 } __attribute__((packed)) code = {
410 0xb858, /* popl %eax ; movl $...,%eax */
411 __NR_ia32_sigreturn,
412 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100413 };
414
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700415 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416
417 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700418 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700420 if (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700421 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700423 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700424 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
426 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700427 if (__copy_to_user(frame->extramask, &set->sig[1],
428 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700429 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Roland McGrathaf65d642008-01-30 13:30:43 +0100432 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100433 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100434 } else {
435 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700436 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100437 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
438 sigreturn);
439 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100440 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100441 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100442 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
443
444 /*
445 * These are actually not used anymore, but left because some
446 * gdb versions depend on them as a marker.
447 */
Hiroshi Shimamotod0b48ca2008-12-16 14:03:36 -0800448 err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700450 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
452 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100453 regs->sp = (unsigned long) frame;
454 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
Andi Kleen536e3ee2006-09-26 10:52:41 +0200456 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100457 regs->ax = sig;
458 regs->dx = 0;
459 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200460
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700461 loadsegment(ds, __USER32_DS);
462 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100464 regs->cs = __USER32_CS;
465 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100468 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100469 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470#endif
471
Andi Kleen1d001df2006-09-26 10:52:26 +0200472 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473}
474
Roland McGrath0928d6e2005-06-23 00:08:37 -0700475int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100476 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800478 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100479 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700481 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100483 /* __copy_to_user optimizes that into a single 8 byte store */
484 static const struct {
485 u8 movl;
486 u32 val;
487 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800488 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100489 } __attribute__((packed)) code = {
490 0xb8,
491 __NR_ia32_rt_sigreturn,
492 0x80cd,
493 0,
494 };
495
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700496 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
498 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700499 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
Hiroshi Shimamoto812b1212008-07-16 19:21:31 -0700501 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
503 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
504 err |= copy_siginfo_to_user32(&frame->info, info);
505 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700506 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 /* Create the ucontext. */
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700509 if (cpu_has_xsave)
510 err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
511 else
512 err |= __put_user(0, &frame->uc.uc_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 err |= __put_user(0, &frame->uc.uc_link);
514 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100515 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 &frame->uc.uc_stack.ss_flags);
517 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700518 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100519 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
521 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700522 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100524 if (ka->sa.sa_flags & SA_RESTORER)
525 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100526 else
527 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
528 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100529 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100531 /*
532 * Not actually used anymore, but left because some gdb
533 * versions need it.
534 */
Hiroshi Shimamotod0b48ca2008-12-16 14:03:36 -0800535 err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700537 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100540 regs->sp = (unsigned long) frame;
541 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500543 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100544 regs->ax = sig;
545 regs->dx = (unsigned long) &frame->info;
546 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500547
Albert Cahalan8e3de532006-12-07 02:14:06 +0100548 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100549 regs->ax = sig;
550 regs->dx = (unsigned long) &frame->info;
551 regs->cx = (unsigned long) &frame->uc;
Albert Cahalan8e3de532006-12-07 02:14:06 +0100552
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700553 loadsegment(ds, __USER32_DS);
554 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100555
556 regs->cs = __USER32_CS;
557 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100560 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100561 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562#endif
563
Andi Kleen1d001df2006-09-26 10:52:26 +0200564 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}