signal32.c 22 KB
Newer Older
1
/*  arch/sparc64/kernel/signal32.c
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
 *  Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
 *  Copyright (C) 1997 Eddie C. Dost   (ecd@skynet.be)
 *  Copyright (C) 1997,1998 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
 */

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/binfmts.h>
#include <linux/compat.h>
#include <linux/bitops.h>
22
#include <linux/tracehook.h>
Linus Torvalds's avatar
Linus Torvalds committed
23

24
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
25 26 27 28 29
#include <asm/ptrace.h>
#include <asm/pgtable.h>
#include <asm/psrcompat.h>
#include <asm/fpumacro.h>
#include <asm/visasm.h>
30
#include <asm/compat_signal.h>
31
#include <asm/switch_to.h>
Linus Torvalds's avatar
Linus Torvalds committed
32

33
#include "sigutil.h"
34
#include "kernel.h"
35

Linus Torvalds's avatar
Linus Torvalds committed
36 37 38 39 40 41 42 43 44 45
/* This magic should be in g_upper[0] for all upper parts
 * to be valid.
 */
#define SIGINFO_EXTRA_V8PLUS_MAGIC	0x130e269
typedef struct {
	unsigned int g_upper[8];
	unsigned int o_upper[8];
	unsigned int asi;
} siginfo_extra_v8plus_t;

46
struct signal_frame32 {
Linus Torvalds's avatar
Linus Torvalds committed
47 48
	struct sparc_stackf32	ss;
	__siginfo32_t		info;
49
	/* __siginfo_fpu_t * */ u32 fpu_save;
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53 54
	unsigned int		insns[2];
	unsigned int		extramask[_COMPAT_NSIG_WORDS - 1];
	unsigned int		extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */
	/* Only valid if (info.si_regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */
	siginfo_extra_v8plus_t	v8plus;
55 56
	/* __siginfo_rwin_t * */u32 rwin_save;
} __attribute__((aligned(8)));
Linus Torvalds's avatar
Linus Torvalds committed
57 58 59 60 61 62

struct rt_signal_frame32 {
	struct sparc_stackf32	ss;
	compat_siginfo_t	info;
	struct pt_regs32	regs;
	compat_sigset_t		mask;
63
	/* __siginfo_fpu_t * */ u32 fpu_save;
Linus Torvalds's avatar
Linus Torvalds committed
64
	unsigned int		insns[2];
65
	compat_stack_t		stack;
Linus Torvalds's avatar
Linus Torvalds committed
66 67 68
	unsigned int		extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */
	/* Only valid if (regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */
	siginfo_extra_v8plus_t	v8plus;
69 70
	/* __siginfo_rwin_t * */u32 rwin_save;
} __attribute__((aligned(8)));
Linus Torvalds's avatar
Linus Torvalds committed
71

72
int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from)
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
{
	int err;

	if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
		return -EFAULT;

	/* If you change siginfo_t structure, please be sure
	   this code is fixed accordingly.
	   It should never copy any pad contained in the structure
	   to avoid security leaks, but must copy the generic
	   3 ints plus the relevant union member.
	   This routine must convert siginfo from 64bit to 32bit as well
	   at the same time.  */
	err = __put_user(from->si_signo, &to->si_signo);
	err |= __put_user(from->si_errno, &to->si_errno);
88
	err |= __put_user(from->si_code, &to->si_code);
Linus Torvalds's avatar
Linus Torvalds committed
89 90 91
	if (from->si_code < 0)
		err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
	else {
92 93
		switch (siginfo_layout(from->si_signo, from->si_code)) {
		case SIL_TIMER:
Linus Torvalds's avatar
Linus Torvalds committed
94 95 96 97
			err |= __put_user(from->si_tid, &to->si_tid);
			err |= __put_user(from->si_overrun, &to->si_overrun);
			err |= __put_user(from->si_int, &to->si_int);
			break;
98
		case SIL_CHLD:
Linus Torvalds's avatar
Linus Torvalds committed
99 100 101 102
			err |= __put_user(from->si_utime, &to->si_utime);
			err |= __put_user(from->si_stime, &to->si_stime);
			err |= __put_user(from->si_status, &to->si_status);
		default:
103
		case SIL_KILL:
Linus Torvalds's avatar
Linus Torvalds committed
104 105 106
			err |= __put_user(from->si_pid, &to->si_pid);
			err |= __put_user(from->si_uid, &to->si_uid);
			break;
107
		case SIL_FAULT:
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110
			err |= __put_user(from->si_trapno, &to->si_trapno);
			err |= __put_user((unsigned long)from->si_addr, &to->si_addr);
			break;
111
		case SIL_POLL:
112 113 114
			err |= __put_user(from->si_band, &to->si_band);
			err |= __put_user(from->si_fd, &to->si_fd);
			break;
115
		case SIL_RT:
Linus Torvalds's avatar
Linus Torvalds committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
			err |= __put_user(from->si_pid, &to->si_pid);
			err |= __put_user(from->si_uid, &to->si_uid);
			err |= __put_user(from->si_int, &to->si_int);
			break;
		}
	}
	return err;
}

/* CAUTION: This is just a very minimalist implementation for the
 *          sake of compat_sys_rt_sigqueueinfo()
 */
int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
{
	if (!access_ok(VERIFY_WRITE, from, sizeof(compat_siginfo_t)))
		return -EFAULT;

	if (copy_from_user(to, from, 3*sizeof(int)) ||
	    copy_from_user(to->_sifields._pad, from->_sifields._pad,
			   SI_PAD_SIZE))
		return -EFAULT;

	return 0;
}

141 142 143 144 145 146 147 148 149 150 151 152
/* Checks if the fp is valid.  We always build signal frames which are
 * 16-byte aligned, therefore we can always enforce that the restore
 * frame has that property as well.
 */
static bool invalid_frame_pointer(void __user *fp, int fplen)
{
	if ((((unsigned long) fp) & 15) ||
	    ((unsigned long)fp) > 0x100000000ULL - fplen)
		return true;
	return false;
}

153
void do_sigreturn32(struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
154
{
155
	struct signal_frame32 __user *sf;
156 157
	compat_uptr_t fpu_save;
	compat_uptr_t rwin_save;
158
	unsigned int psr, ufp;
159
	unsigned int pc, npc;
Linus Torvalds's avatar
Linus Torvalds committed
160
	sigset_t set;
161
	compat_sigset_t seta;
Linus Torvalds's avatar
Linus Torvalds committed
162 163
	int err, i;
	
164
	/* Always make any pending restarted system calls return -EINTR */
165
	current->restart_block.fn = do_no_restart_syscall;
166 167 168

	synchronize_user_stack();

Linus Torvalds's avatar
Linus Torvalds committed
169
	regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
170
	sf = (struct signal_frame32 __user *) regs->u_regs[UREG_FP];
Linus Torvalds's avatar
Linus Torvalds committed
171 172

	/* 1. Make sure we are not getting garbage from the user */
173 174 175 176 177 178 179
	if (invalid_frame_pointer(sf, sizeof(*sf)))
		goto segv;

	if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
		goto segv;

	if (ufp & 0x7)
Linus Torvalds's avatar
Linus Torvalds committed
180 181
		goto segv;

182
	if (__get_user(pc, &sf->info.si_regs.pc) ||
183 184
	    __get_user(npc, &sf->info.si_regs.npc))
		goto segv;
Linus Torvalds's avatar
Linus Torvalds committed
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

	if ((pc | npc) & 3)
		goto segv;

	if (test_thread_flag(TIF_32BIT)) {
		pc &= 0xffffffff;
		npc &= 0xffffffff;
	}
	regs->tpc = pc;
	regs->tnpc = npc;

	/* 2. Restore the state */
	err = __get_user(regs->y, &sf->info.si_regs.y);
	err |= __get_user(psr, &sf->info.si_regs.psr);

	for (i = UREG_G1; i <= UREG_I7; i++)
		err |= __get_user(regs->u_regs[i], &sf->info.si_regs.u_regs[i]);
	if ((psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS) {
		err |= __get_user(i, &sf->v8plus.g_upper[0]);
		if (i == SIGINFO_EXTRA_V8PLUS_MAGIC) {
			unsigned long asi;

			for (i = UREG_G1; i <= UREG_I7; i++)
				err |= __get_user(((u32 *)regs->u_regs)[2*i], &sf->v8plus.g_upper[i]);
			err |= __get_user(asi, &sf->v8plus.asi);
			regs->tstate &= ~TSTATE_ASI;
			regs->tstate |= ((asi & 0xffUL) << 24UL);
		}
	}

	/* User can only change condition codes in %tstate. */
	regs->tstate &= ~(TSTATE_ICC|TSTATE_XCC);
	regs->tstate |= psr_to_tstate_icc(psr);

219
	/* Prevent syscall restart.  */
220
	pt_regs_clear_syscall(regs);
221

Linus Torvalds's avatar
Linus Torvalds committed
222
	err |= __get_user(fpu_save, &sf->fpu_save);
223 224 225 226 227 228 229
	if (!err && fpu_save)
		err |= restore_fpu_state(regs, compat_ptr(fpu_save));
	err |= __get_user(rwin_save, &sf->rwin_save);
	if (!err && rwin_save) {
		if (restore_rwin_state(compat_ptr(rwin_save)))
			goto segv;
	}
230 231
	err |= __get_user(seta.sig[0], &sf->info.si_mask);
	err |= copy_from_user(&seta.sig[1], &sf->extramask,
Linus Torvalds's avatar
Linus Torvalds committed
232 233 234
			      (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int));
	if (err)
	    	goto segv;
235 236

	set.sig[0] = seta.sig[0] + (((long)seta.sig[1]) << 32);
237
	set_current_blocked(&set);
Linus Torvalds's avatar
Linus Torvalds committed
238 239 240 241 242 243 244 245 246
	return;

segv:
	force_sig(SIGSEGV, current);
}

asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
{
	struct rt_signal_frame32 __user *sf;
247
	unsigned int psr, pc, npc, ufp;
248 249
	compat_uptr_t fpu_save;
	compat_uptr_t rwin_save;
Linus Torvalds's avatar
Linus Torvalds committed
250 251 252 253 254
	sigset_t set;
	compat_sigset_t seta;
	int err, i;
	
	/* Always make any pending restarted system calls return -EINTR */
255
	current->restart_block.fn = do_no_restart_syscall;
Linus Torvalds's avatar
Linus Torvalds committed
256 257 258 259 260 261

	synchronize_user_stack();
	regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
	sf = (struct rt_signal_frame32 __user *) regs->u_regs[UREG_FP];

	/* 1. Make sure we are not getting garbage from the user */
262
	if (invalid_frame_pointer(sf, sizeof(*sf)))
Linus Torvalds's avatar
Linus Torvalds committed
263 264
		goto segv;

265 266 267 268 269 270 271
	if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
		goto segv;

	if (ufp & 0x7)
		goto segv;

	if (__get_user(pc, &sf->regs.pc) || 
272 273
	    __get_user(npc, &sf->regs.npc))
		goto segv;
Linus Torvalds's avatar
Linus Torvalds committed
274 275 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

	if ((pc | npc) & 3)
		goto segv;

	if (test_thread_flag(TIF_32BIT)) {
		pc &= 0xffffffff;
		npc &= 0xffffffff;
	}
	regs->tpc = pc;
	regs->tnpc = npc;

	/* 2. Restore the state */
	err = __get_user(regs->y, &sf->regs.y);
	err |= __get_user(psr, &sf->regs.psr);
	
	for (i = UREG_G1; i <= UREG_I7; i++)
		err |= __get_user(regs->u_regs[i], &sf->regs.u_regs[i]);
	if ((psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS) {
		err |= __get_user(i, &sf->v8plus.g_upper[0]);
		if (i == SIGINFO_EXTRA_V8PLUS_MAGIC) {
			unsigned long asi;

			for (i = UREG_G1; i <= UREG_I7; i++)
				err |= __get_user(((u32 *)regs->u_regs)[2*i], &sf->v8plus.g_upper[i]);
			err |= __get_user(asi, &sf->v8plus.asi);
			regs->tstate &= ~TSTATE_ASI;
			regs->tstate |= ((asi & 0xffUL) << 24UL);
		}
	}

	/* User can only change condition codes in %tstate. */
	regs->tstate &= ~(TSTATE_ICC|TSTATE_XCC);
	regs->tstate |= psr_to_tstate_icc(psr);

308
	/* Prevent syscall restart.  */
309
	pt_regs_clear_syscall(regs);
310

Linus Torvalds's avatar
Linus Torvalds committed
311
	err |= __get_user(fpu_save, &sf->fpu_save);
312 313
	if (!err && fpu_save)
		err |= restore_fpu_state(regs, compat_ptr(fpu_save));
Linus Torvalds's avatar
Linus Torvalds committed
314
	err |= copy_from_user(&seta, &sf->mask, sizeof(compat_sigset_t));
315
	err |= compat_restore_altstack(&sf->stack);
Linus Torvalds's avatar
Linus Torvalds committed
316 317 318
	if (err)
		goto segv;
		
319 320 321 322 323 324
	err |= __get_user(rwin_save, &sf->rwin_save);
	if (!err && rwin_save) {
		if (restore_rwin_state(compat_ptr(rwin_save)))
			goto segv;
	}

325
	set.sig[0] = seta.sig[0] + (((long)seta.sig[1]) << 32);
326
	set_current_blocked(&set);
Linus Torvalds's avatar
Linus Torvalds committed
327 328 329 330 331
	return;
segv:
	force_sig(SIGSEGV, current);
}

Al Viro's avatar
Al Viro committed
332
static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize)
Linus Torvalds's avatar
Linus Torvalds committed
333 334 335 336 337 338
{
	unsigned long sp;
	
	regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
	sp = regs->u_regs[UREG_FP];
	
339 340 341 342 343 344 345
	/*
	 * If we are on the alternate signal stack and would overflow it, don't.
	 * Return an always-bogus address instead so we will die with SIGSEGV.
	 */
	if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize)))
		return (void __user *) -1L;

Linus Torvalds's avatar
Linus Torvalds committed
346
	/* This is the X/Open sanctioned signal stack switching.  */
Al Viro's avatar
Al Viro committed
347
	sp = sigsp(sp, ksig) - framesize;
348

349 350 351 352 353 354
	/* Always align the stack frame.  This handles two cases.  First,
	 * sigaltstack need not be mindful of platform specific stack
	 * alignment.  Second, if we took this signal because the stack
	 * is not aligned properly, we'd like to take the signal cleanly
	 * and report that.
	 */
355
	sp &= ~15UL;
356

357
	return (void __user *) sp;
Linus Torvalds's avatar
Linus Torvalds committed
358 359
}

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
/* The I-cache flush instruction only works in the primary ASI, which
 * right now is the nucleus, aka. kernel space.
 *
 * Therefore we have to kick the instructions out using the kernel
 * side linear mapping of the physical address backing the user
 * instructions.
 */
static void flush_signal_insns(unsigned long address)
{
	unsigned long pstate, paddr;
	pte_t *ptep, pte;
	pgd_t *pgdp;
	pud_t *pudp;
	pmd_t *pmdp;

	/* Commit all stores of the instructions we are about to flush.  */
	wmb();

	/* Disable cross-call reception.  In this way even a very wide
	 * munmap() on another cpu can't tear down the page table
	 * hierarchy from underneath us, since that can't complete
	 * until the IPI tlb flush returns.
	 */

	__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
	__asm__ __volatile__("wrpr %0, %1, %%pstate"
				: : "r" (pstate), "i" (PSTATE_IE));

	pgdp = pgd_offset(current->mm, address);
	if (pgd_none(*pgdp))
		goto out_irqs_on;
	pudp = pud_offset(pgdp, address);
	if (pud_none(*pudp))
		goto out_irqs_on;
	pmdp = pmd_offset(pudp, address);
	if (pmd_none(*pmdp))
		goto out_irqs_on;

	ptep = pte_offset_map(pmdp, address);
	pte = *ptep;
	if (!pte_present(pte))
		goto out_unmap;

	paddr = (unsigned long) page_address(pte_page(pte));

	__asm__ __volatile__("flush	%0 + %1"
			     : /* no outputs */
			     : "r" (paddr),
			       "r" (address & (PAGE_SIZE - 1))
			     : "memory");

out_unmap:
	pte_unmap(ptep);
out_irqs_on:
	__asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));

}

Al Viro's avatar
Al Viro committed
418 419
static int setup_frame32(struct ksignal *ksig, struct pt_regs *regs,
			 sigset_t *oldset)
Linus Torvalds's avatar
Linus Torvalds committed
420
{
421
	struct signal_frame32 __user *sf;
422 423
	int i, err, wsaved;
	void __user *tail;
Linus Torvalds's avatar
Linus Torvalds committed
424 425
	int sigframe_size;
	u32 psr;
426
	compat_sigset_t seta;
Linus Torvalds's avatar
Linus Torvalds committed
427 428 429 430 431

	/* 1. Make sure everything is clean */
	synchronize_user_stack();
	save_and_clear_fpu();
	
432 433 434 435 436 437 438
	wsaved = get_thread_wsaved();

	sigframe_size = sizeof(*sf);
	if (current_thread_info()->fpsaved[0] & FPRS_FEF)
		sigframe_size += sizeof(__siginfo_fpu_t);
	if (wsaved)
		sigframe_size += sizeof(__siginfo_rwin_t);
Linus Torvalds's avatar
Linus Torvalds committed
439

440
	sf = (struct signal_frame32 __user *)
Al Viro's avatar
Al Viro committed
441
		get_sigframe(ksig, regs, sigframe_size);
Linus Torvalds's avatar
Linus Torvalds committed
442
	
Al Viro's avatar
Al Viro committed
443 444 445 446
	if (invalid_frame_pointer(sf, sigframe_size)) {
		do_exit(SIGILL);
		return -EINVAL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
447

448
	tail = (sf + 1);
Linus Torvalds's avatar
Linus Torvalds committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472

	/* 2. Save the current process state */
	if (test_thread_flag(TIF_32BIT)) {
		regs->tpc &= 0xffffffff;
		regs->tnpc &= 0xffffffff;
	}
	err  = put_user(regs->tpc, &sf->info.si_regs.pc);
	err |= __put_user(regs->tnpc, &sf->info.si_regs.npc);
	err |= __put_user(regs->y, &sf->info.si_regs.y);
	psr = tstate_to_psr(regs->tstate);
	if (current_thread_info()->fpsaved[0] & FPRS_FEF)
		psr |= PSR_EF;
	err |= __put_user(psr, &sf->info.si_regs.psr);
	for (i = 0; i < 16; i++)
		err |= __put_user(regs->u_regs[i], &sf->info.si_regs.u_regs[i]);
	err |= __put_user(sizeof(siginfo_extra_v8plus_t), &sf->extra_size);
	err |= __put_user(SIGINFO_EXTRA_V8PLUS_MAGIC, &sf->v8plus.g_upper[0]);
	for (i = 1; i < 16; i++)
		err |= __put_user(((u32 *)regs->u_regs)[2*i],
				  &sf->v8plus.g_upper[i]);
	err |= __put_user((regs->tstate & TSTATE_ASI) >> 24UL,
			  &sf->v8plus.asi);

	if (psr & PSR_EF) {
473 474 475 476
		__siginfo_fpu_t __user *fp = tail;
		tail += sizeof(*fp);
		err |= save_fpu_state(regs, fp);
		err |= __put_user((u64)fp, &sf->fpu_save);
Linus Torvalds's avatar
Linus Torvalds committed
477 478 479
	} else {
		err |= __put_user(0, &sf->fpu_save);
	}
480 481 482 483 484 485 486 487 488
	if (wsaved) {
		__siginfo_rwin_t __user *rwp = tail;
		tail += sizeof(*rwp);
		err |= save_rwin_state(wsaved, rwp);
		err |= __put_user((u64)rwp, &sf->rwin_save);
		set_thread_wsaved(0);
	} else {
		err |= __put_user(0, &sf->rwin_save);
	}
Linus Torvalds's avatar
Linus Torvalds committed
489

490 491 492 493 494 495 496 497
	/* If these change we need to know - assignments to seta relies on these sizes */
	BUILD_BUG_ON(_NSIG_WORDS != 1);
	BUILD_BUG_ON(_COMPAT_NSIG_WORDS != 2);
	seta.sig[1] = (oldset->sig[0] >> 32);
	seta.sig[0] = oldset->sig[0];

	err |= __put_user(seta.sig[0], &sf->info.si_mask);
	err |= __copy_to_user(sf->extramask, &seta.sig[1],
Linus Torvalds's avatar
Linus Torvalds committed
498 499
			      (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int));

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
	if (!wsaved) {
		err |= copy_in_user((u32 __user *)sf,
				    (u32 __user *)(regs->u_regs[UREG_FP]),
				    sizeof(struct reg_window32));
	} else {
		struct reg_window *rp;

		rp = &current_thread_info()->reg_window[wsaved - 1];
		for (i = 0; i < 8; i++)
			err |= __put_user(rp->locals[i], &sf->ss.locals[i]);
		for (i = 0; i < 6; i++)
			err |= __put_user(rp->ins[i], &sf->ss.ins[i]);
		err |= __put_user(rp->ins[6], &sf->ss.fp);
		err |= __put_user(rp->ins[7], &sf->ss.callers_pc);
	}	
Linus Torvalds's avatar
Linus Torvalds committed
515
	if (err)
Al Viro's avatar
Al Viro committed
516
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
517 518 519

	/* 3. signal handler back-trampoline and parameters */
	regs->u_regs[UREG_FP] = (unsigned long) sf;
Al Viro's avatar
Al Viro committed
520
	regs->u_regs[UREG_I0] = ksig->sig;
Linus Torvalds's avatar
Linus Torvalds committed
521 522 523 524
	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
	regs->u_regs[UREG_I2] = (unsigned long) &sf->info;

	/* 4. signal handler */
Al Viro's avatar
Al Viro committed
525
	regs->tpc = (unsigned long) ksig->ka.sa.sa_handler;
Linus Torvalds's avatar
Linus Torvalds committed
526 527 528 529 530 531 532
	regs->tnpc = (regs->tpc + 4);
	if (test_thread_flag(TIF_32BIT)) {
		regs->tpc &= 0xffffffff;
		regs->tnpc &= 0xffffffff;
	}

	/* 5. return to kernel instructions */
Al Viro's avatar
Al Viro committed
533 534
	if (ksig->ka.ka_restorer) {
		regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer;
Linus Torvalds's avatar
Linus Torvalds committed
535 536 537 538 539 540 541 542
	} else {
		unsigned long address = ((unsigned long)&(sf->insns[0]));

		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
	
		err  = __put_user(0x821020d8, &sf->insns[0]); /*mov __NR_sigreturn, %g1*/
		err |= __put_user(0x91d02010, &sf->insns[1]); /*t 0x10*/
		if (err)
Al Viro's avatar
Al Viro committed
543
			return err;
544
		flush_signal_insns(address);
Linus Torvalds's avatar
Linus Torvalds committed
545
	}
546
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
547 548
}

Al Viro's avatar
Al Viro committed
549 550
static int setup_rt_frame32(struct ksignal *ksig, struct pt_regs *regs,
			    sigset_t *oldset)
Linus Torvalds's avatar
Linus Torvalds committed
551 552
{
	struct rt_signal_frame32 __user *sf;
553 554
	int i, err, wsaved;
	void __user *tail;
Linus Torvalds's avatar
Linus Torvalds committed
555 556 557 558 559 560 561 562
	int sigframe_size;
	u32 psr;
	compat_sigset_t seta;

	/* 1. Make sure everything is clean */
	synchronize_user_stack();
	save_and_clear_fpu();
	
563 564 565 566 567 568 569
	wsaved = get_thread_wsaved();

	sigframe_size = sizeof(*sf);
	if (current_thread_info()->fpsaved[0] & FPRS_FEF)
		sigframe_size += sizeof(__siginfo_fpu_t);
	if (wsaved)
		sigframe_size += sizeof(__siginfo_rwin_t);
Linus Torvalds's avatar
Linus Torvalds committed
570 571

	sf = (struct rt_signal_frame32 __user *)
Al Viro's avatar
Al Viro committed
572
		get_sigframe(ksig, regs, sigframe_size);
Linus Torvalds's avatar
Linus Torvalds committed
573
	
Al Viro's avatar
Al Viro committed
574 575 576 577
	if (invalid_frame_pointer(sf, sigframe_size)) {
		do_exit(SIGILL);
		return -EINVAL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
578

579
	tail = (sf + 1);
Linus Torvalds's avatar
Linus Torvalds committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

	/* 2. Save the current process state */
	if (test_thread_flag(TIF_32BIT)) {
		regs->tpc &= 0xffffffff;
		regs->tnpc &= 0xffffffff;
	}
	err  = put_user(regs->tpc, &sf->regs.pc);
	err |= __put_user(regs->tnpc, &sf->regs.npc);
	err |= __put_user(regs->y, &sf->regs.y);
	psr = tstate_to_psr(regs->tstate);
	if (current_thread_info()->fpsaved[0] & FPRS_FEF)
		psr |= PSR_EF;
	err |= __put_user(psr, &sf->regs.psr);
	for (i = 0; i < 16; i++)
		err |= __put_user(regs->u_regs[i], &sf->regs.u_regs[i]);
	err |= __put_user(sizeof(siginfo_extra_v8plus_t), &sf->extra_size);
	err |= __put_user(SIGINFO_EXTRA_V8PLUS_MAGIC, &sf->v8plus.g_upper[0]);
	for (i = 1; i < 16; i++)
		err |= __put_user(((u32 *)regs->u_regs)[2*i],
				  &sf->v8plus.g_upper[i]);
	err |= __put_user((regs->tstate & TSTATE_ASI) >> 24UL,
			  &sf->v8plus.asi);

	if (psr & PSR_EF) {
604 605 606 607
		__siginfo_fpu_t __user *fp = tail;
		tail += sizeof(*fp);
		err |= save_fpu_state(regs, fp);
		err |= __put_user((u64)fp, &sf->fpu_save);
Linus Torvalds's avatar
Linus Torvalds committed
608 609 610
	} else {
		err |= __put_user(0, &sf->fpu_save);
	}
611 612 613 614 615 616 617 618 619
	if (wsaved) {
		__siginfo_rwin_t __user *rwp = tail;
		tail += sizeof(*rwp);
		err |= save_rwin_state(wsaved, rwp);
		err |= __put_user((u64)rwp, &sf->rwin_save);
		set_thread_wsaved(0);
	} else {
		err |= __put_user(0, &sf->rwin_save);
	}
Linus Torvalds's avatar
Linus Torvalds committed
620 621

	/* Update the siginfo structure.  */
Al Viro's avatar
Al Viro committed
622
	err |= copy_siginfo_to_user32(&sf->info, &ksig->info);
Linus Torvalds's avatar
Linus Torvalds committed
623 624
	
	/* Setup sigaltstack */
625
	err |= __compat_save_altstack(&sf->stack, regs->u_regs[UREG_FP]);
Linus Torvalds's avatar
Linus Torvalds committed
626

627 628
	seta.sig[1] = (oldset->sig[0] >> 32);
	seta.sig[0] = oldset->sig[0];
Linus Torvalds's avatar
Linus Torvalds committed
629 630
	err |= __copy_to_user(&sf->mask, &seta, sizeof(compat_sigset_t));

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
	if (!wsaved) {
		err |= copy_in_user((u32 __user *)sf,
				    (u32 __user *)(regs->u_regs[UREG_FP]),
				    sizeof(struct reg_window32));
	} else {
		struct reg_window *rp;

		rp = &current_thread_info()->reg_window[wsaved - 1];
		for (i = 0; i < 8; i++)
			err |= __put_user(rp->locals[i], &sf->ss.locals[i]);
		for (i = 0; i < 6; i++)
			err |= __put_user(rp->ins[i], &sf->ss.ins[i]);
		err |= __put_user(rp->ins[6], &sf->ss.fp);
		err |= __put_user(rp->ins[7], &sf->ss.callers_pc);
	}
Linus Torvalds's avatar
Linus Torvalds committed
646
	if (err)
Al Viro's avatar
Al Viro committed
647
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
648 649 650
	
	/* 3. signal handler back-trampoline and parameters */
	regs->u_regs[UREG_FP] = (unsigned long) sf;
Al Viro's avatar
Al Viro committed
651
	regs->u_regs[UREG_I0] = ksig->sig;
Linus Torvalds's avatar
Linus Torvalds committed
652 653 654 655
	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
	regs->u_regs[UREG_I2] = (unsigned long) &sf->regs;

	/* 4. signal handler */
Al Viro's avatar
Al Viro committed
656
	regs->tpc = (unsigned long) ksig->ka.sa.sa_handler;
Linus Torvalds's avatar
Linus Torvalds committed
657 658 659 660 661 662 663
	regs->tnpc = (regs->tpc + 4);
	if (test_thread_flag(TIF_32BIT)) {
		regs->tpc &= 0xffffffff;
		regs->tnpc &= 0xffffffff;
	}

	/* 5. return to kernel instructions */
Al Viro's avatar
Al Viro committed
664 665
	if (ksig->ka.ka_restorer)
		regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer;
Linus Torvalds's avatar
Linus Torvalds committed
666 667 668 669 670 671 672 673 674 675 676
	else {
		unsigned long address = ((unsigned long)&(sf->insns[0]));

		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
	
		/* mov __NR_rt_sigreturn, %g1 */
		err |= __put_user(0x82102065, &sf->insns[0]);

		/* t 0x10 */
		err |= __put_user(0x91d02010, &sf->insns[1]);
		if (err)
Al Viro's avatar
Al Viro committed
677
			return err;
Linus Torvalds's avatar
Linus Torvalds committed
678

679
		flush_signal_insns(address);
Linus Torvalds's avatar
Linus Torvalds committed
680
	}
681
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
682 683
}

Al Viro's avatar
Al Viro committed
684 685
static inline void handle_signal32(struct ksignal *ksig, 
				  struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
686
{
Al Viro's avatar
Al Viro committed
687
	sigset_t *oldset = sigmask_to_save();
688 689
	int err;

Al Viro's avatar
Al Viro committed
690 691
	if (ksig->ka.sa.sa_flags & SA_SIGINFO)
		err = setup_rt_frame32(ksig, regs, oldset);
692
	else
Al Viro's avatar
Al Viro committed
693
		err = setup_frame32(ksig, regs, oldset);
694

Al Viro's avatar
Al Viro committed
695
	signal_setup_done(err, ksig, 0);
Linus Torvalds's avatar
Linus Torvalds committed
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
}

static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs,
				     struct sigaction *sa)
{
	switch (regs->u_regs[UREG_I0]) {
	case ERESTART_RESTARTBLOCK:
	case ERESTARTNOHAND:
	no_system_call_restart:
		regs->u_regs[UREG_I0] = EINTR;
		regs->tstate |= TSTATE_ICARRY;
		break;
	case ERESTARTSYS:
		if (!(sa->sa_flags & SA_RESTART))
			goto no_system_call_restart;
		/* fallthrough */
	case ERESTARTNOINTR:
		regs->u_regs[UREG_I0] = orig_i0;
		regs->tpc -= 4;
		regs->tnpc -= 4;
	}
}

/* Note that 'init' is a special process: it doesn't get signals it doesn't
 * want to handle. Thus you cannot kill init even with a SIGKILL even by
 * mistake.
 */
723
void do_signal32(struct pt_regs * regs)
Linus Torvalds's avatar
Linus Torvalds committed
724
{
Al Viro's avatar
Al Viro committed
725 726 727 728
	struct ksignal ksig;
	unsigned long orig_i0 = 0;
	int restart_syscall = 0;
	bool has_handler = get_signal(&ksig);
729

730 731 732
	if (pt_regs_is_syscall(regs) &&
	    (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
		restart_syscall = 1;
733
		orig_i0 = regs->u_regs[UREG_G6];
734
	}
735

Al Viro's avatar
Al Viro committed
736
	if (has_handler) {
737
		if (restart_syscall)
Al Viro's avatar
Al Viro committed
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
			syscall_restart32(orig_i0, regs, &ksig.ka.sa);
		handle_signal32(&ksig, regs);
	} else {
		if (restart_syscall) {
			switch (regs->u_regs[UREG_I0]) {
			case ERESTARTNOHAND:
	     		case ERESTARTSYS:
			case ERESTARTNOINTR:
				/* replay the system call when we are done */
				regs->u_regs[UREG_I0] = orig_i0;
				regs->tpc -= 4;
				regs->tnpc -= 4;
				pt_regs_clear_syscall(regs);
			case ERESTART_RESTARTBLOCK:
				regs->u_regs[UREG_G1] = __NR_restart_syscall;
				regs->tpc -= 4;
				regs->tnpc -= 4;
				pt_regs_clear_syscall(regs);
			}
		}
		restore_saved_sigmask();
Linus Torvalds's avatar
Linus Torvalds committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
	}
}

struct sigstack32 {
	u32 the_stack;
	int cur_status;
};

asmlinkage int do_sys32_sigstack(u32 u_ssptr, u32 u_ossptr, unsigned long sp)
{
	struct sigstack32 __user *ssptr =
		(struct sigstack32 __user *)((unsigned long)(u_ssptr));
	struct sigstack32 __user *ossptr =
		(struct sigstack32 __user *)((unsigned long)(u_ossptr));
	int ret = -EFAULT;

	/* First see if old state is wanted. */
	if (ossptr) {
		if (put_user(current->sas_ss_sp + current->sas_ss_size,
			     &ossptr->the_stack) ||
		    __put_user(on_sig_stack(sp), &ossptr->cur_status))
			goto out;
	}
	
	/* Now see if we want to update the new state. */
	if (ssptr) {
		u32 ss_sp;

		if (get_user(ss_sp, &ssptr->the_stack))
			goto out;

		/* If the current stack was set with sigaltstack, don't
		 * swap stacks while we are on it.
		 */
		ret = -EPERM;
		if (current->sas_ss_sp && on_sig_stack(sp))
			goto out;
			
		/* Since we don't know the extent of the stack, and we don't
		 * track onstack-ness, but rather calculate it, we must
		 * presume a size.  Ho hum this interface is lossy.
		 */
		current->sas_ss_sp = (unsigned long)ss_sp - SIGSTKSZ;
		current->sas_ss_size = SIGSTKSZ;
	}
	
	ret = 0;
out:
	return ret;
}