asmlinkage void do_rt_sigreturn32()

in kernel/signal32.c [175:259]


asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
{
	struct rt_signal_frame32 __user *sf;
	unsigned int psr, pc, npc, ufp;
	compat_uptr_t fpu_save;
	compat_uptr_t rwin_save;
	sigset_t set;
	int err, i;
	
	/* Always make any pending restarted system calls return -EINTR */
	current->restart_block.fn = do_no_restart_syscall;

	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 */
	if (invalid_frame_pointer(sf, sizeof(*sf)))
		goto segv;

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

	if (ufp & 0x7)
		goto segv;

	if (__get_user(pc, &sf->regs.pc) || 
	    __get_user(npc, &sf->regs.npc))
		goto segv;

	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);

	/* Prevent syscall restart.  */
	pt_regs_clear_syscall(regs);

	err |= __get_user(fpu_save, &sf->fpu_save);
	if (!err && fpu_save)
		err |= restore_fpu_state(regs, compat_ptr(fpu_save));
	err |= get_compat_sigset(&set, &sf->mask);
	err |= compat_restore_altstack(&sf->stack);
	if (err)
		goto segv;
		
	err |= __get_user(rwin_save, &sf->rwin_save);
	if (!err && rwin_save) {
		if (restore_rwin_state(compat_ptr(rwin_save)))
			goto segv;
	}

	set_current_blocked(&set);
	return;
segv:
	force_sig(SIGSEGV);
}