diff options
Diffstat (limited to 'arch/sparc/kernel/signal32.c')
| -rw-r--r-- | arch/sparc/kernel/signal32.c | 184 | 
1 files changed, 105 insertions, 79 deletions
| diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c index 75fad425e249..1ba95aff5d59 100644 --- a/arch/sparc/kernel/signal32.c +++ b/arch/sparc/kernel/signal32.c @@ -29,6 +29,8 @@  #include <asm/visasm.h>  #include <asm/compat_signal.h> +#include "sigutil.h" +  #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))  /* This magic should be in g_upper[0] for all upper parts @@ -44,14 +46,14 @@ typedef struct {  struct signal_frame32 {  	struct sparc_stackf32	ss;  	__siginfo32_t		info; -	/* __siginfo_fpu32_t * */ u32 fpu_save; +	/* __siginfo_fpu_t * */ u32 fpu_save;  	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; -	__siginfo_fpu_t		fpu_state; -}; +	/* __siginfo_rwin_t * */u32 rwin_save; +} __attribute__((aligned(8)));  typedef struct compat_siginfo{  	int si_signo; @@ -110,18 +112,14 @@ struct rt_signal_frame32 {  	compat_siginfo_t	info;  	struct pt_regs32	regs;  	compat_sigset_t		mask; -	/* __siginfo_fpu32_t * */ u32 fpu_save; +	/* __siginfo_fpu_t * */ u32 fpu_save;  	unsigned int		insns[2];  	stack_t32		stack;  	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; -	__siginfo_fpu_t		fpu_state; -}; - -/* Align macros */ -#define SF_ALIGNEDSZ  (((sizeof(struct signal_frame32) + 15) & (~15))) -#define RT_ALIGNEDSZ  (((sizeof(struct rt_signal_frame32) + 15) & (~15))) +	/* __siginfo_rwin_t * */u32 rwin_save; +} __attribute__((aligned(8)));  int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)  { @@ -192,30 +190,13 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)  	return 0;  } -static int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) -{ -	unsigned long *fpregs = current_thread_info()->fpregs; -	unsigned long fprs; -	int err; -	 -	err = __get_user(fprs, &fpu->si_fprs); -	fprs_write(0); -	regs->tstate &= ~TSTATE_PEF; -	if (fprs & FPRS_DL) -		err |= copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32)); -	if (fprs & FPRS_DU) -		err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32)); -	err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr); -	err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr); -	current_thread_info()->fpsaved[0] |= fprs; -	return err; -} -  void do_sigreturn32(struct pt_regs *regs)  {  	struct signal_frame32 __user *sf; +	compat_uptr_t fpu_save; +	compat_uptr_t rwin_save;  	unsigned int psr; -	unsigned pc, npc, fpu_save; +	unsigned pc, npc;  	sigset_t set;  	unsigned seta[_COMPAT_NSIG_WORDS];  	int err, i; @@ -273,8 +254,13 @@ void do_sigreturn32(struct pt_regs *regs)  	pt_regs_clear_syscall(regs);  	err |= __get_user(fpu_save, &sf->fpu_save); -	if (fpu_save) -		err |= restore_fpu_state32(regs, &sf->fpu_state); +	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; +	}  	err |= __get_user(seta[0], &sf->info.si_mask);  	err |= copy_from_user(seta+1, &sf->extramask,  			      (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int)); @@ -300,7 +286,9 @@ segv:  asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)  {  	struct rt_signal_frame32 __user *sf; -	unsigned int psr, pc, npc, fpu_save, u_ss_sp; +	unsigned int psr, pc, npc, u_ss_sp; +	compat_uptr_t fpu_save; +	compat_uptr_t rwin_save;  	mm_segment_t old_fs;  	sigset_t set;  	compat_sigset_t seta; @@ -359,8 +347,8 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)  	pt_regs_clear_syscall(regs);  	err |= __get_user(fpu_save, &sf->fpu_save); -	if (fpu_save) -		err |= restore_fpu_state32(regs, &sf->fpu_state); +	if (!err && fpu_save) +		err |= restore_fpu_state(regs, compat_ptr(fpu_save));  	err |= copy_from_user(&seta, &sf->mask, sizeof(compat_sigset_t));  	err |= __get_user(u_ss_sp, &sf->stack.ss_sp);  	st.ss_sp = compat_ptr(u_ss_sp); @@ -376,6 +364,12 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)  	do_sigaltstack((stack_t __user *) &st, NULL, (unsigned long)sf);  	set_fs(old_fs); +	err |= __get_user(rwin_save, &sf->rwin_save); +	if (!err && rwin_save) { +		if (restore_rwin_state(compat_ptr(rwin_save))) +			goto segv; +	} +  	switch (_NSIG_WORDS) {  		case 4: set.sig[3] = seta.sig[6] + (((long)seta.sig[7]) << 32);  		case 3: set.sig[2] = seta.sig[4] + (((long)seta.sig[5]) << 32); @@ -433,26 +427,6 @@ static void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, uns  	return (void __user *) sp;  } -static int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) -{ -	unsigned long *fpregs = current_thread_info()->fpregs; -	unsigned long fprs; -	int err = 0; -	 -	fprs = current_thread_info()->fpsaved[0]; -	if (fprs & FPRS_DL) -		err |= copy_to_user(&fpu->si_float_regs[0], fpregs, -				    (sizeof(unsigned int) * 32)); -	if (fprs & FPRS_DU) -		err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16, -				    (sizeof(unsigned int) * 32)); -	err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr); -	err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr); -	err |= __put_user(fprs, &fpu->si_fprs); - -	return err; -} -  /* The I-cache flush instruction only works in the primary ASI, which   * right now is the nucleus, aka. kernel space.   * @@ -515,18 +489,23 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			 int signo, sigset_t *oldset)  {  	struct signal_frame32 __user *sf; +	int i, err, wsaved; +	void __user *tail;  	int sigframe_size;  	u32 psr; -	int i, err;  	unsigned int seta[_COMPAT_NSIG_WORDS];  	/* 1. Make sure everything is clean */  	synchronize_user_stack();  	save_and_clear_fpu(); -	sigframe_size = SF_ALIGNEDSZ; -	if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) -		sigframe_size -= sizeof(__siginfo_fpu_t); +	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(&ka->sa, regs, sigframe_size); @@ -534,8 +513,7 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	if (invalid_frame_pointer(sf, sigframe_size))  		goto sigill; -	if (get_thread_wsaved() != 0) -		goto sigill; +	tail = (sf + 1);  	/* 2. Save the current process state */  	if (test_thread_flag(TIF_32BIT)) { @@ -560,11 +538,22 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			  &sf->v8plus.asi);  	if (psr & PSR_EF) { -		err |= save_fpu_state32(regs, &sf->fpu_state); -		err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); +		__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); +	}  	switch (_NSIG_WORDS) {  	case 4: seta[7] = (oldset->sig[3] >> 32); @@ -580,10 +569,21 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	err |= __copy_to_user(sf->extramask, seta + 1,  			      (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int)); -	err |= copy_in_user((u32 __user *)sf, -			    (u32 __user *)(regs->u_regs[UREG_FP]), -			    sizeof(struct reg_window32)); -	 +	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 = ¤t_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)  		goto sigsegv; @@ -613,7 +613,6 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  		err |= __put_user(0x91d02010, &sf->insns[1]); /*t 0x10*/  		if (err)  			goto sigsegv; -  		flush_signal_insns(address);  	}  	return 0; @@ -632,18 +631,23 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			    siginfo_t *info)  {  	struct rt_signal_frame32 __user *sf; +	int i, err, wsaved; +	void __user *tail;  	int sigframe_size;  	u32 psr; -	int i, err;  	compat_sigset_t seta;  	/* 1. Make sure everything is clean */  	synchronize_user_stack();  	save_and_clear_fpu(); -	sigframe_size = RT_ALIGNEDSZ; -	if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) -		sigframe_size -= sizeof(__siginfo_fpu_t); +	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 rt_signal_frame32 __user *)  		get_sigframe(&ka->sa, regs, sigframe_size); @@ -651,8 +655,7 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	if (invalid_frame_pointer(sf, sigframe_size))  		goto sigill; -	if (get_thread_wsaved() != 0) -		goto sigill; +	tail = (sf + 1);  	/* 2. Save the current process state */  	if (test_thread_flag(TIF_32BIT)) { @@ -677,11 +680,22 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			  &sf->v8plus.asi);  	if (psr & PSR_EF) { -		err |= save_fpu_state32(regs, &sf->fpu_state); -		err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); +		__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); +	}  	/* Update the siginfo structure.  */  	err |= copy_siginfo_to_user32(&sf->info, info); @@ -703,9 +717,21 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	}  	err |= __copy_to_user(&sf->mask, &seta, sizeof(compat_sigset_t)); -	err |= copy_in_user((u32 __user *)sf, -			    (u32 __user *)(regs->u_regs[UREG_FP]), -			    sizeof(struct reg_window32)); +	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 = ¤t_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)  		goto sigsegv; | 
