signal.c 9.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright (C) 2003 PathScale, Inc.
 * Licensed under the GPL
 */

#include "linux/stddef.h"
#include "linux/errno.h"
#include "linux/personality.h"
#include "linux/ptrace.h"
#include "asm/current.h"
#include "asm/uaccess.h"
#include "asm/sigcontext.h"
#include "asm/ptrace.h"
#include "asm/arch/ucontext.h"
#include "choose-mode.h"
#include "sysdep/ptrace.h"
#include "frame_kern.h"

#ifdef CONFIG_MODE_SKAS

#include "skas.h"

static int copy_sc_from_user_skas(struct pt_regs *regs,
A
Al Viro 已提交
24
                                 struct sigcontext __user *from)
L
Linus Torvalds 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
{
       int err = 0;

#define GETREG(regs, regno, sc, regname) \
       __get_user((regs)->regs.skas.regs[(regno) / sizeof(unsigned long)], \
                  &(sc)->regname)

       err |= GETREG(regs, R8, from, r8);
       err |= GETREG(regs, R9, from, r9);
       err |= GETREG(regs, R10, from, r10);
       err |= GETREG(regs, R11, from, r11);
       err |= GETREG(regs, R12, from, r12);
       err |= GETREG(regs, R13, from, r13);
       err |= GETREG(regs, R14, from, r14);
       err |= GETREG(regs, R15, from, r15);
       err |= GETREG(regs, RDI, from, rdi);
       err |= GETREG(regs, RSI, from, rsi);
       err |= GETREG(regs, RBP, from, rbp);
       err |= GETREG(regs, RBX, from, rbx);
       err |= GETREG(regs, RDX, from, rdx);
       err |= GETREG(regs, RAX, from, rax);
       err |= GETREG(regs, RCX, from, rcx);
       err |= GETREG(regs, RSP, from, rsp);
       err |= GETREG(regs, RIP, from, rip);
       err |= GETREG(regs, EFLAGS, from, eflags);
       err |= GETREG(regs, CS, from, cs);

#undef GETREG

J
Jeff Dike 已提交
54
       return err;
L
Linus Torvalds 已提交
55 56
}

A
Al Viro 已提交
57 58
int copy_sc_to_user_skas(struct sigcontext __user *to,
			 struct _fpstate __user *to_fp,
59 60
			 struct pt_regs *regs, unsigned long mask,
			 unsigned long sp)
L
Linus Torvalds 已提交
61
{
62
        struct faultinfo * fi = &current->thread.arch.faultinfo;
L
Linus Torvalds 已提交
63 64 65 66 67 68 69 70 71 72 73 74
	int err = 0;

	err |= __put_user(0, &to->gs);
	err |= __put_user(0, &to->fs);

#define PUTREG(regs, regno, sc, regname) \
       __put_user((regs)->regs.skas.regs[(regno) / sizeof(unsigned long)], \
                  &(sc)->regname)

	err |= PUTREG(regs, RDI, to, rdi);
	err |= PUTREG(regs, RSI, to, rsi);
	err |= PUTREG(regs, RBP, to, rbp);
75 76 77 78 79
        /* Must use orignal RSP, which is passed in, rather than what's in
         * the pt_regs, because that's already been updated to point at the
         * signal frame.
         */
	err |= __put_user(sp, &to->rsp);
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92
	err |= PUTREG(regs, RBX, to, rbx);
	err |= PUTREG(regs, RDX, to, rdx);
	err |= PUTREG(regs, RCX, to, rcx);
	err |= PUTREG(regs, RAX, to, rax);
	err |= PUTREG(regs, R8, to, r8);
	err |= PUTREG(regs, R9, to, r9);
	err |= PUTREG(regs, R10, to, r10);
	err |= PUTREG(regs, R11, to, r11);
	err |= PUTREG(regs, R12, to, r12);
	err |= PUTREG(regs, R13, to, r13);
	err |= PUTREG(regs, R14, to, r14);
	err |= PUTREG(regs, R15, to, r15);
	err |= PUTREG(regs, CS, to, cs); /* XXX x86_64 doesn't do this */
93 94 95 96 97

        err |= __put_user(fi->cr2, &to->cr2);
        err |= __put_user(fi->error_code, &to->err);
        err |= __put_user(fi->trap_no, &to->trapno);

L
Linus Torvalds 已提交
98 99 100 101 102 103 104 105 106 107 108 109
	err |= PUTREG(regs, RIP, to, rip);
	err |= PUTREG(regs, EFLAGS, to, eflags);
#undef PUTREG

	err |= __put_user(mask, &to->oldmask);

	return(err);
}

#endif

#ifdef CONFIG_MODE_TT
A
Al Viro 已提交
110
int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext __user *from,
111
			 int fpsize)
L
Linus Torvalds 已提交
112
{
A
Al Viro 已提交
113 114
	struct _fpstate *to_fp;
	struct _fpstate __user *from_fp;
115 116 117 118 119 120 121 122 123 124 125 126
	unsigned long sigs;
	int err;

	to_fp = to->fpstate;
	sigs = to->oldmask;
	err = copy_from_user(to, from, sizeof(*to));
	from_fp = to->fpstate;
	to->fpstate = to_fp;
	to->oldmask = sigs;
	if(to_fp != NULL)
		err |= copy_from_user(to_fp, from_fp, fpsize);
	return(err);
L
Linus Torvalds 已提交
127 128
}

A
Al Viro 已提交
129
int copy_sc_to_user_tt(struct sigcontext __user *to, struct _fpstate __user *fp,
130
		       struct sigcontext *from, int fpsize, unsigned long sp)
L
Linus Torvalds 已提交
131
{
A
Al Viro 已提交
132 133
	struct _fpstate __user *to_fp;
	struct _fpstate *from_fp;
134 135
	int err;

A
Al Viro 已提交
136
	to_fp = (fp ? fp : (struct _fpstate __user *) (to + 1));
137 138
	from_fp = from->fpstate;
	err = copy_to_user(to, from, sizeof(*to));
139 140 141 142
	/* The SP in the sigcontext is the updated one for the signal
	 * delivery.  The sp passed in is the original, and this needs
	 * to be restored, so we stick it in separately.
	 */
143
	err |= copy_to_user(&SC_SP(to), &sp, sizeof(sp));
144

145 146 147 148
	if(from_fp != NULL){
		err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate));
		err |= copy_to_user(to_fp, from_fp, fpsize);
	}
149
	return err;
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163
}

#endif

static int copy_sc_from_user(struct pt_regs *to, void __user *from)
{
       int ret;

       ret = CHOOSE_MODE(copy_sc_from_user_tt(UPT_SC(&to->regs), from,
                                              sizeof(struct _fpstate)),
                         copy_sc_from_user_skas(to, from));
       return(ret);
}

A
Al Viro 已提交
164 165
static int copy_sc_to_user(struct sigcontext __user *to,
			   struct _fpstate __user *fp,
166 167
			   struct pt_regs *from, unsigned long mask,
			   unsigned long sp)
L
Linus Torvalds 已提交
168 169
{
       return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs),
170 171
                                             sizeof(*fp), sp),
                          copy_sc_to_user_skas(to, fp, from, mask, sp)));
L
Linus Torvalds 已提交
172 173 174 175
}

struct rt_sigframe
{
A
Al Viro 已提交
176
       char __user *pretcode;
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185 186 187 188
       struct ucontext uc;
       struct siginfo info;
};

#define round_down(m, n) (((m) / (n)) * (n))

int setup_signal_stack_si(unsigned long stack_top, int sig,
			  struct k_sigaction *ka, struct pt_regs * regs,
			  siginfo_t *info, sigset_t *set)
{
	struct rt_sigframe __user *frame;
	struct _fpstate __user *fp = NULL;
189
	unsigned long save_sp = PT_REGS_RSP(regs);
L
Linus Torvalds 已提交
190 191 192 193
	int err = 0;
	struct task_struct *me = current;

	frame = (struct rt_sigframe __user *)
194 195 196
		round_down(stack_top - sizeof(struct rt_sigframe), 16);
	/* Subtract 128 for a red zone and 8 for proper alignment */
        frame = (struct rt_sigframe __user *) ((unsigned long) frame - 128 - 8);
L
Linus Torvalds 已提交
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

	if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate)))
		goto out;

#if 0 /* XXX */
	if (save_i387(fp) < 0)
		err |= -1;
#endif
	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
		goto out;

	if (ka->sa.sa_flags & SA_SIGINFO) {
		err |= copy_siginfo_to_user(&frame->info, info);
		if (err)
			goto out;
	}

214 215 216 217 218 219 220 221 222 223
	/* Update SP now because the page fault handler refuses to extend
	 * the stack if the faulting address is too far below the current
	 * SP, which frame now certainly is.  If there's an error, the original
	 * value is restored on the way out.
	 * When writing the sigcontext to the stack, we have to write the
	 * original value, so that's passed to copy_sc_to_user, which does
	 * the right thing with it.
	 */
	PT_REGS_RSP(regs) = (unsigned long) frame;

L
Linus Torvalds 已提交
224 225 226 227
	/* Create the ucontext.  */
	err |= __put_user(0, &frame->uc.uc_flags);
	err |= __put_user(0, &frame->uc.uc_link);
	err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
228
	err |= __put_user(sas_ss_flags(save_sp),
L
Linus Torvalds 已提交
229 230
			  &frame->uc.uc_stack.ss_flags);
	err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size);
231 232
	err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0],
		save_sp);
L
Linus Torvalds 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
	err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate);
	if (sizeof(*set) == 16) {
		__put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
		__put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
	}
	else
		err |= __copy_to_user(&frame->uc.uc_sigmask, set,
				      sizeof(*set));

	/* Set up to return from userspace.  If provided, use a stub
	   already in userspace.  */
	/* x86-64 should always use SA_RESTORER. */
	if (ka->sa.sa_flags & SA_RESTORER)
		err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
	else
		/* could use a vstub here */
249
		goto restore_sp;
L
Linus Torvalds 已提交
250 251

	if (err)
252
		goto restore_sp;
L
Linus Torvalds 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

	/* Set up registers for signal handler */
	{
		struct exec_domain *ed = current_thread_info()->exec_domain;
		if (unlikely(ed && ed->signal_invmap && sig < 32))
			sig = ed->signal_invmap[sig];
	}

	PT_REGS_RDI(regs) = sig;
	/* In case the signal handler was declared without prototypes */
	PT_REGS_RAX(regs) = 0;

	/* This also works for non SA_SIGINFO handlers because they expect the
	   next argument after the signal number on the stack. */
	PT_REGS_RSI(regs) = (unsigned long) &frame->info;
	PT_REGS_RDX(regs) = (unsigned long) &frame->uc;
	PT_REGS_RIP(regs) = (unsigned long) ka->sa.sa_handler;
 out:
271 272 273 274 275
	return err;

restore_sp:
	PT_REGS_RSP(regs) = save_sp;
	return err;
L
Linus Torvalds 已提交
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
}

long sys_rt_sigreturn(struct pt_regs *regs)
{
	unsigned long sp = PT_REGS_SP(&current->thread.regs);
	struct rt_sigframe __user *frame =
		(struct rt_sigframe __user *)(sp - 8);
	struct ucontext __user *uc = &frame->uc;
	sigset_t set;

	if(copy_from_user(&set, &uc->uc_sigmask, sizeof(set)))
		goto segfault;

	sigdelsetmask(&set, ~_BLOCKABLE);

	spin_lock_irq(&current->sighand->siglock);
	current->blocked = set;
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	if(copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext))
		goto segfault;

	/* Avoid ERESTART handling */
	PT_REGS_SYSCALL_NR(&current->thread.regs) = -1;
	return(PT_REGS_SYSCALL_RET(&current->thread.regs));

 segfault:
	force_sig(SIGSEGV, current);
	return 0;
}
/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-file-style: "linux"
 * End:
 */