blob: 5f42cfcc1c5ad512f3176ca1183df05de8cfd748 [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
H. Peter Anvin742fa542008-01-30 13:30:56 +0100200#define COPY(x) { \
201 unsigned int reg; \
202 err |= __get_user(reg, &sc->x); \
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100203 regs->x = reg; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204}
205
206#define RELOAD_SEG(seg,mask) \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100207 { unsigned int cur; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 unsigned short pre; \
209 err |= __get_user(pre, &sc->seg); \
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700210 savesegment(seg, cur); \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100211 pre |= mask; \
212 if (pre != cur) loadsegment(seg, pre); }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100214static int ia32_restore_sigcontext(struct pt_regs *regs,
215 struct sigcontext_ia32 __user *sc,
216 unsigned int *peax)
217{
218 unsigned int tmpflags, gs, oldgs, err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700219 void __user *buf;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100220 u32 tmp;
221
222 /* Always make any pending restarted system calls return -EINTR */
223 current_thread_info()->restart_block.fn = do_no_restart_syscall;
224
225#if DEBUG_SIG
226 printk(KERN_DEBUG "SIG restore_sigcontext: "
227 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100228 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100229#endif
230
231 /*
232 * Reload fs and gs if they have changed in the signal
233 * handler. This does not handle long fs/gs base changes in
234 * the handler, but does not clobber them at least in the
235 * normal case.
236 */
237 err |= __get_user(gs, &sc->gs);
238 gs |= 3;
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700239 savesegment(gs, oldgs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100240 if (gs != oldgs)
241 load_gs_index(gs);
242
243 RELOAD_SEG(fs, 3);
244 RELOAD_SEG(ds, 3);
245 RELOAD_SEG(es, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
248 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100249 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100251 err |= __get_user(regs->cs, &sc->cs);
252 regs->cs |= 3;
253 err |= __get_user(regs->ss, &sc->ss);
254 regs->ss |= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
H. Peter Anvin742fa542008-01-30 13:30:56 +0100256 err |= __get_user(tmpflags, &sc->flags);
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -0700257 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100258 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100259 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100260
261 err |= __get_user(tmp, &sc->fpstate);
262 buf = compat_ptr(tmp);
Suresh Siddhaab513702008-07-29 10:29:22 -0700263 err |= restore_i387_xstate_ia32(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
H. Peter Anvin742fa542008-01-30 13:30:56 +0100265 err |= __get_user(tmp, &sc->ax);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100266 *peax = tmp;
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
271asmlinkage long sys32_sigreturn(struct pt_regs *regs)
272{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100273 struct sigframe __user *frame = (struct sigframe __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100275 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
278 goto badframe;
279 if (__get_user(set.sig[0], &frame->sc.oldmask)
280 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100281 && __copy_from_user((((char *) &set.sig) + 4),
282 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 sizeof(frame->extramask))))
284 goto badframe;
285
286 sigdelsetmask(&set, ~_BLOCKABLE);
287 spin_lock_irq(&current->sighand->siglock);
288 current->blocked = set;
289 recalc_sigpending();
290 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100291
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100292 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100294 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296badframe:
297 signal_fault(regs, frame, "32bit sigreturn");
298 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100299}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
302{
303 struct rt_sigframe __user *frame;
304 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100305 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 struct pt_regs tregs;
307
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100308 frame = (struct rt_sigframe __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
311 goto badframe;
312 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
313 goto badframe;
314
315 sigdelsetmask(&set, ~_BLOCKABLE);
316 spin_lock_irq(&current->sighand->siglock);
317 current->blocked = set;
318 recalc_sigpending();
319 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100320
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100321 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 goto badframe;
323
324 tregs = *regs;
325 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
326 goto badframe;
327
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100328 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
330badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100331 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100333}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334
335/*
336 * Set up a signal frame.
337 */
338
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100339static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
Suresh Siddhaab513702008-07-29 10:29:22 -0700340 void __user *fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100341 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
343 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700345 savesegment(gs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700347 savesegment(fs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700349 savesegment(ds, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700351 savesegment(es, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
353
H. Peter Anvin742fa542008-01-30 13:30:56 +0100354 err |= __put_user((u32)regs->di, &sc->di);
355 err |= __put_user((u32)regs->si, &sc->si);
356 err |= __put_user((u32)regs->bp, &sc->bp);
357 err |= __put_user((u32)regs->sp, &sc->sp);
358 err |= __put_user((u32)regs->bx, &sc->bx);
359 err |= __put_user((u32)regs->dx, &sc->dx);
360 err |= __put_user((u32)regs->cx, &sc->cx);
361 err |= __put_user((u32)regs->ax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 err |= __put_user((u32)regs->cs, &sc->cs);
363 err |= __put_user((u32)regs->ss, &sc->ss);
364 err |= __put_user(current->thread.trap_no, &sc->trapno);
365 err |= __put_user(current->thread.error_code, &sc->err);
H. Peter Anvin742fa542008-01-30 13:30:56 +0100366 err |= __put_user((u32)regs->ip, &sc->ip);
367 err |= __put_user((u32)regs->flags, &sc->flags);
368 err |= __put_user((u32)regs->sp, &sc->sp_at_signal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
Suresh Siddhaab513702008-07-29 10:29:22 -0700370 tmp = save_i387_xstate_ia32(fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 if (tmp < 0)
372 err = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100373 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 clear_used_math();
375 stts();
376 err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL),
377 &sc->fpstate);
378 }
379
380 /* non-iBCS2 extensions.. */
381 err |= __put_user(mask, &sc->oldmask);
382 err |= __put_user(current->thread.cr2, &sc->cr2);
383
384 return err;
385}
386
387/*
388 * Determine which stack to use..
389 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100390static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700391 size_t frame_size,
Suresh Siddhaab513702008-07-29 10:29:22 -0700392 void **fpstate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100394 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
396 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100397 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
399 /* This is the X/Open sanctioned signal stack switching. */
400 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100401 if (sas_ss_flags(sp) == 0)
402 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 }
404
405 /* This is the legacy signal stack switching. */
406 else if ((regs->ss & 0xffff) != __USER_DS &&
407 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100408 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100409 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700411 if (used_math()) {
412 sp = sp - sig_xstate_ia32_size;
413 *fpstate = (struct _fpstate_ia32 *) sp;
414 }
415
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100416 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200417 /* Align the stack pointer according to the i386 ABI,
418 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100419 sp = ((sp + 4) & -16ul) - 4;
420 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421}
422
Roland McGrath0928d6e2005-06-23 00:08:37 -0700423int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100424 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
426 struct sigframe __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100427 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700429 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100431 /* copy_to_user optimizes that into a single 8 byte store */
432 static const struct {
433 u16 poplmovl;
434 u32 val;
435 u16 int80;
436 u16 pad;
437 } __attribute__((packed)) code = {
438 0xb858, /* popl %eax ; movl $...,%eax */
439 __NR_ia32_sigreturn,
440 0x80cd, /* int $0x80 */
441 0,
442 };
443
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700444 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
446 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700447 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448
Andi Kleendd54a112006-09-26 10:52:41 +0200449 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700451 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700453 err |= ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700455 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
457 if (_COMPAT_NSIG_WORDS > 1) {
458 err |= __copy_to_user(frame->extramask, &set->sig[1],
459 sizeof(frame->extramask));
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100460 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700461 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
Roland McGrathaf65d642008-01-30 13:30:43 +0100464 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100465 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100466 } else {
467 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700468 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100469 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
470 sigreturn);
471 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100472 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100473 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100474 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
475
476 /*
477 * These are actually not used anymore, but left because some
478 * gdb versions depend on them as a marker.
479 */
480 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700482 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
484 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100485 regs->sp = (unsigned long) frame;
486 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
Andi Kleen536e3ee2006-09-26 10:52:41 +0200488 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100489 regs->ax = sig;
490 regs->dx = 0;
491 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200492
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700493 loadsegment(ds, __USER32_DS);
494 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100496 regs->cs = __USER32_CS;
497 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100500 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100501 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502#endif
503
Andi Kleen1d001df2006-09-26 10:52:26 +0200504 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505}
506
Roland McGrath0928d6e2005-06-23 00:08:37 -0700507int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100508 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 struct rt_sigframe __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100511 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700513 void __user *fpstate = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100515 /* __copy_to_user optimizes that into a single 8 byte store */
516 static const struct {
517 u8 movl;
518 u32 val;
519 u16 int80;
520 u16 pad;
521 u8 pad2;
522 } __attribute__((packed)) code = {
523 0xb8,
524 __NR_ia32_rt_sigreturn,
525 0x80cd,
526 0,
527 };
528
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700529 frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
531 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700532 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Hiroshi Shimamoto812b1212008-07-16 19:21:31 -0700534 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
536 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
537 err |= copy_siginfo_to_user32(&frame->info, info);
538 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700539 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541 /* Create the ucontext. */
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700542 if (cpu_has_xsave)
543 err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
544 else
545 err |= __put_user(0, &frame->uc.uc_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 err |= __put_user(0, &frame->uc.uc_link);
547 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100548 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 &frame->uc.uc_stack.ss_flags);
550 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700551 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100552 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
554 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700555 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100557 if (ka->sa.sa_flags & SA_RESTORER)
558 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100559 else
560 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
561 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100562 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100564 /*
565 * Not actually used anymore, but left because some gdb
566 * versions need it.
567 */
568 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 if (err)
Hiroshi Shimamoto3d0aedd2008-09-12 17:01:09 -0700570 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
572 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100573 regs->sp = (unsigned long) frame;
574 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500576 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100577 regs->ax = sig;
578 regs->dx = (unsigned long) &frame->info;
579 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500580
Albert Cahalan8e3de532006-12-07 02:14:06 +0100581 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100582 regs->ax = sig;
583 regs->dx = (unsigned long) &frame->info;
584 regs->cx = (unsigned long) &frame->uc;
Albert Cahalan8e3de532006-12-07 02:14:06 +0100585
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700586 loadsegment(ds, __USER32_DS);
587 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100588
589 regs->cs = __USER32_CS;
590 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100593 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100594 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595#endif
596
Andi Kleen1d001df2006-09-26 10:52:26 +0200597 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598}