Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a274c08 authored by Michael Neuling's avatar Michael Neuling Committed by Paul Mackerras
Browse files

powerpc: Clean up copy_to/from_user for vsx and fpr



This merges and cleans up some of the ugly copy/to from user code
which is required for the new fpr and vsx layout in the thread_struct.

Also fixes some hard coded buffer sizes and removes a redundant
fpr_flush_to_thread.

Signed-off-by: default avatarMichael Neuling <mikey@neuling.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 2d1b2027
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -24,6 +24,16 @@ extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
			      siginfo_t *info, sigset_t *oldset,
			      struct pt_regs *regs);

extern unsigned long copy_fpr_to_user(void __user *to,
				      struct task_struct *task);
extern unsigned long copy_fpr_from_user(struct task_struct *task,
					void __user *from);
#ifdef CONFIG_VSX
extern unsigned long copy_vsx_to_user(void __user *to,
				      struct task_struct *task);
extern unsigned long copy_vsx_from_user(struct task_struct *task,
					void __user *from);
#endif

#ifdef CONFIG_PPC64

+75 −34
Original line number Diff line number Diff line
@@ -328,6 +328,75 @@ struct rt_sigframe {
	int			abigap[56];
};

#ifdef CONFIG_VSX
unsigned long copy_fpr_to_user(void __user *to,
			       struct task_struct *task)
{
	double buf[ELF_NFPREG];
	int i;

	/* save FPR copy to local buffer then write to the thread_struct */
	for (i = 0; i < (ELF_NFPREG - 1) ; i++)
		buf[i] = task->thread.TS_FPR(i);
	memcpy(&buf[i], &task->thread.fpscr, sizeof(double));
	return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double));
}

unsigned long copy_fpr_from_user(struct task_struct *task,
				 void __user *from)
{
	double buf[ELF_NFPREG];
	int i;

	if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double)))
		return 1;
	for (i = 0; i < (ELF_NFPREG - 1) ; i++)
		task->thread.TS_FPR(i) = buf[i];
	memcpy(&task->thread.fpscr, &buf[i], sizeof(double));

	return 0;
}

unsigned long copy_vsx_to_user(void __user *to,
			       struct task_struct *task)
{
	double buf[ELF_NVSRHALFREG];
	int i;

	/* save FPR copy to local buffer then write to the thread_struct */
	for (i = 0; i < ELF_NVSRHALFREG; i++)
		buf[i] = task->thread.fpr[i][TS_VSRLOWOFFSET];
	return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double));
}

unsigned long copy_vsx_from_user(struct task_struct *task,
				 void __user *from)
{
	double buf[ELF_NVSRHALFREG];
	int i;

	if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double)))
		return 1;
	for (i = 0; i < ELF_NVSRHALFREG ; i++)
		task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
	return 0;
}
#else
inline unsigned long copy_fpr_to_user(void __user *to,
				      struct task_struct *task)
{
	return __copy_to_user(to, task->thread.fpr,
			      ELF_NFPREG * sizeof(double));
}

inline unsigned long copy_fpr_from_user(struct task_struct *task,
					void __user *from)
{
	return __copy_from_user(task->thread.fpr, from,
			      ELF_NFPREG * sizeof(double));
}
#endif

/*
 * Save the current user registers on the user stack.
 * We only save the altivec/spe registers if the process has used
@@ -337,10 +406,6 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
		int sigret)
{
	unsigned long msr = regs->msr;
#ifdef CONFIG_VSX
	double buf[32];
	int i;
#endif

	/* Make sure floating point registers are stored in regs */
	flush_fp_to_thread(current);
@@ -370,14 +435,9 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
	if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32]))
		return 1;
#endif /* CONFIG_ALTIVEC */
#ifdef CONFIG_VSX
	/* save FPR copy to local buffer then write to the thread_struct */
	flush_fp_to_thread(current);
	for (i = 0; i < 32 ; i++)
		buf[i] = current->thread.TS_FPR(i);
	memcpy(&buf[i], &current->thread.fpscr, sizeof(double));
	if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double)))
	if (copy_fpr_to_user(&frame->mc_fregs, current))
		return 1;
#ifdef CONFIG_VSX
	/*
	 * Copy VSR 0-31 upper half from thread_struct to local
	 * buffer, then write that to userspace.  Also set MSR_VSX in
@@ -386,18 +446,10 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
	 */
	if (current->thread.used_vsr) {
		flush_vsx_to_thread(current);
		for (i = 0; i < 32 ; i++)
			buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
		if (__copy_to_user(&frame->mc_vsregs, buf,
				   ELF_NVSRHALFREG  * sizeof(double)))
		if (copy_vsx_to_user(&frame->mc_vsregs, current))
			return 1;
		msr |= MSR_VSX;
	}
#else
	/* save floating-point registers */
	if (__copy_to_user(&frame->mc_fregs, current->thread.fpr,
		    ELF_NFPREG * sizeof(double)))
		return 1;
#endif /* CONFIG_VSX */
#ifdef CONFIG_SPE
	/* save spe registers */
@@ -442,7 +494,6 @@ static long restore_user_regs(struct pt_regs *regs,
	unsigned int save_r2 = 0;
	unsigned long msr;
#ifdef CONFIG_VSX
	double buf[32];
	int i;
#endif

@@ -490,13 +541,10 @@ static long restore_user_regs(struct pt_regs *regs,
	if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32]))
		return 1;
#endif /* CONFIG_ALTIVEC */
	if (copy_fpr_from_user(current, &sr->mc_fregs))
		return 1;

#ifdef CONFIG_VSX
	if (__copy_from_user(buf, &sr->mc_fregs,sizeof(sr->mc_fregs)))
		return 1;
	for (i = 0; i < 32 ; i++)
		current->thread.TS_FPR(i) = buf[i];
	memcpy(&current->thread.fpscr, &buf[i], sizeof(double));
	/*
	 * Force the process to reload the VSX registers from
	 * current->thread when it next does VSX instruction.
@@ -507,18 +555,11 @@ static long restore_user_regs(struct pt_regs *regs,
		 * Restore altivec registers from the stack to a local
		 * buffer, then write this out to the thread_struct
		 */
		if (__copy_from_user(buf, &sr->mc_vsregs,
				     sizeof(sr->mc_vsregs)))
		if (copy_vsx_from_user(current, &sr->mc_vsregs))
			return 1;
		for (i = 0; i < 32 ; i++)
			current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
	} else if (current->thread.used_vsr)
		for (i = 0; i < 32 ; i++)
			current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;
#else
	if (__copy_from_user(current->thread.fpr, &sr->mc_fregs,
			     sizeof(sr->mc_fregs)))
		return 1;
#endif /* CONFIG_VSX */
	/*
	 * force the process to reload the FP registers from
+11 −32
Original line number Diff line number Diff line
@@ -89,10 +89,6 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
#endif
	unsigned long msr = regs->msr;
	long err = 0;
#ifdef CONFIG_VSX
	double buf[FP_REGS_SIZE];
	int i;
#endif

	flush_fp_to_thread(current);

@@ -117,12 +113,9 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
	err |= __put_user(0, &sc->v_regs);
#endif /* CONFIG_ALTIVEC */
	flush_fp_to_thread(current);
	/* copy fpr regs and fpscr */
	err |= copy_fpr_to_user(&sc->fp_regs, current);
#ifdef CONFIG_VSX
	/* Copy FP to local buffer then write that out */
	for (i = 0; i < 32 ; i++)
		buf[i] = current->thread.TS_FPR(i);
	memcpy(&buf[i], &current->thread.fpscr, sizeof(double));
	err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE);
	/*
	 * Copy VSX low doubleword to local buffer for formatting,
	 * then out to userspace.  Update v_regs to point after the
@@ -131,17 +124,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
	if (current->thread.used_vsr) {
		flush_vsx_to_thread(current);
		v_regs += ELF_NVRREG;
		for (i = 0; i < 32 ; i++)
			buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
		err |= __copy_to_user(v_regs, buf, 32 * sizeof(double));
		err |= copy_vsx_to_user(v_regs, current);
		/* set MSR_VSX in the MSR value in the frame to
		 * indicate that sc->vs_reg) contains valid data.
		 */
		msr |= MSR_VSX;
	}
#else /* CONFIG_VSX */
	/* copy fpr regs and fpscr */
	err |= __copy_to_user(&sc->fp_regs, &current->thread.fpr, FP_REGS_SIZE);
#endif /* CONFIG_VSX */
	err |= __put_user(&sc->gp_regs, &sc->regs);
	WARN_ON(!FULL_REGS(regs));
@@ -164,14 +152,13 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
{
#ifdef CONFIG_ALTIVEC
	elf_vrreg_t __user *v_regs;
#endif
#ifdef CONFIG_VSX
	double buf[FP_REGS_SIZE];
	int i;
#endif
	unsigned long err = 0;
	unsigned long save_r13 = 0;
	unsigned long msr;
#ifdef CONFIG_VSX
	int i;
#endif

	/* If this is not a signal return, we preserve the TLS in r13 */
	if (!sig)
@@ -234,15 +221,9 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
	else
		current->thread.vrsave = 0;
#endif /* CONFIG_ALTIVEC */
#ifdef CONFIG_VSX
	/* restore floating point */
	err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE);
	if (err)
		return err;
	for (i = 0; i < 32 ; i++)
		current->thread.TS_FPR(i) = buf[i];
	memcpy(&current->thread.fpscr, &buf[i], sizeof(double));

	err |= copy_fpr_from_user(current, &sc->fp_regs);
#ifdef CONFIG_VSX
	/*
	 * Get additional VSX data. Update v_regs to point after the
	 * VMX data.  Copy VSX low doubleword from userspace to local
@@ -250,14 +231,12 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
	 */
	v_regs += ELF_NVRREG;
	if ((msr & MSR_VSX) != 0)
		err |= __copy_from_user(buf, v_regs, 32 * sizeof(double));
		err |= copy_vsx_from_user(current, v_regs);
	else
		memset(buf, 0, 32 * sizeof(double));

		for (i = 0; i < 32 ; i++)
		current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
			current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;

#else
	err |= __copy_from_user(&current->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
#endif
	return err;
}