static int setup_frame32()

in kernel/signal32.c [351:484]


static int setup_frame32(struct ksignal *ksig, struct pt_regs *regs,
			 sigset_t *oldset)
{
	struct signal_frame32 __user *sf;
	int i, err, wsaved;
	void __user *tail;
	int sigframe_size;
	u32 psr;
	compat_sigset_t seta;

	/* 1. Make sure everything is clean */
	synchronize_user_stack();
	save_and_clear_fpu();
	
	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);

	sf = (struct signal_frame32 __user *)
		get_sigframe(ksig, regs, sigframe_size);
	
	if (invalid_frame_pointer(sf, sigframe_size)) {
		if (show_unhandled_signals)
			pr_info("%s[%d] bad frame in setup_frame32: %08lx TPC %08lx O7 %08lx\n",
				current->comm, current->pid, (unsigned long)sf,
				regs->tpc, regs->u_regs[UREG_I7]);
		force_sigsegv(ksig->sig);
		return -EINVAL;
	}

	tail = (sf + 1);

	/* 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) {
		__siginfo_fpu_t __user *fp = tail;
		tail += sizeof(*fp);
		err |= save_fpu_state(regs, fp);
		err |= __put_user((u64)fp, &sf->fpu_save);
	} else {
		err |= __put_user(0, &sf->fpu_save);
	}
	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);
	}

	/* 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],
			      (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int));

	if (!wsaved) {
		err |= raw_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);
	}	
	if (err)
		return err;

	/* 3. signal handler back-trampoline and parameters */
	regs->u_regs[UREG_FP] = (unsigned long) sf;
	regs->u_regs[UREG_I0] = ksig->sig;
	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
	regs->u_regs[UREG_I2] = (unsigned long) &sf->info;

	/* 4. signal handler */
	regs->tpc = (unsigned long) ksig->ka.sa.sa_handler;
	regs->tnpc = (regs->tpc + 4);
	if (test_thread_flag(TIF_32BIT)) {
		regs->tpc &= 0xffffffff;
		regs->tnpc &= 0xffffffff;
	}

	/* 5. return to kernel instructions */
	if (ksig->ka.ka_restorer) {
		regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer;
	} 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)
			return err;
		flush_signal_insns(address);
	}
	return 0;
}