blob: f1a2ac777fafa0a6b6f14b8296eae650eb45e6ee [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;
182 struct _fpstate_ia32 fpstate;
183 unsigned int extramask[_COMPAT_NSIG_WORDS-1];
184 char retcode[8];
185};
186
187struct rt_sigframe
188{
189 u32 pretcode;
190 int sig;
191 u32 pinfo;
192 u32 puc;
193 compat_siginfo_t info;
194 struct ucontext_ia32 uc;
195 struct _fpstate_ia32 fpstate;
196 char retcode[8];
197};
198
H. Peter Anvin742fa542008-01-30 13:30:56 +0100199#define COPY(x) { \
200 unsigned int reg; \
201 err |= __get_user(reg, &sc->x); \
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100202 regs->x = reg; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
204
205#define RELOAD_SEG(seg,mask) \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100206 { unsigned int cur; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 unsigned short pre; \
208 err |= __get_user(pre, &sc->seg); \
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700209 savesegment(seg, cur); \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100210 pre |= mask; \
211 if (pre != cur) loadsegment(seg, pre); }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100213static int ia32_restore_sigcontext(struct pt_regs *regs,
214 struct sigcontext_ia32 __user *sc,
215 unsigned int *peax)
216{
217 unsigned int tmpflags, gs, oldgs, err = 0;
218 struct _fpstate_ia32 __user *buf;
219 u32 tmp;
220
221 /* Always make any pending restarted system calls return -EINTR */
222 current_thread_info()->restart_block.fn = do_no_restart_syscall;
223
224#if DEBUG_SIG
225 printk(KERN_DEBUG "SIG restore_sigcontext: "
226 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100227 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100228#endif
229
230 /*
231 * Reload fs and gs if they have changed in the signal
232 * handler. This does not handle long fs/gs base changes in
233 * the handler, but does not clobber them at least in the
234 * normal case.
235 */
236 err |= __get_user(gs, &sc->gs);
237 gs |= 3;
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700238 savesegment(gs, oldgs);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100239 if (gs != oldgs)
240 load_gs_index(gs);
241
242 RELOAD_SEG(fs, 3);
243 RELOAD_SEG(ds, 3);
244 RELOAD_SEG(es, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
246 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
247 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100248 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100250 err |= __get_user(regs->cs, &sc->cs);
251 regs->cs |= 3;
252 err |= __get_user(regs->ss, &sc->ss);
253 regs->ss |= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
H. Peter Anvin742fa542008-01-30 13:30:56 +0100255 err |= __get_user(tmpflags, &sc->flags);
Hiroshi Shimamotofbdb7da2008-07-14 15:34:09 -0700256 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100257 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100258 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100259
260 err |= __get_user(tmp, &sc->fpstate);
261 buf = compat_ptr(tmp);
262 if (buf) {
263 if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
264 goto badframe;
Roland McGrath44210112008-01-30 13:31:50 +0100265 err |= restore_i387_ia32(buf);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100266 } else {
267 struct task_struct *me = current;
268
269 if (used_math()) {
270 clear_fpu(me);
271 clear_used_math();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 }
273 }
274
H. Peter Anvin742fa542008-01-30 13:30:56 +0100275 err |= __get_user(tmp, &sc->ax);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100276 *peax = tmp;
277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 return err;
279
280badframe:
281 return 1;
282}
283
284asmlinkage long sys32_sigreturn(struct pt_regs *regs)
285{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100286 struct sigframe __user *frame = (struct sigframe __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100288 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
291 goto badframe;
292 if (__get_user(set.sig[0], &frame->sc.oldmask)
293 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100294 && __copy_from_user((((char *) &set.sig) + 4),
295 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 sizeof(frame->extramask))))
297 goto badframe;
298
299 sigdelsetmask(&set, ~_BLOCKABLE);
300 spin_lock_irq(&current->sighand->siglock);
301 current->blocked = set;
302 recalc_sigpending();
303 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100304
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100305 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100307 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309badframe:
310 signal_fault(regs, frame, "32bit sigreturn");
311 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100312}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
314asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
315{
316 struct rt_sigframe __user *frame;
317 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100318 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 struct pt_regs tregs;
320
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100321 frame = (struct rt_sigframe __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
324 goto badframe;
325 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
326 goto badframe;
327
328 sigdelsetmask(&set, ~_BLOCKABLE);
329 spin_lock_irq(&current->sighand->siglock);
330 current->blocked = set;
331 recalc_sigpending();
332 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100333
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100334 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 goto badframe;
336
337 tregs = *regs;
338 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
339 goto badframe;
340
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100341 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
343badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100344 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100346}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
348/*
349 * Set up a signal frame.
350 */
351
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100352static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
353 struct _fpstate_ia32 __user *fpstate,
354 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
356 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700358 savesegment(gs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700360 savesegment(fs, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700362 savesegment(ds, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700364 savesegment(es, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
366
H. Peter Anvin742fa542008-01-30 13:30:56 +0100367 err |= __put_user((u32)regs->di, &sc->di);
368 err |= __put_user((u32)regs->si, &sc->si);
369 err |= __put_user((u32)regs->bp, &sc->bp);
370 err |= __put_user((u32)regs->sp, &sc->sp);
371 err |= __put_user((u32)regs->bx, &sc->bx);
372 err |= __put_user((u32)regs->dx, &sc->dx);
373 err |= __put_user((u32)regs->cx, &sc->cx);
374 err |= __put_user((u32)regs->ax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 err |= __put_user((u32)regs->cs, &sc->cs);
376 err |= __put_user((u32)regs->ss, &sc->ss);
377 err |= __put_user(current->thread.trap_no, &sc->trapno);
378 err |= __put_user(current->thread.error_code, &sc->err);
H. Peter Anvin742fa542008-01-30 13:30:56 +0100379 err |= __put_user((u32)regs->ip, &sc->ip);
380 err |= __put_user((u32)regs->flags, &sc->flags);
381 err |= __put_user((u32)regs->sp, &sc->sp_at_signal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
Roland McGrath44210112008-01-30 13:31:50 +0100383 tmp = save_i387_ia32(fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 if (tmp < 0)
385 err = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100386 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 clear_used_math();
388 stts();
389 err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL),
390 &sc->fpstate);
391 }
392
393 /* non-iBCS2 extensions.. */
394 err |= __put_user(mask, &sc->oldmask);
395 err |= __put_user(current->thread.cr2, &sc->cr2);
396
397 return err;
398}
399
400/*
401 * Determine which stack to use..
402 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100403static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
404 size_t frame_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100406 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100409 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411 /* This is the X/Open sanctioned signal stack switching. */
412 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100413 if (sas_ss_flags(sp) == 0)
414 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 }
416
417 /* This is the legacy signal stack switching. */
418 else if ((regs->ss & 0xffff) != __USER_DS &&
419 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100420 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100421 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100423 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200424 /* Align the stack pointer according to the i386 ABI,
425 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100426 sp = ((sp + 4) & -16ul) - 4;
427 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428}
429
Roland McGrath0928d6e2005-06-23 00:08:37 -0700430int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100431 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432{
433 struct sigframe __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100434 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 int err = 0;
436
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100437 /* copy_to_user optimizes that into a single 8 byte store */
438 static const struct {
439 u16 poplmovl;
440 u32 val;
441 u16 int80;
442 u16 pad;
443 } __attribute__((packed)) code = {
444 0xb858, /* popl %eax ; movl $...,%eax */
445 __NR_ia32_sigreturn,
446 0x80cd, /* int $0x80 */
447 0,
448 };
449
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 frame = get_sigframe(ka, regs, sizeof(*frame));
451
452 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
453 goto give_sigsegv;
454
Andi Kleendd54a112006-09-26 10:52:41 +0200455 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 if (err)
457 goto give_sigsegv;
458
459 err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs,
460 set->sig[0]);
461 if (err)
462 goto give_sigsegv;
463
464 if (_COMPAT_NSIG_WORDS > 1) {
465 err |= __copy_to_user(frame->extramask, &set->sig[1],
466 sizeof(frame->extramask));
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100467 if (err)
468 goto give_sigsegv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Roland McGrathaf65d642008-01-30 13:30:43 +0100471 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100472 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100473 } else {
474 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700475 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100476 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
477 sigreturn);
478 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100479 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100480 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100481 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
482
483 /*
484 * These are actually not used anymore, but left because some
485 * gdb versions depend on them as a marker.
486 */
487 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 if (err)
489 goto give_sigsegv;
490
491 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100492 regs->sp = (unsigned long) frame;
493 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
Andi Kleen536e3ee2006-09-26 10:52:41 +0200495 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100496 regs->ax = sig;
497 regs->dx = 0;
498 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200499
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700500 loadsegment(ds, __USER32_DS);
501 loadsegment(es, __USER32_DS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100503 regs->cs = __USER32_CS;
504 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100507 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100508 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509#endif
510
Andi Kleen1d001df2006-09-26 10:52:26 +0200511 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513give_sigsegv:
514 force_sigsegv(sig, current);
Andi Kleen1d001df2006-09-26 10:52:26 +0200515 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
Roland McGrath0928d6e2005-06-23 00:08:37 -0700518int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100519 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520{
521 struct rt_sigframe __user *frame;
Roland McGrathaf65d642008-01-30 13:30:43 +0100522 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 int err = 0;
524
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100525 /* __copy_to_user optimizes that into a single 8 byte store */
526 static const struct {
527 u8 movl;
528 u32 val;
529 u16 int80;
530 u16 pad;
531 u8 pad2;
532 } __attribute__((packed)) code = {
533 0xb8,
534 __NR_ia32_rt_sigreturn,
535 0x80cd,
536 0,
537 };
538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 frame = get_sigframe(ka, regs, sizeof(*frame));
540
541 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
542 goto give_sigsegv;
543
Hiroshi Shimamoto812b1212008-07-16 19:21:31 -0700544 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
546 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
547 err |= copy_siginfo_to_user32(&frame->info, info);
548 if (err)
549 goto give_sigsegv;
550
551 /* Create the ucontext. */
552 err |= __put_user(0, &frame->uc.uc_flags);
553 err |= __put_user(0, &frame->uc.uc_link);
554 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100555 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 &frame->uc.uc_stack.ss_flags);
557 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
558 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100559 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
561 if (err)
562 goto give_sigsegv;
563
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100564 if (ka->sa.sa_flags & SA_RESTORER)
565 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100566 else
567 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
568 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100569 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100571 /*
572 * Not actually used anymore, but left because some gdb
573 * versions need it.
574 */
575 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 if (err)
577 goto give_sigsegv;
578
579 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100580 regs->sp = (unsigned long) frame;
581 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500583 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100584 regs->ax = sig;
585 regs->dx = (unsigned long) &frame->info;
586 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500587
Albert Cahalan8e3de532006-12-07 02:14:06 +0100588 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100589 regs->ax = sig;
590 regs->dx = (unsigned long) &frame->info;
591 regs->cx = (unsigned long) &frame->uc;
Albert Cahalan8e3de532006-12-07 02:14:06 +0100592
Jeremy Fitzhardingeb6edbb12008-08-19 13:04:19 -0700593 loadsegment(ds, __USER32_DS);
594 loadsegment(es, __USER32_DS);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100595
596 regs->cs = __USER32_CS;
597 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100600 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100601 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602#endif
603
Andi Kleen1d001df2006-09-26 10:52:26 +0200604 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
606give_sigsegv:
607 force_sigsegv(sig, current);
Andi Kleen1d001df2006-09-26 10:52:26 +0200608 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609}