blob: cb3856a18c8544e1ffe722d33a7713439043829d [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
39asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
40void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
41
42int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
43{
44 int err;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010045
46 if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 return -EFAULT;
48
49 /* If you change siginfo_t structure, please make sure that
50 this code is fixed accordingly.
51 It should never copy any pad contained in the structure
52 to avoid security leaks, but must copy the generic
53 3 ints plus the relevant union member. */
54 err = __put_user(from->si_signo, &to->si_signo);
55 err |= __put_user(from->si_errno, &to->si_errno);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010056 err |= __put_user((short)from->si_code, &to->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58 if (from->si_code < 0) {
59 err |= __put_user(from->si_pid, &to->si_pid);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010060 err |= __put_user(from->si_uid, &to->si_uid);
61 err |= __put_user(ptr_to_compat(from->si_ptr), &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 } else {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010063 /*
64 * First 32bits of unions are always present:
65 * si_pid === si_band === si_tid === si_addr(LS half)
66 */
67 err |= __put_user(from->_sifields._pad[0],
68 &to->_sifields._pad[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 switch (from->si_code >> 16) {
70 case __SI_FAULT >> 16:
71 break;
72 case __SI_CHLD >> 16:
73 err |= __put_user(from->si_utime, &to->si_utime);
74 err |= __put_user(from->si_stime, &to->si_stime);
75 err |= __put_user(from->si_status, &to->si_status);
76 /* FALL THROUGH */
77 default:
78 case __SI_KILL >> 16:
79 err |= __put_user(from->si_uid, &to->si_uid);
80 break;
81 case __SI_POLL >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010082 err |= __put_user(from->si_fd, &to->si_fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 break;
84 case __SI_TIMER >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010085 err |= __put_user(from->si_overrun, &to->si_overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 err |= __put_user(ptr_to_compat(from->si_ptr),
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010087 &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 break;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010089 /* This is not generated by the kernel as of now. */
90 case __SI_RT >> 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 case __SI_MESGQ >> 16:
92 err |= __put_user(from->si_uid, &to->si_uid);
93 err |= __put_user(from->si_int, &to->si_int);
94 break;
95 }
96 }
97 return err;
98}
99
100int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
101{
102 int err;
103 u32 ptr32;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100104
105 if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 return -EFAULT;
107
108 err = __get_user(to->si_signo, &from->si_signo);
109 err |= __get_user(to->si_errno, &from->si_errno);
110 err |= __get_user(to->si_code, &from->si_code);
111
112 err |= __get_user(to->si_pid, &from->si_pid);
113 err |= __get_user(to->si_uid, &from->si_uid);
114 err |= __get_user(ptr32, &from->si_ptr);
115 to->si_ptr = compat_ptr(ptr32);
116
117 return err;
118}
119
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100120asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 mask &= _BLOCKABLE;
123 spin_lock_irq(&current->sighand->siglock);
Andi Kleen1d001df2006-09-26 10:52:26 +0200124 current->saved_sigmask = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 siginitset(&current->blocked, mask);
126 recalc_sigpending();
127 spin_unlock_irq(&current->sighand->siglock);
128
Andi Kleen1d001df2006-09-26 10:52:26 +0200129 current->state = TASK_INTERRUPTIBLE;
130 schedule();
Roland McGrath5a8da0e2008-04-30 00:53:10 -0700131 set_restore_sigmask();
Andi Kleen1d001df2006-09-26 10:52:26 +0200132 return -ERESTARTNOHAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133}
134
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100135asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
136 stack_ia32_t __user *uoss_ptr,
137 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100139 stack_t uss, uoss;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 int ret;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100141 mm_segment_t seg;
142
143 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100145
146 memset(&uss, 0, sizeof(stack_t));
147 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 __get_user(ptr, &uss_ptr->ss_sp) ||
149 __get_user(uss.ss_flags, &uss_ptr->ss_flags) ||
150 __get_user(uss.ss_size, &uss_ptr->ss_size))
151 return -EFAULT;
152 uss.ss_sp = compat_ptr(ptr);
153 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100154 seg = get_fs();
155 set_fs(KERNEL_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100156 ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100157 set_fs(seg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 if (ret >= 0 && uoss_ptr) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100159 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 __put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) ||
161 __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) ||
162 __put_user(uoss.ss_size, &uoss_ptr->ss_size))
163 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100164 }
165 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166}
167
168/*
169 * Do a signal return; undo the signal stack.
170 */
171
172struct sigframe
173{
174 u32 pretcode;
175 int sig;
176 struct sigcontext_ia32 sc;
177 struct _fpstate_ia32 fpstate;
178 unsigned int extramask[_COMPAT_NSIG_WORDS-1];
179 char retcode[8];
180};
181
182struct rt_sigframe
183{
184 u32 pretcode;
185 int sig;
186 u32 pinfo;
187 u32 puc;
188 compat_siginfo_t info;
189 struct ucontext_ia32 uc;
190 struct _fpstate_ia32 fpstate;
191 char retcode[8];
192};
193
H. Peter Anvin742fa542008-01-30 13:30:56 +0100194#define COPY(x) { \
195 unsigned int reg; \
196 err |= __get_user(reg, &sc->x); \
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100197 regs->x = reg; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198}
199
200#define RELOAD_SEG(seg,mask) \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100201 { unsigned int cur; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 unsigned short pre; \
203 err |= __get_user(pre, &sc->seg); \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100204 asm volatile("movl %%" #seg ",%0" : "=r" (cur)); \
205 pre |= mask; \
206 if (pre != cur) loadsegment(seg, pre); }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100208static int ia32_restore_sigcontext(struct pt_regs *regs,
209 struct sigcontext_ia32 __user *sc,
210 unsigned int *peax)
211{
212 unsigned int tmpflags, gs, oldgs, err = 0;
213 struct _fpstate_ia32 __user *buf;
214 u32 tmp;
215
216 /* Always make any pending restarted system calls return -EINTR */
217 current_thread_info()->restart_block.fn = do_no_restart_syscall;
218
219#if DEBUG_SIG
220 printk(KERN_DEBUG "SIG restore_sigcontext: "
221 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100222 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100223#endif
224
225 /*
226 * Reload fs and gs if they have changed in the signal
227 * handler. This does not handle long fs/gs base changes in
228 * the handler, but does not clobber them at least in the
229 * normal case.
230 */
231 err |= __get_user(gs, &sc->gs);
232 gs |= 3;
233 asm("movl %%gs,%0" : "=r" (oldgs));
234 if (gs != oldgs)
235 load_gs_index(gs);
236
237 RELOAD_SEG(fs, 3);
238 RELOAD_SEG(ds, 3);
239 RELOAD_SEG(es, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
241 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
242 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100243 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100245 err |= __get_user(regs->cs, &sc->cs);
246 regs->cs |= 3;
247 err |= __get_user(regs->ss, &sc->ss);
248 regs->ss |= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
H. Peter Anvin742fa542008-01-30 13:30:56 +0100250 err |= __get_user(tmpflags, &sc->flags);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100251 regs->flags = (regs->flags & ~0x40DD5) | (tmpflags & 0x40DD5);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100252 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100253 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100254
255 err |= __get_user(tmp, &sc->fpstate);
256 buf = compat_ptr(tmp);
257 if (buf) {
258 if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
259 goto badframe;
Roland McGrath44210112008-01-30 13:31:50 +0100260 err |= restore_i387_ia32(buf);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100261 } else {
262 struct task_struct *me = current;
263
264 if (used_math()) {
265 clear_fpu(me);
266 clear_used_math();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 }
268 }
269
H. Peter Anvin742fa542008-01-30 13:30:56 +0100270 err |= __get_user(tmp, &sc->ax);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100271 *peax = tmp;
272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 return err;
274
275badframe:
276 return 1;
277}
278
279asmlinkage long sys32_sigreturn(struct pt_regs *regs)
280{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100281 struct sigframe __user *frame = (struct sigframe __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100283 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
285 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
286 goto badframe;
287 if (__get_user(set.sig[0], &frame->sc.oldmask)
288 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100289 && __copy_from_user((((char *) &set.sig) + 4),
290 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 sizeof(frame->extramask))))
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->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100302 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
304badframe:
305 signal_fault(regs, frame, "32bit sigreturn");
306 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100307}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
310{
311 struct rt_sigframe __user *frame;
312 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100313 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 struct pt_regs tregs;
315
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100316 frame = (struct rt_sigframe __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
318 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
319 goto badframe;
320 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
321 goto badframe;
322
323 sigdelsetmask(&set, ~_BLOCKABLE);
324 spin_lock_irq(&current->sighand->siglock);
325 current->blocked = set;
326 recalc_sigpending();
327 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100328
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100329 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 goto badframe;
331
332 tregs = *regs;
333 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
334 goto badframe;
335
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100336 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100339 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100341}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
343/*
344 * Set up a signal frame.
345 */
346
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100347static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
348 struct _fpstate_ia32 __user *fpstate,
349 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350{
351 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353 tmp = 0;
354 __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp));
355 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
356 __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp));
357 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
358 __asm__("movl %%ds,%0" : "=r"(tmp): "0"(tmp));
359 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
360 __asm__("movl %%es,%0" : "=r"(tmp): "0"(tmp));
361 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
362
H. Peter Anvin742fa542008-01-30 13:30:56 +0100363 err |= __put_user((u32)regs->di, &sc->di);
364 err |= __put_user((u32)regs->si, &sc->si);
365 err |= __put_user((u32)regs->bp, &sc->bp);
366 err |= __put_user((u32)regs->sp, &sc->sp);
367 err |= __put_user((u32)regs->bx, &sc->bx);
368 err |= __put_user((u32)regs->dx, &sc->dx);
369 err |= __put_user((u32)regs->cx, &sc->cx);
370 err |= __put_user((u32)regs->ax, &sc->ax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 err |= __put_user((u32)regs->cs, &sc->cs);
372 err |= __put_user((u32)regs->ss, &sc->ss);
373 err |= __put_user(current->thread.trap_no, &sc->trapno);
374 err |= __put_user(current->thread.error_code, &sc->err);
H. Peter Anvin742fa542008-01-30 13:30:56 +0100375 err |= __put_user((u32)regs->ip, &sc->ip);
376 err |= __put_user((u32)regs->flags, &sc->flags);
377 err |= __put_user((u32)regs->sp, &sc->sp_at_signal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Roland McGrath44210112008-01-30 13:31:50 +0100379 tmp = save_i387_ia32(fpstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 if (tmp < 0)
381 err = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100382 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 clear_used_math();
384 stts();
385 err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL),
386 &sc->fpstate);
387 }
388
389 /* non-iBCS2 extensions.. */
390 err |= __put_user(mask, &sc->oldmask);
391 err |= __put_user(current->thread.cr2, &sc->cr2);
392
393 return err;
394}
395
396/*
397 * Determine which stack to use..
398 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100399static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
400 size_t frame_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100402 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
404 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100405 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
407 /* This is the X/Open sanctioned signal stack switching. */
408 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100409 if (sas_ss_flags(sp) == 0)
410 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 }
412
413 /* This is the legacy signal stack switching. */
414 else if ((regs->ss & 0xffff) != __USER_DS &&
415 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100416 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100417 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100419 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200420 /* Align the stack pointer according to the i386 ABI,
421 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100422 sp = ((sp + 4) & -16ul) - 4;
423 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424}
425
Roland McGrath0928d6e2005-06-23 00:08:37 -0700426int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100427 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428{
429 struct sigframe __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100430 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 int err = 0;
432
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100433 /* copy_to_user optimizes that into a single 8 byte store */
434 static const struct {
435 u16 poplmovl;
436 u32 val;
437 u16 int80;
438 u16 pad;
439 } __attribute__((packed)) code = {
440 0xb858, /* popl %eax ; movl $...,%eax */
441 __NR_ia32_sigreturn,
442 0x80cd, /* int $0x80 */
443 0,
444 };
445
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 frame = get_sigframe(ka, regs, sizeof(*frame));
447
448 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
449 goto give_sigsegv;
450
Andi Kleendd54a112006-09-26 10:52:41 +0200451 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 if (err)
453 goto give_sigsegv;
454
455 err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs,
456 set->sig[0]);
457 if (err)
458 goto give_sigsegv;
459
460 if (_COMPAT_NSIG_WORDS > 1) {
461 err |= __copy_to_user(frame->extramask, &set->sig[1],
462 sizeof(frame->extramask));
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100463 if (err)
464 goto give_sigsegv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
Roland McGrathaf65d642008-01-30 13:30:43 +0100467 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100468 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100469 } else {
470 /* Return stub is in 32bit vsyscall page */
Roland McGrath1a3e4ca2008-04-09 01:29:27 -0700471 if (current->mm->context.vdso)
Roland McGrathaf65d642008-01-30 13:30:43 +0100472 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
473 sigreturn);
474 else
Jan Engelhardtade1af72008-01-30 13:33:23 +0100475 restorer = &frame->retcode;
Roland McGrathaf65d642008-01-30 13:30:43 +0100476 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100477 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
478
479 /*
480 * These are actually not used anymore, but left because some
481 * gdb versions depend on them as a marker.
482 */
483 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 if (err)
485 goto give_sigsegv;
486
487 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100488 regs->sp = (unsigned long) frame;
489 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
Andi Kleen536e3ee2006-09-26 10:52:41 +0200491 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100492 regs->ax = sig;
493 regs->dx = 0;
494 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200495
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100496 asm volatile("movl %0,%%ds" :: "r" (__USER32_DS));
497 asm volatile("movl %0,%%es" :: "r" (__USER32_DS));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100499 regs->cs = __USER32_CS;
500 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100503 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100504 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505#endif
506
Andi Kleen1d001df2006-09-26 10:52:26 +0200507 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509give_sigsegv:
510 force_sigsegv(sig, current);
Andi Kleen1d001df2006-09-26 10:52:26 +0200511 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512}
513
Roland McGrath0928d6e2005-06-23 00:08:37 -0700514int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100515 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516{
517 struct rt_sigframe __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100518 struct exec_domain *ed = current_thread_info()->exec_domain;
Roland McGrathaf65d642008-01-30 13:30:43 +0100519 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 int err = 0;
521
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100522 /* __copy_to_user optimizes that into a single 8 byte store */
523 static const struct {
524 u8 movl;
525 u32 val;
526 u16 int80;
527 u16 pad;
528 u8 pad2;
529 } __attribute__((packed)) code = {
530 0xb8,
531 __NR_ia32_rt_sigreturn,
532 0x80cd,
533 0,
534 };
535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 frame = get_sigframe(ka, regs, sizeof(*frame));
537
538 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
539 goto give_sigsegv;
540
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100541 err |= __put_user((ed && ed->signal_invmap && sig < 32
542 ? ed->signal_invmap[sig] : sig), &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
544 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
545 err |= copy_siginfo_to_user32(&frame->info, info);
546 if (err)
547 goto give_sigsegv;
548
549 /* Create the ucontext. */
550 err |= __put_user(0, &frame->uc.uc_flags);
551 err |= __put_user(0, &frame->uc.uc_link);
552 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100553 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 &frame->uc.uc_stack.ss_flags);
555 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
556 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100557 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
559 if (err)
560 goto give_sigsegv;
561
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100562 if (ka->sa.sa_flags & SA_RESTORER)
563 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100564 else
565 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
566 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100567 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100569 /*
570 * Not actually used anymore, but left because some gdb
571 * versions need it.
572 */
573 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 if (err)
575 goto give_sigsegv;
576
577 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100578 regs->sp = (unsigned long) frame;
579 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500581 /* 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 Cahalana7aacdf2006-10-29 22:26:17 -0500585
Albert Cahalan8e3de532006-12-07 02:14:06 +0100586 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100587 regs->ax = sig;
588 regs->dx = (unsigned long) &frame->info;
589 regs->cx = (unsigned long) &frame->uc;
Albert Cahalan8e3de532006-12-07 02:14:06 +0100590
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100591 asm volatile("movl %0,%%ds" :: "r" (__USER32_DS));
592 asm volatile("movl %0,%%es" :: "r" (__USER32_DS));
593
594 regs->cs = __USER32_CS;
595 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100598 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100599 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600#endif
601
Andi Kleen1d001df2006-09-26 10:52:26 +0200602 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
604give_sigsegv:
605 force_sigsegv(sig, current);
Andi Kleen1d001df2006-09-26 10:52:26 +0200606 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607}