blob: f2da443f8c7b2819b5aa87b4c3306301c5a39219 [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>
32#include <asm/fpu32.h>
33#include <asm/proto.h>
Roland McGrathaf65d642008-01-30 13:30:43 +010034#include <asm/vdso.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36#define DEBUG_SIG 0
37
38#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
39
40asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
41void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
42
43int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
44{
45 int err;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010046
47 if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 return -EFAULT;
49
50 /* If you change siginfo_t structure, please make sure that
51 this code is fixed accordingly.
52 It should never copy any pad contained in the structure
53 to avoid security leaks, but must copy the generic
54 3 ints plus the relevant union member. */
55 err = __put_user(from->si_signo, &to->si_signo);
56 err |= __put_user(from->si_errno, &to->si_errno);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010057 err |= __put_user((short)from->si_code, &to->si_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59 if (from->si_code < 0) {
60 err |= __put_user(from->si_pid, &to->si_pid);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010061 err |= __put_user(from->si_uid, &to->si_uid);
62 err |= __put_user(ptr_to_compat(from->si_ptr), &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 } else {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010064 /*
65 * First 32bits of unions are always present:
66 * si_pid === si_band === si_tid === si_addr(LS half)
67 */
68 err |= __put_user(from->_sifields._pad[0],
69 &to->_sifields._pad[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 switch (from->si_code >> 16) {
71 case __SI_FAULT >> 16:
72 break;
73 case __SI_CHLD >> 16:
74 err |= __put_user(from->si_utime, &to->si_utime);
75 err |= __put_user(from->si_stime, &to->si_stime);
76 err |= __put_user(from->si_status, &to->si_status);
77 /* FALL THROUGH */
78 default:
79 case __SI_KILL >> 16:
80 err |= __put_user(from->si_uid, &to->si_uid);
81 break;
82 case __SI_POLL >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010083 err |= __put_user(from->si_fd, &to->si_fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 break;
85 case __SI_TIMER >> 16:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010086 err |= __put_user(from->si_overrun, &to->si_overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 err |= __put_user(ptr_to_compat(from->si_ptr),
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010088 &to->si_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 break;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +010090 /* This is not generated by the kernel as of now. */
91 case __SI_RT >> 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 case __SI_MESGQ >> 16:
93 err |= __put_user(from->si_uid, &to->si_uid);
94 err |= __put_user(from->si_int, &to->si_int);
95 break;
96 }
97 }
98 return err;
99}
100
101int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
102{
103 int err;
104 u32 ptr32;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100105
106 if (!access_ok(VERIFY_READ, from, sizeof(compat_siginfo_t)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 return -EFAULT;
108
109 err = __get_user(to->si_signo, &from->si_signo);
110 err |= __get_user(to->si_errno, &from->si_errno);
111 err |= __get_user(to->si_code, &from->si_code);
112
113 err |= __get_user(to->si_pid, &from->si_pid);
114 err |= __get_user(to->si_uid, &from->si_uid);
115 err |= __get_user(ptr32, &from->si_ptr);
116 to->si_ptr = compat_ptr(ptr32);
117
118 return err;
119}
120
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100121asmlinkage long sys32_sigsuspend(int history0, int history1, old_sigset_t mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 mask &= _BLOCKABLE;
124 spin_lock_irq(&current->sighand->siglock);
Andi Kleen1d001df2006-09-26 10:52:26 +0200125 current->saved_sigmask = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 siginitset(&current->blocked, mask);
127 recalc_sigpending();
128 spin_unlock_irq(&current->sighand->siglock);
129
Andi Kleen1d001df2006-09-26 10:52:26 +0200130 current->state = TASK_INTERRUPTIBLE;
131 schedule();
132 set_thread_flag(TIF_RESTORE_SIGMASK);
133 return -ERESTARTNOHAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100136asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
137 stack_ia32_t __user *uoss_ptr,
138 struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139{
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100140 stack_t uss, uoss;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 int ret;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100142 mm_segment_t seg;
143
144 if (uss_ptr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 u32 ptr;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100146
147 memset(&uss, 0, sizeof(stack_t));
148 if (!access_ok(VERIFY_READ, uss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 __get_user(ptr, &uss_ptr->ss_sp) ||
150 __get_user(uss.ss_flags, &uss_ptr->ss_flags) ||
151 __get_user(uss.ss_size, &uss_ptr->ss_size))
152 return -EFAULT;
153 uss.ss_sp = compat_ptr(ptr);
154 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100155 seg = get_fs();
156 set_fs(KERNEL_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100157 ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100158 set_fs(seg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 if (ret >= 0 && uoss_ptr) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100160 if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 __put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) ||
162 __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) ||
163 __put_user(uoss.ss_size, &uoss_ptr->ss_size))
164 ret = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100165 }
166 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167}
168
169/*
170 * Do a signal return; undo the signal stack.
171 */
172
173struct sigframe
174{
175 u32 pretcode;
176 int sig;
177 struct sigcontext_ia32 sc;
178 struct _fpstate_ia32 fpstate;
179 unsigned int extramask[_COMPAT_NSIG_WORDS-1];
180 char retcode[8];
181};
182
183struct rt_sigframe
184{
185 u32 pretcode;
186 int sig;
187 u32 pinfo;
188 u32 puc;
189 compat_siginfo_t info;
190 struct ucontext_ia32 uc;
191 struct _fpstate_ia32 fpstate;
192 char retcode[8];
193};
194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195#define COPY(x) { \
196 unsigned int reg; \
197 err |= __get_user(reg, &sc->e ##x); \
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100198 regs->x = reg; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
201#define RELOAD_SEG(seg,mask) \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100202 { unsigned int cur; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 unsigned short pre; \
204 err |= __get_user(pre, &sc->seg); \
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100205 asm volatile("movl %%" #seg ",%0" : "=r" (cur)); \
206 pre |= mask; \
207 if (pre != cur) loadsegment(seg, pre); }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100209static int ia32_restore_sigcontext(struct pt_regs *regs,
210 struct sigcontext_ia32 __user *sc,
211 unsigned int *peax)
212{
213 unsigned int tmpflags, gs, oldgs, err = 0;
214 struct _fpstate_ia32 __user *buf;
215 u32 tmp;
216
217 /* Always make any pending restarted system calls return -EINTR */
218 current_thread_info()->restart_block.fn = do_no_restart_syscall;
219
220#if DEBUG_SIG
221 printk(KERN_DEBUG "SIG restore_sigcontext: "
222 "sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100223 sc, sc->err, sc->ip, sc->cs, sc->flags);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100224#endif
225
226 /*
227 * Reload fs and gs if they have changed in the signal
228 * handler. This does not handle long fs/gs base changes in
229 * the handler, but does not clobber them at least in the
230 * normal case.
231 */
232 err |= __get_user(gs, &sc->gs);
233 gs |= 3;
234 asm("movl %%gs,%0" : "=r" (oldgs));
235 if (gs != oldgs)
236 load_gs_index(gs);
237
238 RELOAD_SEG(fs, 3);
239 RELOAD_SEG(ds, 3);
240 RELOAD_SEG(es, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
242 COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
243 COPY(dx); COPY(cx); COPY(ip);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100244 /* Don't touch extended registers */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100246 err |= __get_user(regs->cs, &sc->cs);
247 regs->cs |= 3;
248 err |= __get_user(regs->ss, &sc->ss);
249 regs->ss |= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100251 err |= __get_user(tmpflags, &sc->eflags);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100252 regs->flags = (regs->flags & ~0x40DD5) | (tmpflags & 0x40DD5);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100253 /* disable syscall checks */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100254 regs->orig_ax = -1;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100255
256 err |= __get_user(tmp, &sc->fpstate);
257 buf = compat_ptr(tmp);
258 if (buf) {
259 if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
260 goto badframe;
261 err |= restore_i387_ia32(current, buf, 0);
262 } else {
263 struct task_struct *me = current;
264
265 if (used_math()) {
266 clear_fpu(me);
267 clear_used_math();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 }
269 }
270
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100271 err |= __get_user(tmp, &sc->eax);
272 *peax = tmp;
273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 return err;
275
276badframe:
277 return 1;
278}
279
280asmlinkage long sys32_sigreturn(struct pt_regs *regs)
281{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100282 struct sigframe __user *frame = (struct sigframe __user *)(regs->sp-8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100284 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
287 goto badframe;
288 if (__get_user(set.sig[0], &frame->sc.oldmask)
289 || (_COMPAT_NSIG_WORDS > 1
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100290 && __copy_from_user((((char *) &set.sig) + 4),
291 &frame->extramask,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 sizeof(frame->extramask))))
293 goto badframe;
294
295 sigdelsetmask(&set, ~_BLOCKABLE);
296 spin_lock_irq(&current->sighand->siglock);
297 current->blocked = set;
298 recalc_sigpending();
299 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100300
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100301 if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 goto badframe;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100303 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305badframe:
306 signal_fault(regs, frame, "32bit sigreturn");
307 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100308}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
311{
312 struct rt_sigframe __user *frame;
313 sigset_t set;
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100314 unsigned int ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 struct pt_regs tregs;
316
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100317 frame = (struct rt_sigframe __user *)(regs->sp - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
320 goto badframe;
321 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
322 goto badframe;
323
324 sigdelsetmask(&set, ~_BLOCKABLE);
325 spin_lock_irq(&current->sighand->siglock);
326 current->blocked = set;
327 recalc_sigpending();
328 spin_unlock_irq(&current->sighand->siglock);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100329
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100330 if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 goto badframe;
332
333 tregs = *regs;
334 if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT)
335 goto badframe;
336
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100337 return ax;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
339badframe:
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100340 signal_fault(regs, frame, "32bit rt sigreturn");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 return 0;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100342}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344/*
345 * Set up a signal frame.
346 */
347
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100348static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
349 struct _fpstate_ia32 __user *fpstate,
350 struct pt_regs *regs, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
352 int tmp, err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354 tmp = 0;
355 __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp));
356 err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
357 __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp));
358 err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
359 __asm__("movl %%ds,%0" : "=r"(tmp): "0"(tmp));
360 err |= __put_user(tmp, (unsigned int __user *)&sc->ds);
361 __asm__("movl %%es,%0" : "=r"(tmp): "0"(tmp));
362 err |= __put_user(tmp, (unsigned int __user *)&sc->es);
363
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100364 err |= __put_user((u32)regs->di, &sc->edi);
365 err |= __put_user((u32)regs->si, &sc->esi);
366 err |= __put_user((u32)regs->bp, &sc->ebp);
367 err |= __put_user((u32)regs->sp, &sc->esp);
368 err |= __put_user((u32)regs->bx, &sc->ebx);
369 err |= __put_user((u32)regs->dx, &sc->edx);
370 err |= __put_user((u32)regs->cx, &sc->ecx);
371 err |= __put_user((u32)regs->ax, &sc->eax);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 err |= __put_user((u32)regs->cs, &sc->cs);
373 err |= __put_user((u32)regs->ss, &sc->ss);
374 err |= __put_user(current->thread.trap_no, &sc->trapno);
375 err |= __put_user(current->thread.error_code, &sc->err);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100376 err |= __put_user((u32)regs->ip, &sc->eip);
377 err |= __put_user((u32)regs->flags, &sc->eflags);
378 err |= __put_user((u32)regs->sp, &sc->esp_at_signal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
380 tmp = save_i387_ia32(current, fpstate, regs, 0);
381 if (tmp < 0)
382 err = -EFAULT;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100383 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 clear_used_math();
385 stts();
386 err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL),
387 &sc->fpstate);
388 }
389
390 /* non-iBCS2 extensions.. */
391 err |= __put_user(mask, &sc->oldmask);
392 err |= __put_user(current->thread.cr2, &sc->cr2);
393
394 return err;
395}
396
397/*
398 * Determine which stack to use..
399 */
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100400static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
401 size_t frame_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100403 unsigned long sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405 /* Default to using normal stack */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100406 sp = regs->sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 /* This is the X/Open sanctioned signal stack switching. */
409 if (ka->sa.sa_flags & SA_ONSTACK) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100410 if (sas_ss_flags(sp) == 0)
411 sp = current->sas_ss_sp + current->sas_ss_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 }
413
414 /* This is the legacy signal stack switching. */
415 else if ((regs->ss & 0xffff) != __USER_DS &&
416 !(ka->sa.sa_flags & SA_RESTORER) &&
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100417 ka->sa.sa_restorer)
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100418 sp = (unsigned long) ka->sa.sa_restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100420 sp -= frame_size;
Markus F.X.J. Oberhumerd347f372005-10-09 18:54:23 +0200421 /* Align the stack pointer according to the i386 ABI,
422 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100423 sp = ((sp + 4) & -16ul) - 4;
424 return (void __user *) sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426
Roland McGrath0928d6e2005-06-23 00:08:37 -0700427int ia32_setup_frame(int sig, struct k_sigaction *ka,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100428 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
430 struct sigframe __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100431 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 int err = 0;
433
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100434 /* copy_to_user optimizes that into a single 8 byte store */
435 static const struct {
436 u16 poplmovl;
437 u32 val;
438 u16 int80;
439 u16 pad;
440 } __attribute__((packed)) code = {
441 0xb858, /* popl %eax ; movl $...,%eax */
442 __NR_ia32_sigreturn,
443 0x80cd, /* int $0x80 */
444 0,
445 };
446
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 frame = get_sigframe(ka, regs, sizeof(*frame));
448
449 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
450 goto give_sigsegv;
451
Andi Kleendd54a112006-09-26 10:52:41 +0200452 err |= __put_user(sig, &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 if (err)
454 goto give_sigsegv;
455
456 err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs,
457 set->sig[0]);
458 if (err)
459 goto give_sigsegv;
460
461 if (_COMPAT_NSIG_WORDS > 1) {
462 err |= __copy_to_user(frame->extramask, &set->sig[1],
463 sizeof(frame->extramask));
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100464 if (err)
465 goto give_sigsegv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
Roland McGrathaf65d642008-01-30 13:30:43 +0100468 if (ka->sa.sa_flags & SA_RESTORER) {
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100469 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100470 } else {
471 /* Return stub is in 32bit vsyscall page */
472 if (current->binfmt->hasvdso)
473 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
474 sigreturn);
475 else
476 restorer = (void *)&frame->retcode;
477 }
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100478 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
479
480 /*
481 * These are actually not used anymore, but left because some
482 * gdb versions depend on them as a marker.
483 */
484 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 if (err)
486 goto give_sigsegv;
487
488 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100489 regs->sp = (unsigned long) frame;
490 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
Andi Kleen536e3ee2006-09-26 10:52:41 +0200492 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100493 regs->ax = sig;
494 regs->dx = 0;
495 regs->cx = 0;
Andi Kleen536e3ee2006-09-26 10:52:41 +0200496
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100497 asm volatile("movl %0,%%ds" :: "r" (__USER32_DS));
498 asm volatile("movl %0,%%es" :: "r" (__USER32_DS));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100500 regs->cs = __USER32_CS;
501 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503 set_fs(USER_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100504 regs->flags &= ~TF_MASK;
Andi Kleenab2e0b42006-09-26 10:52:41 +0200505 if (test_thread_flag(TIF_SINGLESTEP))
506 ptrace_notify(SIGTRAP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100509 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100510 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511#endif
512
Andi Kleen1d001df2006-09-26 10:52:26 +0200513 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
515give_sigsegv:
516 force_sigsegv(sig, current);
Andi Kleen1d001df2006-09-26 10:52:26 +0200517 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518}
519
Roland McGrath0928d6e2005-06-23 00:08:37 -0700520int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100521 compat_sigset_t *set, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522{
523 struct rt_sigframe __user *frame;
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100524 struct exec_domain *ed = current_thread_info()->exec_domain;
Roland McGrathaf65d642008-01-30 13:30:43 +0100525 void __user *restorer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 int err = 0;
527
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100528 /* __copy_to_user optimizes that into a single 8 byte store */
529 static const struct {
530 u8 movl;
531 u32 val;
532 u16 int80;
533 u16 pad;
534 u8 pad2;
535 } __attribute__((packed)) code = {
536 0xb8,
537 __NR_ia32_rt_sigreturn,
538 0x80cd,
539 0,
540 };
541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 frame = get_sigframe(ka, regs, sizeof(*frame));
543
544 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
545 goto give_sigsegv;
546
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100547 err |= __put_user((ed && ed->signal_invmap && sig < 32
548 ? ed->signal_invmap[sig] : sig), &frame->sig);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
550 err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
551 err |= copy_siginfo_to_user32(&frame->info, info);
552 if (err)
553 goto give_sigsegv;
554
555 /* Create the ucontext. */
556 err |= __put_user(0, &frame->uc.uc_flags);
557 err |= __put_user(0, &frame->uc.uc_link);
558 err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100559 err |= __put_user(sas_ss_flags(regs->sp),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 &frame->uc.uc_stack.ss_flags);
561 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
562 err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100563 regs, set->sig[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
565 if (err)
566 goto give_sigsegv;
567
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100568 if (ka->sa.sa_flags & SA_RESTORER)
569 restorer = ka->sa.sa_restorer;
Roland McGrathaf65d642008-01-30 13:30:43 +0100570 else
571 restorer = VDSO32_SYMBOL(current->mm->context.vdso,
572 rt_sigreturn);
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100573 err |= __put_user(ptr_to_compat(restorer), &frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100575 /*
576 * Not actually used anymore, but left because some gdb
577 * versions need it.
578 */
579 err |= __copy_to_user(frame->retcode, &code, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 if (err)
581 goto give_sigsegv;
582
583 /* Set up registers for signal handler */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100584 regs->sp = (unsigned long) frame;
585 regs->ip = (unsigned long) ka->sa.sa_handler;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500587 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100588 regs->ax = sig;
589 regs->dx = (unsigned long) &frame->info;
590 regs->cx = (unsigned long) &frame->uc;
Albert Cahalana7aacdf2006-10-29 22:26:17 -0500591
Albert Cahalan8e3de532006-12-07 02:14:06 +0100592 /* Make -mregparm=3 work */
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100593 regs->ax = sig;
594 regs->dx = (unsigned long) &frame->info;
595 regs->cx = (unsigned long) &frame->uc;
Albert Cahalan8e3de532006-12-07 02:14:06 +0100596
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100597 asm volatile("movl %0,%%ds" :: "r" (__USER32_DS));
598 asm volatile("movl %0,%%es" :: "r" (__USER32_DS));
599
600 regs->cs = __USER32_CS;
601 regs->ss = __USER32_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
603 set_fs(USER_DS);
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100604 regs->flags &= ~TF_MASK;
Andi Kleenab2e0b42006-09-26 10:52:41 +0200605 if (test_thread_flag(TIF_SINGLESTEP))
606 ptrace_notify(SIGTRAP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608#if DEBUG_SIG
Thomas Gleixner99b9cdf2008-01-30 13:30:07 +0100609 printk(KERN_DEBUG "SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
H. Peter Anvin65ea5b02008-01-30 13:30:56 +0100610 current->comm, current->pid, frame, regs->ip, frame->pretcode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611#endif
612
Andi Kleen1d001df2006-09-26 10:52:26 +0200613 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615give_sigsegv:
616 force_sigsegv(sig, current);
Andi Kleen1d001df2006-09-26 10:52:26 +0200617 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618}