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

Commit ab513701 authored by Suresh Siddha's avatar Suresh Siddha Committed by Ingo Molnar
Browse files

x86, xsave: reorganization of signal save/restore fpstate code layout



move 64bit routines that saves/restores fpstate in/from user stack from
signal_64.c to xsave.c

restore_i387_xstate() now handles the condition when user passes
NULL fpstate.

Other misc changes for prepartion of xsave/xrstor sigcontext support.

Signed-off-by: default avatarSuresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: default avatarH. Peter Anvin <hpa@zytor.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 3c1c7f10
Loading
Loading
Loading
Loading
+7 −21
Original line number Diff line number Diff line
@@ -216,7 +216,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
				   unsigned int *peax)
{
	unsigned int tmpflags, gs, oldgs, err = 0;
	struct _fpstate_ia32 __user *buf;
	void __user *buf;
	u32 tmp;

	/* Always make any pending restarted system calls return -EINTR */
@@ -260,26 +260,12 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,

	err |= __get_user(tmp, &sc->fpstate);
	buf = compat_ptr(tmp);
	if (buf) {
		if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
			goto badframe;
		err |= restore_i387_ia32(buf);
	} else {
		struct task_struct *me = current;

		if (used_math()) {
			clear_fpu(me);
			clear_used_math();
		}
	}
	err |= restore_i387_xstate_ia32(buf);

	err |= __get_user(tmp, &sc->ax);
	*peax = tmp;

	return err;

badframe:
	return 1;
}

asmlinkage long sys32_sigreturn(struct pt_regs *regs)
@@ -351,7 +337,7 @@ asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
 */

static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
				 struct _fpstate_ia32 __user *fpstate,
				 void __user *fpstate,
				 struct pt_regs *regs, unsigned int mask)
{
	int tmp, err = 0;
@@ -382,7 +368,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
	err |= __put_user((u32)regs->flags, &sc->flags);
	err |= __put_user((u32)regs->sp, &sc->sp_at_signal);

	tmp = save_i387_ia32(fpstate);
	tmp = save_i387_xstate_ia32(fpstate);
	if (tmp < 0)
		err = -EFAULT;
	else {
@@ -404,7 +390,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
 */
static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
				 size_t frame_size,
				 struct _fpstate_ia32 **fpstate)
				 void **fpstate)
{
	unsigned long sp;

@@ -441,7 +427,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
	struct sigframe __user *frame;
	void __user *restorer;
	int err = 0;
	struct _fpstate_ia32 __user *fpstate = NULL;
	void __user *fpstate = NULL;

	/* copy_to_user optimizes that into a single 8 byte store */
	static const struct {
@@ -529,7 +515,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
	struct rt_sigframe __user *frame;
	void __user *restorer;
	int err = 0;
	struct _fpstate_ia32 __user *fpstate = NULL;
	void __user *fpstate = NULL;

	/* __copy_to_user optimizes that into a single 8 byte store */
	static const struct {
+31 −13
Original line number Diff line number Diff line
@@ -21,9 +21,10 @@
# include <asm/sigcontext32.h>
# include <asm/user32.h>
#else
# define save_i387_ia32		save_i387
# define restore_i387_ia32	restore_i387
# define save_i387_xstate_ia32		save_i387_xstate
# define restore_i387_xstate_ia32	restore_i387_xstate
# define _fpstate_ia32		_fpstate
# define _xstate_ia32		_xstate
# define sig_xstate_ia32_size   sig_xstate_size
# define user_i387_ia32_struct	user_i387_struct
# define user32_fxsr_struct	user_fxsr_struct
@@ -424,7 +425,6 @@ static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
	struct task_struct *tsk = current;
	struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;

	unlazy_fpu(tsk);
	fp->status = fp->swd;
	if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
		return -1;
@@ -438,8 +438,6 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
	struct user_i387_ia32_struct env;
	int err = 0;

	unlazy_fpu(tsk);

	convert_from_fxsr(&env, tsk);
	if (__copy_to_user(buf, &env, sizeof(env)))
		return -1;
@@ -455,10 +453,16 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
	return 1;
}

int save_i387_ia32(struct _fpstate_ia32 __user *buf)
int save_i387_xstate_ia32(void __user *buf)
{
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
	struct task_struct *tsk = current;

	if (!used_math())
		return 0;

	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
		return -EACCES;
	/*
	 * This will cause a "finit" to be triggered by the next
	 * attempted FPU operation by the 'current' process.
@@ -468,13 +472,15 @@ int save_i387_ia32(struct _fpstate_ia32 __user *buf)
	if (!HAVE_HWFP) {
		return fpregs_soft_get(current, NULL,
				       0, sizeof(struct user_i387_ia32_struct),
				       NULL, buf) ? -1 : 1;
				       NULL, fp) ? -1 : 1;
	}

	unlazy_fpu(tsk);

	if (cpu_has_fxsr)
		return save_i387_fxsave(buf);
		return save_i387_fxsave(fp);
	else
		return save_i387_fsave(buf);
		return save_i387_fsave(fp);
}

static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
@@ -502,14 +508,26 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
	return 0;
}

int restore_i387_ia32(struct _fpstate_ia32 __user *buf)
int restore_i387_xstate_ia32(void __user *buf)
{
	int err;
	struct task_struct *tsk = current;
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;

	if (HAVE_HWFP)
		clear_fpu(tsk);

	if (!buf) {
		if (used_math()) {
			clear_fpu(tsk);
			clear_used_math();
		}

		return 0;
	} else
		if (!access_ok(VERIFY_READ, buf, sig_xstate_ia32_size))
			return -EACCES;

	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
@@ -518,13 +536,13 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf)

	if (HAVE_HWFP) {
		if (cpu_has_fxsr)
			err = restore_i387_fxsave(buf);
			err = restore_i387_fxsave(fp);
		else
			err = restore_i387_fsave(buf);
			err = restore_i387_fsave(fp);
	} else {
		err = fpregs_soft_set(current, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      NULL, buf) != 0;
				      NULL, fp) != 0;
	}
	set_used_math();

+7 −21
Original line number Diff line number Diff line
@@ -159,28 +159,14 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
	}

	{
		struct _fpstate __user *buf;
		void __user *buf;

		err |= __get_user(buf, &sc->fpstate);
		if (buf) {
			if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
				goto badframe;
			err |= restore_i387(buf);
		} else {
			struct task_struct *me = current;

			if (used_math()) {
				clear_fpu(me);
				clear_used_math();
			}
		}
		err |= restore_i387_xstate(buf);
	}

	err |= __get_user(*pax, &sc->ax);
	return err;

badframe:
	return 1;
}

asmlinkage unsigned long sys_sigreturn(unsigned long __unused)
@@ -262,7 +248,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused)
 * Set up a signal frame.
 */
static int
setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
		 struct pt_regs *regs, unsigned long mask)
{
	int tmp, err = 0;
@@ -289,7 +275,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
	err |= __put_user(regs->sp, &sc->sp_at_signal);
	err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);

	tmp = save_i387(fpstate);
	tmp = save_i387_xstate(fpstate);
	if (tmp < 0)
		err = 1;
	else
@@ -307,7 +293,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
 */
static inline void __user *
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
	     struct _fpstate **fpstate)
	     void **fpstate)
{
	unsigned long sp;

@@ -356,7 +342,7 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
	void __user *restorer;
	int err = 0;
	int usig;
	struct _fpstate __user *fpstate = NULL;
	void __user *fpstate = NULL;

	frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);

@@ -434,7 +420,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
	void __user *restorer;
	int err = 0;
	int usig;
	struct _fpstate __user *fpstate = NULL;
	void __user *fpstate = NULL;

	frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);

+3 −80
Original line number Diff line number Diff line
@@ -53,69 +53,6 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
	return do_sigaltstack(uss, uoss, regs->sp);
}

/*
 * Signal frame handlers.
 */

static inline int save_i387(struct _fpstate __user *buf)
{
	struct task_struct *tsk = current;
	int err = 0;

	BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
			sizeof(tsk->thread.xstate->fxsave));

	if ((unsigned long)buf % 16)
		printk("save_i387: bad fpstate %p\n", buf);

	if (!used_math())
		return 0;
	clear_used_math(); /* trigger finit */
	if (task_thread_info(tsk)->status & TS_USEDFPU) {
		err = save_i387_checking((struct i387_fxsave_struct __user *)
					 buf);
		if (err)
			return err;
		task_thread_info(tsk)->status &= ~TS_USEDFPU;
		stts();
	} else {
		if (__copy_to_user(buf, &tsk->thread.xstate->fxsave,
				   sizeof(struct i387_fxsave_struct)))
			return -1;
	}
	return 1;
}

/*
 * This restores directly out of user space. Exceptions are handled.
 */
static inline int restore_i387(struct _fpstate __user *buf)
{
	struct task_struct *tsk = current;
	int err;

	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}

	if (!(task_thread_info(current)->status & TS_USEDFPU)) {
		clts();
		task_thread_info(current)->status |= TS_USEDFPU;
	}
	err = restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
	if (unlikely(err)) {
		/*
		 * Encountered an error while doing the restore from the
		 * user buffer, clear the fpu state.
		 */
		clear_fpu(tsk);
		clear_used_math();
	}
	return err;
}

/*
 * Do a signal return; undo the signal stack.
 */
@@ -160,25 +97,11 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
	{
		struct _fpstate __user * buf;
		err |= __get_user(buf, &sc->fpstate);

		if (buf) {
			if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
				goto badframe;
			err |= restore_i387(buf);
		} else {
			struct task_struct *me = current;
			if (used_math()) {
				clear_fpu(me);
				clear_used_math();
			}
		}
		err |= restore_i387_xstate(buf);
	}

	err |= __get_user(*pax, &sc->ax);
	return err;

badframe:
	return 1;
}

asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
@@ -276,7 +199,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
			   sigset_t *set, struct pt_regs * regs)
{
	struct rt_sigframe __user *frame;
	struct _fpstate __user *fp = NULL; 
	void __user *fp = NULL;
	int err = 0;
	struct task_struct *me = current;

@@ -288,7 +211,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
		if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate)))
			goto give_sigsegv;

		if (save_i387(fp) < 0) 
		if (save_i387_xstate(fp) < 0)
			err |= -1; 
	} else
		frame = get_stack(ka, regs, sizeof(struct rt_sigframe)) - 8;
+79 −0
Original line number Diff line number Diff line
@@ -12,6 +12,85 @@
 */
unsigned int pcntxt_hmask, pcntxt_lmask;

#ifdef CONFIG_X86_64
/*
 * Signal frame handlers.
 */

int save_i387_xstate(void __user *buf)
{
	struct task_struct *tsk = current;
	int err = 0;

	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_size))
		return -EACCES;

	BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
			sizeof(tsk->thread.xstate->fxsave));

	if ((unsigned long)buf % 16)
		printk("save_i387_xstate: bad fpstate %p\n", buf);

	if (!used_math())
		return 0;
	clear_used_math(); /* trigger finit */
	if (task_thread_info(tsk)->status & TS_USEDFPU) {
		err = save_i387_checking((struct i387_fxsave_struct __user *)
					 buf);
		if (err)
			return err;
		task_thread_info(tsk)->status &= ~TS_USEDFPU;
		stts();
	} else {
		if (__copy_to_user(buf, &tsk->thread.xstate->fxsave,
				   xstate_size))
			return -1;
	}
	return 1;
}

/*
 * This restores directly out of user space. Exceptions are handled.
 */
int restore_i387_xstate(void __user *buf)
{
	struct task_struct *tsk = current;
	int err;

	if (!buf) {
		if (used_math()) {
			clear_fpu(tsk);
			clear_used_math();
		}

		return 0;
	} else
		if (!access_ok(VERIFY_READ, buf, sig_xstate_size))
			return -EACCES;

	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}

	if (!(task_thread_info(current)->status & TS_USEDFPU)) {
		clts();
		task_thread_info(current)->status |= TS_USEDFPU;
	}
	err = fxrstor_checking((__force struct i387_fxsave_struct *)buf);
	if (unlikely(err)) {
		/*
		 * Encountered an error while doing the restore from the
		 * user buffer, clear the fpu state.
		 */
		clear_fpu(tsk);
		clear_used_math();
	}
	return err;
}
#endif

/*
 * Represents init state for the supported extended state.
 */
Loading