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

Commit accf0fa6 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branch 'x86/xsave' into x86/core

parents ebd60cd6 fe47784b
Loading
Loading
Loading
Loading
+24 −27
Original line number Diff line number Diff line
@@ -179,9 +179,10 @@ struct sigframe
	u32 pretcode;
	int sig;
	struct sigcontext_ia32 sc;
	struct _fpstate_ia32 fpstate;
	struct _fpstate_ia32 fpstate_unused; /* look at kernel/sigframe.h */
	unsigned int extramask[_COMPAT_NSIG_WORDS-1];
	char retcode[8];
	/* fp state follows here */
};

struct rt_sigframe
@@ -192,8 +193,8 @@ struct rt_sigframe
	u32 puc;
	compat_siginfo_t info;
	struct ucontext_ia32 uc;
	struct _fpstate_ia32 fpstate;
	char retcode[8];
	/* fp state follows here */
};

#define COPY(x)		{ 		\
@@ -215,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 */
@@ -259,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)
@@ -350,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;
@@ -381,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 {
@@ -402,7 +389,8 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
 * Determine which stack to use..
 */
static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
				 size_t frame_size)
				 size_t frame_size,
				 void **fpstate)
{
	unsigned long sp;

@@ -421,6 +409,11 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
		 ka->sa.sa_restorer)
		sp = (unsigned long) ka->sa.sa_restorer;

	if (used_math()) {
		sp = sp - sig_xstate_ia32_size;
		*fpstate = (struct _fpstate_ia32 *) sp;
	}

	sp -= frame_size;
	/* Align the stack pointer according to the i386 ABI,
	 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
@@ -434,6 +427,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
	struct sigframe __user *frame;
	void __user *restorer;
	int err = 0;
	void __user *fpstate = NULL;

	/* copy_to_user optimizes that into a single 8 byte store */
	static const struct {
@@ -448,7 +442,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
		0,
	};

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

	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
		goto give_sigsegv;
@@ -457,8 +451,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
	if (err)
		goto give_sigsegv;

	err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs,
					set->sig[0]);
	err |= ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]);
	if (err)
		goto give_sigsegv;

@@ -522,6 +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;
	void __user *fpstate = NULL;

	/* __copy_to_user optimizes that into a single 8 byte store */
	static const struct {
@@ -537,7 +531,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
		0,
	};

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

	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
		goto give_sigsegv;
@@ -550,13 +544,16 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
		goto give_sigsegv;

	/* Create the ucontext.  */
	if (cpu_has_xsave)
		err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
	else
		err |= __put_user(0, &frame->uc.uc_flags);
	err |= __put_user(0, &frame->uc.uc_link);
	err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
	err |= __put_user(sas_ss_flags(regs->sp),
			  &frame->uc.uc_stack.ss_flags);
	err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
	err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
	err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
				     regs, set->sig[0]);
	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
	if (err)
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ obj-y += tsc.o io_delay.o rtc.o

obj-$(CONFIG_X86_TRAMPOLINE)	+= trampoline.o
obj-y				+= process.o
obj-y				+= i387.o
obj-y				+= i387.o xsave.o
obj-y				+= ptrace.o
obj-y				+= ds.o
obj-$(CONFIG_X86_32)		+= tls.o
+12 −1
Original line number Diff line number Diff line
@@ -739,9 +739,20 @@ void __cpuinit cpu_init(void)
	/*
	 * Force FPU initialization:
	 */
	if (cpu_has_xsave)
		current_thread_info()->status = TS_XSAVE;
	else
		current_thread_info()->status = 0;
	clear_used_math();
	mxcsr_feature_mask_init();

	/*
	 * Boot processor to setup the FP and extended state context info.
	 */
	if (!smp_processor_id())
		init_thread_xstate();

	xsave_init();
}

#ifdef CONFIG_HOTPLUG_CPU
+135 −19
Original line number Diff line number Diff line
@@ -21,9 +21,12 @@
# 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 fx_sw_reserved_ia32	fx_sw_reserved
# define user_i387_ia32_struct	user_i387_struct
# define user32_fxsr_struct	user_fxsr_struct
#endif
@@ -36,6 +39,7 @@

static unsigned int		mxcsr_feature_mask __read_mostly = 0xffffffffu;
unsigned int xstate_size;
unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32);
static struct i387_fxsave_struct fx_scratch __cpuinitdata;

void __cpuinit mxcsr_feature_mask_init(void)
@@ -61,6 +65,11 @@ void __init init_thread_xstate(void)
		return;
	}

	if (cpu_has_xsave) {
		xsave_cntxt_init();
		return;
	}

	if (cpu_has_fxsr)
		xstate_size = sizeof(struct i387_fxsave_struct);
#ifdef CONFIG_X86_32
@@ -83,8 +92,18 @@ void __cpuinit fpu_init(void)

	write_cr0(oldcr0 & ~(X86_CR0_TS|X86_CR0_EM)); /* clear TS and EM */

	/*
	 * Boot processor to setup the FP and extended state context info.
	 */
	if (!smp_processor_id())
		init_thread_xstate();
	xsave_init();

	mxcsr_feature_mask_init();
	/* clean state in init */
	if (cpu_has_xsave)
		current_thread_info()->status = TS_XSAVE;
	else
		current_thread_info()->status = 0;
	clear_used_math();
}
@@ -195,6 +214,13 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
	 */
	target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;

	/*
	 * update the header bits in the xsave header, indicating the
	 * presence of FP and SSE state.
	 */
	if (cpu_has_xsave)
		target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;

	return ret;
}

@@ -395,6 +421,12 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,
	if (!ret)
		convert_to_fxsr(target, &env);

	/*
	 * update the header bit in the xsave header, indicating the
	 * presence of FP.
	 */
	if (cpu_has_xsave)
		target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FP;
	return ret;
}

@@ -407,7 +439,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;
@@ -421,8 +452,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;
@@ -432,16 +461,40 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
	if (err)
		return -1;

	if (__copy_to_user(&buf->_fxsr_env[0], fx,
			   sizeof(struct i387_fxsave_struct)))
	if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
		return -1;
	return 1;
}

static int save_i387_xsave(void __user *buf)
{
	struct _fpstate_ia32 __user *fx = buf;
	int err = 0;

	if (save_i387_fxsave(fx) < 0)
		return -1;

	err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved_ia32,
			     sizeof(struct _fpx_sw_bytes));
	err |= __put_user(FP_XSTATE_MAGIC2,
			  (__u32 __user *) (buf + sig_xstate_ia32_size
					    - FP_XSTATE_MAGIC2_SIZE));
	if (err)
		return -1;

	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.
@@ -451,13 +504,17 @@ 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_xsave)
		return save_i387_xsave(fp);
	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)
@@ -468,14 +525,15 @@ static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
				sizeof(struct i387_fsave_struct));
}

static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
			       unsigned int size)
{
	struct task_struct *tsk = current;
	struct user_i387_ia32_struct env;
	int err;

	err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
			       sizeof(struct i387_fxsave_struct));
			       size);
	/* mxcsr reserved bits must be masked to zero for security reasons */
	tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
	if (err || __copy_from_user(&env, buf, sizeof(env)))
@@ -485,14 +543,69 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
	return 0;
}

int restore_i387_ia32(struct _fpstate_ia32 __user *buf)
static int restore_i387_xsave(void __user *buf)
{
	struct _fpx_sw_bytes fx_sw_user;
	struct _fpstate_ia32 __user *fx_user =
			((struct _fpstate_ia32 __user *) buf);
	struct i387_fxsave_struct __user *fx =
		(struct i387_fxsave_struct __user *) &fx_user->_fxsr_env[0];
	struct xsave_hdr_struct *xsave_hdr =
				&current->thread.xstate->xsave.xsave_hdr;
	u64 mask;
	int err;

	if (check_for_xstate(fx, buf, &fx_sw_user))
		goto fx_only;

	mask = fx_sw_user.xstate_bv;

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

	xsave_hdr->xstate_bv &= pcntxt_mask;
	/*
	 * These bits must be zero.
	 */
	xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;

	/*
	 * Init the state that is not present in the memory layout
	 * and enabled by the OS.
	 */
	mask = ~(pcntxt_mask & ~mask);
	xsave_hdr->xstate_bv &= mask;

	return err;
fx_only:
	/*
	 * Couldn't find the extended state information in the memory
	 * layout. Restore the FP/SSE and init the other extended state
	 * enabled by the OS.
	 */
	xsave_hdr->xstate_bv = XSTATE_FPSSE;
	return restore_i387_fxsave(buf, sizeof(struct i387_fxsave_struct));
}

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)
@@ -500,14 +613,17 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf)
	}

	if (HAVE_HWFP) {
		if (cpu_has_fxsr)
			err = restore_i387_fxsave(buf);
		if (cpu_has_xsave)
			err = restore_i387_xsave(buf);
		else if (cpu_has_fxsr)
			err = restore_i387_fxsave(fp, sizeof(struct
							   i387_fxsave_struct));
		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();

+12 −2
Original line number Diff line number Diff line
@@ -3,9 +3,18 @@ struct sigframe {
	char __user *pretcode;
	int sig;
	struct sigcontext sc;
	struct _fpstate fpstate;
	/*
	 * fpstate is unused. fpstate is moved/allocated after
	 * retcode[] below. This movement allows to have the FP state and the
	 * future state extensions (xsave) stay together.
	 * And at the same time retaining the unused fpstate, prevents changing
	 * the offset of extramask[] in the sigframe and thus prevent any
	 * legacy application accessing/modifying it.
	 */
	struct _fpstate fpstate_unused;
	unsigned long extramask[_NSIG_WORDS-1];
	char retcode[8];
	/* fp state follows here */
};

struct rt_sigframe {
@@ -15,13 +24,14 @@ struct rt_sigframe {
	void __user *puc;
	struct siginfo info;
	struct ucontext uc;
	struct _fpstate fpstate;
	char retcode[8];
	/* fp state follows here */
};
#else
struct rt_sigframe {
	char __user *pretcode;
	struct ucontext uc;
	struct siginfo info;
	/* fp state follows here */
};
#endif
Loading