blob: 9dabd00e98055c8d21526e6f86a63deced16a98b [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 DEBUG_SIG 0
37
38#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
39
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -070040#define FIX_EFLAGS (X86_EFLAGS_AC | X86_EFLAGS_OF | \
41 X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
42 X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
43 X86_EFLAGS_CF)
44
Linus Torvalds1da177e2005-04-16 15:20:36 -070045void 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 */
Hiroshi Shimamotob78a5b52008-11-17 15:44:50 -0800176#define COPY(x) { \
177 err |= __get_user(regs->x, &sc->x); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178}
179
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800180#define COPY_SEG_CPL3(seg) { \
181 unsigned short tmp; \
182 err |= __get_user(tmp, &sc->seg); \
183 regs->seg = tmp | 3; \
184}
185
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800186#define RELOAD_SEG(seg) { \
187 unsigned int cur, pre; \
188 err |= __get_user(pre, &sc->seg); \
189 savesegment(seg, cur); \
190 pre |= 3; \
191 if (pre != cur) \
192 loadsegment(seg, pre); \
193}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100195static int ia32_restore_sigcontext(struct pt_regs *regs,
196 struct sigcontext_ia32 __user *sc,
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800197 unsigned int *pax)
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100198{
199 unsigned int tmpflags, gs, oldgs, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700200 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100201 u32 tmp;
202
203 /* Always make any pending restarted system calls return -EINTR */
204 current_thread_info()->restart_block.fn = do_no_restart_syscall;
205
206#if DEBUG_SIG
207 printk(KERN_DEBUG "SIG restore_sigcontext: "
208 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100209 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100210#endif
211
212 /*
213 * Reload fs and gs if they have changed in the signal
214 * handler. This does not handle long fs/gs base changes in
215 * the handler, but does not clobber them at least in the
216 * normal case.
217 */
218 err |= __get_user(gs, &sc->gs);
219 gs |= 3;
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700220 savesegment(gs, oldgs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100221 if (gs != oldgs)
222 load_gs_index(gs);
223
Hiroshi Shimamoto8c6e5ce2008-11-17 15:47:48 -0800224 RELOAD_SEG(fs);
225 RELOAD_SEG(ds);
226 RELOAD_SEG(es);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
228 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
229 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100230 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Hiroshi Shimamotod71a68d2008-11-17 15:47:06 -0800232 COPY_SEG_CPL3(cs);
233 COPY_SEG_CPL3(ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
H. Peter Anvin742fa542008-01-30 13:30:56 +0100235 err |= __get_user(tmpflags, &sc->flags);
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -0700236 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100237 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100238 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100239
240 err |= __get_user(tmp, &sc->fpstate);
241 buf = compat_ptr(tmp);
Suresh Siddhaab513702008-07-29 10:29:22 -0700242 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
Hiroshi Shimamoto047ce932008-11-17 15:48:27 -0800244 err |= __get_user(*pax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246}
247
248asmlinkage long sys32_sigreturn(struct pt_regs *regs)
249{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800250 struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100252 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
254 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
255 goto badframe;
256 if (__get_user(set.sig[0], &frame->sc.oldmask)
257 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100258 && __copy_from_user((((char *) &set.sig) + 4),
259 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 sizeof(frame->extramask))))
261 goto badframe;
262
263 sigdelsetmask(&set, ~_BLOCKABLE);
264 spin_lock_irq(&current->sighand->siglock);
265 current->blocked = set;
266 recalc_sigpending();
267 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100268
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100269 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100271 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
273badframe:
274 signal_fault(regs, frame, "32bit sigreturn");
275 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100276}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
278asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
279{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800280 struct rt_sigframe_ia32 __user *frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100282 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 struct pt_regs tregs;
284
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800285 frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
287 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
288 goto badframe;
289 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
290 goto badframe;
291
292 sigdelsetmask(&set, ~_BLOCKABLE);
293 spin_lock_irq(&current->sighand->siglock);
294 current->blocked = set;
295 recalc_sigpending();
296 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100297
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100298 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 goto badframe;
300
301 tregs = *regs;
302 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
303 goto badframe;
304
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100305 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
307badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100308 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100310}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
312/*
313 * Set up a signal frame.
314 */
315
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100316static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700317 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100318 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
320 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700322 savesegment(gs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700324 savesegment(fs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700326 savesegment(ds, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700328 savesegment(es, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
330
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700331 err |= __put_user(regs->di, &sc->di);
332 err |= __put_user(regs->si, &sc->si);
333 err |= __put_user(regs->bp, &sc->bp);
334 err |= __put_user(regs->sp, &sc->sp);
335 err |= __put_user(regs->bx, &sc->bx);
336 err |= __put_user(regs->dx, &sc->dx);
337 err |= __put_user(regs->cx, &sc->cx);
338 err |= __put_user(regs->ax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 err |= __put_user(current->thread.trap_no, &sc->trapno);
340 err |= __put_user(current->thread.error_code, &sc->err);
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700341 err |= __put_user(regs->ip, &sc->ip);
Hiroshi Shimamoto64977602008-11-17 15:49:14 -0800342 err |= __put_user(regs->cs, (unsigned int __user *)&sc->cs);
Hiroshi Shimamotobff0aa42008-09-30 20:28:20 -0700343 err |= __put_user(regs->flags, &sc->flags);
344 err |= __put_user(regs->sp, &sc->sp_at_signal);
Hiroshi Shimamoto64977602008-11-17 15:49:14 -0800345 err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800347 err |= __put_user(ptr_to_compat(fpstate), &sc->fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349 /* non-iBCS2 extensions.. */
350 err |= __put_user(mask, &sc->oldmask);
351 err |= __put_user(current->thread.cr2, &sc->cr2);
352
353 return err;
354}
355
356/*
357 * Determine which stack to use..
358 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100359static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700360 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700361 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100363 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
365 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100366 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
368 /* This is the X/Open sanctioned signal stack switching. */
369 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100370 if (sas_ss_flags(sp) == 0)
371 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 }
373
374 /* This is the legacy signal stack switching. */
Hiroshi Shimamoto8bee3f02008-12-16 14:04:43 -0800375 else if ((regs->ss & 0xffff) != __USER32_DS &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100377 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100378 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700380 if (used_math()) {
381 sp = sp - sig_xstate_ia32_size;
382 *fpstate = (struct _fpstate_ia32 *) sp;
Hiroshi Shimamoto99ea1b932008-11-05 18:32:54 -0800383 if (save_i387_xstate_ia32(*fpstate) < 0)
384 return (void __user *) -1L;
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700385 }
386
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100387 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200388 /* Align the stack pointer according to the i386 ABI,
389 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100390 sp = ((sp + 4) & -16ul) - 4;
391 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392}
393
Roland McGrath0928d6e2005-06-23 00:08:37 -0700394int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100395 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800397 struct sigframe_ia32 __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100398 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700400 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100402 /* copy_to_user optimizes that into a single 8 byte store */
403 static const struct {
404 u16 poplmovl;
405 u32 val;
406 u16 int80;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100407 } __attribute__((packed)) code = {
408 0xb858, /* popl %eax ; movl $...,%eax */
409 __NR_ia32_sigreturn,
410 0x80cd, /* int $0x80 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100411 };
412
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700413 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
415 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700416 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700418 if (__put_user(sig, &frame->sig))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700419 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700421 if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700422 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
424 if (_COMPAT_NSIG_WORDS > 1) {
Hiroshi Shimamoto2ba48e12008-09-12 17:02:53 -0700425 if (__copy_to_user(frame->extramask, &set->sig[1],
426 sizeof(frame->extramask)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700427 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
Roland McGrathaf65d642008-01-30 13:30:43 +0100430 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100431 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100432 } else {
433 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700434 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100435 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
436 sigreturn);
437 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100438 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100439 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100440 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
441
442 /*
443 * These are actually not used anymore, but left because some
444 * gdb versions depend on them as a marker.
445 */
Hiroshi Shimamotod0b48ca2008-12-16 14:03:36 -0800446 err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700448 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
450 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100451 regs->sp = (unsigned long) frame;
452 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Andi Kleen536e3ee2006-09-26 10:52:41 +0200454 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100455 regs->ax = sig;
456 regs->dx = 0;
457 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200458
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700459 loadsegment(ds, __USER32_DS);
460 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100462 regs->cs = __USER32_CS;
463 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100466 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100467 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468#endif
469
Andi Kleen1d001df2006-09-26 10:52:26 +0200470 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
Roland McGrath0928d6e2005-06-23 00:08:37 -0700473int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100474 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475{
Hiroshi Shimamoto3b0d29e2008-12-17 18:51:46 -0800476 struct rt_sigframe_ia32 __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100477 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700479 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100481 /* __copy_to_user optimizes that into a single 8 byte store */
482 static const struct {
483 u8 movl;
484 u32 val;
485 u16 int80;
Hiroshi Shimamoto9cc3c492008-11-11 19:11:39 -0800486 u8 pad;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100487 } __attribute__((packed)) code = {
488 0xb8,
489 __NR_ia32_rt_sigreturn,
490 0x80cd,
491 0,
492 };
493
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700494 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
496 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700497 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Hiroshi Shimamoto812b1212008-07-16 19:21:31 -0700499 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
501 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
502 err |= copy_siginfo_to_user32(&frame->info, info);
503 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700504 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
506 /* Create the ucontext. */
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700507 if (cpu_has_xsave)
508 err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
509 else
510 err |= __put_user(0, &frame->uc.uc_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 err |= __put_user(0, &frame->uc.uc_link);
512 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100513 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 &frame->uc.uc_stack.ss_flags);
515 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700516 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100517 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
519 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700520 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100522 if (ka->sa.sa_flags & SA_RESTORER)
523 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100524 else
525 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
526 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100527 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100529 /*
530 * Not actually used anymore, but left because some gdb
531 * versions need it.
532 */
Hiroshi Shimamotod0b48ca2008-12-16 14:03:36 -0800533 err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700535 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100538 regs->sp = (unsigned long) frame;
539 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500541 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100542 regs->ax = sig;
543 regs->dx = (unsigned long) &frame->info;
544 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500545
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700546 loadsegment(ds, __USER32_DS);
547 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100548
549 regs->cs = __USER32_CS;
550 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100553 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100554 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555#endif
556
Andi Kleen1d001df2006-09-26 10:52:26 +0200557 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558}